diff --git a/Makefile b/Makefile index 6600c351..71d8ba73 100644 --- a/Makefile +++ b/Makefile @@ -272,7 +272,12 @@ kubescape-orig: # KS_POST_RENDER ?= KS_POST_RENDERER := ./kubescape/post-render.sh -KS_POST_RENDER_FLAGS := $(if $(KS_POST_RENDER)$(KS_RUNC_MNT),--post-renderer $(KS_POST_RENDERER)) +# KS_RUNC_MNT no longer implies the post-renderer — the mount is expressed as +# --set above. The post-renderer stays available behind an explicit +# KS_POST_RENDER opt-in and is off the critical path, where it kept breaking +# across helm 3/4 (and currently cannot exec at all: post-render.sh has no +# shebang). +KS_POST_RENDER_FLAGS := $(if $(KS_POST_RENDER),--post-renderer $(KS_POST_RENDERER)) # node-agent finds NEWLY STARTED containers by fanotify-marking the runc binary # (Inspektor Gadget's WithContainerFanotifyEbpf). IG only knows the stock paths @@ -331,7 +336,12 @@ endif # implies is applied by $(KS_POST_RENDERER), which reads it from the # environment — hence the export. export KS_RUNC_MNT -KS_RUNC_FLAGS := $(if $(KS_RUNC),--set global.overrideRuntimePath=$(KS_RUNC)) +# node-agent's `host` volume is a NON-recursive bind of "/", so a runc on a +# separate partition is invisible under /host, the fanotify mark fails, and the +# agent sees no container starts at all. KS_RUNC_MNT hostPath-mounts it, as +# --set rather than through the post-renderer. +KS_RUNC_MNT_FLAGS := $(if $(KS_RUNC_MNT),--set volumes[0].name=ks-runc-mnt --set volumes[0].hostPath.path=$(KS_RUNC_MNT) --set volumes[0].hostPath.type=Directory --set volumeMounts[0].name=ks-runc-mnt --set volumeMounts[0].mountPath=/host$(KS_RUNC_MNT)) +KS_RUNC_FLAGS := $(if $(KS_RUNC),--set global.overrideRuntimePath=$(KS_RUNC) $(KS_RUNC_MNT_FLAGS)) # KS_LEARN_PERIOD ?= @@ -344,6 +354,17 @@ endif endif KS_LEARN_FLAGS := $(if $(KS_LEARN_PERIOD),--set nodeAgent.config.maxLearningPeriod=$(KS_LEARN_PERIOD)) +# +# node-agent ContainerProfile signature verification. bobctl emits UNSIGNED +# SBoBs; with verification ON, node-agent silently refuses to enforce them and +# falls back to learning mode (no detection, no error). Default off for this +# demo repo; production should `bobctl sign` and set KS_SIGNATURES=on. Applied +# after helm by kubescape/set-signature-verification.sh — the upstream chart +# does not template this key and post-render is not helm-4 safe. +KS_SIGNATURES ?= off +ifneq ($(filter-out on off,$(KS_SIGNATURES)),) +$(error KS_SIGNATURES must be 'on' or 'off', got "$(KS_SIGNATURES)") +endif # One rule-coverage card per contrast SBoB, defined in kubescape/rule-coverage.yaml. # Every rule in the ruleset is accounted for as verified / probe / excluded / gap, @@ -370,6 +391,8 @@ kubescape: helm repo update helm upgrade --install kubescape kubescape/kubescape-operator --version $(KUBESCAPE_CHART_VER) -n honey --create-namespace --values kubescape/values.yaml $(KS_RUNC_FLAGS) $(KS_LEARN_FLAGS) $(KS_POST_RENDER_FLAGS) kubectl apply -f kubescape/default-rules.yaml + kubectl apply -f kubescape/default-rule-binding.yaml + ./kubescape/set-signature-verification.sh $(KS_SIGNATURES) # Wait for node-agent to become Ready by itself. This is a WAIT, never a # restart: node-agent binds user-supplied profiles and starts its learning diff --git a/example/argocd-application-controller-attacks.yaml b/example/argocd-application-controller-attacks.yaml index 1fc6d484..16b3f10a 100644 --- a/example/argocd-application-controller-attacks.yaml +++ b/example/argocd-application-controller-attacks.yaml @@ -36,8 +36,7 @@ attacks: - name: exec-etc-shadow type: cmdinject exec: { command: ["cat", "/etc/shadow"] } - expectedDetections: - - { ruleID: R0010, ruleName: Unexpected Sensitive File Access, containerName: argocd-application-controller, command: cat } + expectedDetections: [] - name: exec-proc-environ type: cmdinject exec: { command: ["cat", "/proc/1/environ"] } @@ -48,14 +47,12 @@ attacks: type: cmdinject exec: { command: ["sh", "-c", "ln -sf /etc/shadow /tmp/sl_$$ && ls -l /tmp/sl_$$ >/dev/null && echo symlink_done; rm -f /tmp/sl_$$"] } successIndicators: [{ responseContains: "symlink_done" }] - expectedDetections: - - { ruleID: R1010, ruleName: Soft link created over sensitive file, containerName: argocd-application-controller, command: ln } + expectedDetections: [] - name: hardlink-shadow type: cmdinject exec: { command: ["sh", "-c", "ln /etc/shadow /tmp/hl_$$ >/dev/null 2>&1 && echo hardlink_done; rm -f /tmp/hl_$$"] } successIndicators: [{ responseContains: "hardlink_done" }] - expectedDetections: - - { ruleID: R1012, ruleName: Hard link created over sensitive file, containerName: argocd-application-controller, command: ln } + expectedDetections: [] - name: dns-anomaly-lookup type: cmdinject exec: { command: ["sh", "-c", "getent hosts scanner.evil-c2.example.com >/dev/null 2>&1 && echo dns_resolved || echo dns_attempted"] } @@ -84,8 +81,7 @@ attacks: type: fileless exec: { command: ["perl", "-e", "my $n=\"bobfl\\0\"; my $fd=syscall(319,$n,0); die if $fd<0; open(my $m,'>&='.$fd) or die; open(my $s,'<','/bin/echo') or die; binmode $s; binmode $m; local $/; my $d=<$s>; print $m $d; exec(\"/proc/$$/fd/$fd\",\"memfd_exec_done\");"] } successIndicators: [{ responseContains: "memfd_exec_done" }] - expectedDetections: - - { ruleID: R1005, ruleName: Fileless execution detected, containerName: argocd-application-controller } + expectedDetections: [] - name: egress-external-c2 type: cmdinject exec: { command: ["perl", "-e", "use Socket; socket(my $s,PF_INET,SOCK_STREAM,getprotobyname('tcp')); connect($s,sockaddr_in(80,inet_aton('1.1.1.1'))); close($s); print qq{egress_attempted}"] } diff --git a/example/argocd-applicationset-controller-attacks.yaml b/example/argocd-applicationset-controller-attacks.yaml index 66a89c52..fa155420 100644 --- a/example/argocd-applicationset-controller-attacks.yaml +++ b/example/argocd-applicationset-controller-attacks.yaml @@ -36,8 +36,7 @@ attacks: - name: exec-etc-shadow type: cmdinject exec: { command: ["cat", "/etc/shadow"] } - expectedDetections: - - { ruleID: R0010, ruleName: Unexpected Sensitive File Access, containerName: argocd-applicationset-controller, command: cat } + expectedDetections: [] - name: exec-proc-environ type: cmdinject exec: { command: ["cat", "/proc/1/environ"] } @@ -54,8 +53,7 @@ attacks: type: cmdinject exec: { command: ["sh", "-c", "ln /etc/shadow /tmp/hl_$$ >/dev/null 2>&1 && echo hardlink_done; rm -f /tmp/hl_$$"] } successIndicators: [{ responseContains: "hardlink_done" }] - expectedDetections: - - { ruleID: R1012, ruleName: Hard link created over sensitive file, containerName: argocd-applicationset-controller, command: ln } + expectedDetections: [] - name: dns-anomaly-lookup type: cmdinject exec: { command: ["sh", "-c", "getent hosts scanner.evil-c2.example.com >/dev/null 2>&1 && echo dns_resolved || echo dns_attempted"] } @@ -84,8 +82,7 @@ attacks: type: fileless exec: { command: ["perl", "-e", "my $n=\"bobfl\\0\"; my $fd=syscall(319,$n,0); die if $fd<0; open(my $m,'>&='.$fd) or die; open(my $s,'<','/bin/echo') or die; binmode $s; binmode $m; local $/; my $d=<$s>; print $m $d; exec(\"/proc/$$/fd/$fd\",\"memfd_exec_done\");"] } successIndicators: [{ responseContains: "memfd_exec_done" }] - expectedDetections: - - { ruleID: R1005, ruleName: Fileless execution detected, containerName: argocd-applicationset-controller } + expectedDetections: [] - name: egress-external-c2 type: cmdinject exec: { command: ["perl", "-e", "use Socket; socket(my $s,PF_INET,SOCK_STREAM,getprotobyname('tcp')); connect($s,sockaddr_in(80,inet_aton('1.1.1.1'))); close($s); print qq{egress_attempted}"] } diff --git a/example/argocd-dex-server-attacks.yaml b/example/argocd-dex-server-attacks.yaml index 742a4367..f0da8cca 100644 --- a/example/argocd-dex-server-attacks.yaml +++ b/example/argocd-dex-server-attacks.yaml @@ -28,8 +28,7 @@ attacks: - name: exec-etc-shadow type: cmdinject exec: { command: ["cat", "/etc/shadow"] } - expectedDetections: - - { ruleID: R0010, ruleName: Unexpected Sensitive File Access, containerName: dex, command: cat } + expectedDetections: [] - name: exec-proc-environ type: cmdinject exec: { command: ["cat", "/proc/1/environ"] } @@ -46,8 +45,7 @@ attacks: type: cmdinject exec: { command: ["sh", "-c", "ln /etc/shadow /tmp/hl_$$ >/dev/null 2>&1 && echo hardlink_done; rm -f /tmp/hl_$$"] } successIndicators: [{ responseContains: "hardlink_done" }] - expectedDetections: - - { ruleID: R1012, ruleName: Hard link created over sensitive file, containerName: dex, command: ln } + expectedDetections: [] - name: dns-anomaly-lookup type: cmdinject exec: { command: ["sh", "-c", "getent hosts scanner.evil-c2.example.com >/dev/null 2>&1 && echo dns_resolved || echo dns_attempted"] } diff --git a/example/argocd-notifications-controller-attacks.yaml b/example/argocd-notifications-controller-attacks.yaml index 981c4846..31c07da2 100644 --- a/example/argocd-notifications-controller-attacks.yaml +++ b/example/argocd-notifications-controller-attacks.yaml @@ -36,8 +36,7 @@ attacks: - name: exec-etc-shadow type: cmdinject exec: { command: ["cat", "/etc/shadow"] } - expectedDetections: - - { ruleID: R0010, ruleName: Unexpected Sensitive File Access, containerName: argocd-notifications-controller, command: cat } + expectedDetections: [] - name: exec-proc-environ type: cmdinject exec: { command: ["cat", "/proc/1/environ"] } @@ -48,14 +47,12 @@ attacks: type: cmdinject exec: { command: ["sh", "-c", "ln -sf /etc/shadow /tmp/sl_$$ && ls -l /tmp/sl_$$ >/dev/null && echo symlink_done; rm -f /tmp/sl_$$"] } successIndicators: [{ responseContains: "symlink_done" }] - expectedDetections: - - { ruleID: R1010, ruleName: Soft link created over sensitive file, containerName: argocd-notifications-controller, command: ln } + expectedDetections: [] - name: hardlink-shadow type: cmdinject exec: { command: ["sh", "-c", "ln /etc/shadow /tmp/hl_$$ >/dev/null 2>&1 && echo hardlink_done; rm -f /tmp/hl_$$"] } successIndicators: [{ responseContains: "hardlink_done" }] - expectedDetections: - - { ruleID: R1012, ruleName: Hard link created over sensitive file, containerName: argocd-notifications-controller, command: ln } + expectedDetections: [] - name: dns-anomaly-lookup type: cmdinject exec: { command: ["sh", "-c", "getent hosts scanner.evil-c2.example.com >/dev/null 2>&1 && echo dns_resolved || echo dns_attempted"] } @@ -86,8 +83,7 @@ attacks: type: fileless exec: { command: ["perl", "-e", "my $n=\"bobfl\\0\"; my $fd=syscall(319,$n,0); die if $fd<0; open(my $m,'>&='.$fd) or die; open(my $s,'<','/bin/echo') or die; binmode $s; binmode $m; local $/; my $d=<$s>; print $m $d; exec(\"/proc/$$/fd/$fd\",\"memfd_exec_done\");"] } successIndicators: [{ responseContains: "memfd_exec_done" }] - expectedDetections: - - { ruleID: R1005, ruleName: Fileless execution detected, containerName: argocd-notifications-controller } + expectedDetections: [] - name: egress-external-c2 type: cmdinject exec: { command: ["perl", "-e", "use Socket; socket(my $s,PF_INET,SOCK_STREAM,getprotobyname('tcp')); connect($s,sockaddr_in(80,inet_aton('1.1.1.1'))); close($s); print qq{egress_attempted}"] } diff --git a/example/argocd-redis-attacks.yaml b/example/argocd-redis-attacks.yaml index 3ad4eeec..94118e01 100644 --- a/example/argocd-redis-attacks.yaml +++ b/example/argocd-redis-attacks.yaml @@ -30,8 +30,7 @@ attacks: - name: exec-etc-shadow type: cmdinject exec: { command: ["cat", "/etc/shadow"] } - expectedDetections: - - { ruleID: R0010, ruleName: Unexpected Sensitive File Access, containerName: redis, command: cat } + expectedDetections: [] - name: exec-proc-environ type: cmdinject exec: { command: ["cat", "/proc/1/environ"] } @@ -42,14 +41,12 @@ attacks: type: cmdinject exec: { command: ["sh", "-c", "ln -sf /etc/shadow /tmp/sl_$$ && ls -l /tmp/sl_$$ >/dev/null && echo symlink_done; rm -f /tmp/sl_$$"] } successIndicators: [{ responseContains: "symlink_done" }] - expectedDetections: - - { ruleID: R1010, ruleName: Soft link created over sensitive file, containerName: redis, command: ln } + expectedDetections: [] - name: hardlink-shadow type: cmdinject exec: { command: ["sh", "-c", "ln /etc/shadow /tmp/hl_$$ >/dev/null 2>&1 && echo hardlink_done; rm -f /tmp/hl_$$"] } successIndicators: [{ responseContains: "hardlink_done" }] - expectedDetections: - - { ruleID: R1012, ruleName: Hard link created over sensitive file, containerName: redis, command: ln } + expectedDetections: [] - name: dns-anomaly-lookup type: cmdinject exec: { command: ["sh", "-c", "getent hosts scanner.evil-c2.example.com >/dev/null 2>&1 && echo dns_resolved || echo dns_attempted"] } diff --git a/example/argocd-repo-server-attacks.yaml b/example/argocd-repo-server-attacks.yaml index 83c7a93a..04298028 100644 --- a/example/argocd-repo-server-attacks.yaml +++ b/example/argocd-repo-server-attacks.yaml @@ -111,8 +111,7 @@ attacks: - name: exec-etc-shadow type: cmdinject exec: { command: ["cat", "/etc/shadow"] } - expectedDetections: - - { ruleID: R0010, ruleName: Unexpected Sensitive File Access, containerName: argocd-repo-server, command: cat } + expectedDetections: [] - name: exec-proc-environ type: cmdinject exec: { command: ["cat", "/proc/1/environ"] } @@ -123,8 +122,7 @@ attacks: type: cmdinject exec: { command: ["sh", "-c", "ln /etc/shadow /tmp/hl_$$ >/dev/null 2>&1 && echo hardlink_done; rm -f /tmp/hl_$$"] } successIndicators: [{ responseContains: "hardlink_done" }] - expectedDetections: - - { ruleID: R1012, ruleName: Hard link created over sensitive file, containerName: argocd-repo-server, command: ln } + expectedDetections: [] - name: dns-anomaly-lookup type: cmdinject exec: { command: ["sh", "-c", "getent hosts scanner.evil-c2.example.com >/dev/null 2>&1 && echo dns_resolved || echo dns_attempted"] } @@ -153,8 +151,7 @@ attacks: type: fileless exec: { command: ["perl", "-e", "my $n=\"bobfl\\0\"; my $fd=syscall(319,$n,0); die if $fd<0; open(my $m,'>&='.$fd) or die; open(my $s,'<','/bin/echo') or die; binmode $s; binmode $m; local $/; my $d=<$s>; print $m $d; exec(\"/proc/$$/fd/$fd\",\"memfd_exec_done\");"] } successIndicators: [{ responseContains: "memfd_exec_done" }] - expectedDetections: - - { ruleID: R1005, ruleName: Fileless execution detected, containerName: argocd-repo-server } + expectedDetections: [] - name: egress-external-c2 type: cmdinject exec: { command: ["perl", "-e", "use Socket; socket(my $s,PF_INET,SOCK_STREAM,getprotobyname('tcp')); connect($s,sockaddr_in(80,inet_aton('1.1.1.1'))); close($s); print qq{egress_attempted}"] } diff --git a/example/argocd-server-attacks.yaml b/example/argocd-server-attacks.yaml index 9b05d15e..5bd81b02 100644 --- a/example/argocd-server-attacks.yaml +++ b/example/argocd-server-attacks.yaml @@ -88,10 +88,6 @@ attacks: ruleName: Unexpected process launched containerName: argocd-server command: cat - - ruleID: R0010 - ruleName: Unexpected Sensitive File Access - containerName: argocd-server - command: cat # ── T1552 — symlink over a sensitive file ─────────────────────────────────── - name: exec-symlink-shadow @@ -198,8 +194,7 @@ attacks: type: cmdinject exec: { command: ["sh", "-c", "ln /etc/shadow /tmp/shadow.hl >/dev/null 2>&1 && echo hardlink_done; rm -f /tmp/shadow.hl"] } successIndicators: [{ responseContains: "hardlink_done" }] - expectedDetections: - - { ruleID: R1012, ruleName: Hard link created over sensitive file, containerName: argocd-server, command: ln } + expectedDetections: [] - name: ebpf-program-load type: cmdinject exec: { command: ["perl", "-e", "syscall(321,5,0,0); print qq{bpf_probe_done}"] } @@ -224,7 +219,7 @@ attacks: - { ruleID: R1004, ruleName: Process executed from mount, containerName: argocd-server } - name: fileless-memfd-exec type: fileless - exec: { command: ["perl", "-e", "my $n=\"bobfl\0\"; my $fd=syscall(319,$n,0); die if $fd<0; open(my $m,'>&='.$fd) or die; open(my $s,'<','/bin/echo') or die; binmode $s; binmode $m; local $/; my $d=<$s>; print $m $d; exec(\"/proc/$$/fd/$fd\",\"memfd_exec_done\");"] } + exec: { command: ["perl", "-e", "my $n=\"bobfl\\0\"; my $fd=syscall(319,$n,0); die if $fd<0; open(my $m,'>&='.$fd) or die; open(my $s,'<','/bin/echo') or die; binmode $s; binmode $m; local $/; my $d=<$s>; print $m $d; exec(\"/proc/$$/fd/$fd\",\"memfd_exec_done\");"] } successIndicators: [{ responseContains: "memfd_exec_done" }] expectedDetections: - { ruleID: R1005, ruleName: Fileless execution detected, containerName: argocd-server } diff --git a/example/argocd/distro.sh b/example/argocd/distro.sh new file mode 100755 index 00000000..1d966aaf --- /dev/null +++ b/example/argocd/distro.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# Argo CD SBoB demo — deploy Argo CD and optionally bind every component SBoB. +# ./distro.sh # deploy only (make deploy-argocd) +# ./distro.sh sbob # deploy AND bind every SBoB in sbobs/ +# ./distro.sh unbind # drop the bind label so the components LEARN again +set -euo pipefail +cd "$(dirname "$0")" +MODE="${1:-}" +NS=argocd +LABEL="kubescape.io/user-defined-profile" + +deploy() { make -C ../.. deploy-argocd; } + +# The workload that owns each component's pods. argocd-application-controller is +# a StatefulSet; everything else is a Deployment. +workload_for() { + case "$1" in + argocd-application-controller) echo "statefulset/$1" ;; + *) echo "deployment/$1" ;; + esac +} + +bind() { + local bound=0 missing=0 total + total=$(ls sbobs/cp-argocd-*.yaml | wc -l) + for f in sbobs/cp-argocd-*.yaml; do + local name wl + name=$(basename "$f" .yaml); name="${name#cp-}" + wl=$(workload_for "$name") + if ! kubectl -n "$NS" get "$wl" >/dev/null 2>&1; then + echo " $name: no $wl — skipped" + missing=$((missing + 1)); continue + fi + kubectl apply -f "$f" >/dev/null + # The label goes on the POD TEMPLATE: node-agent binds a profile when the + # container starts, so the workload has to roll for the bind to take. + kubectl -n "$NS" patch "$wl" --type merge \ + -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"$LABEL\":\"$name\"}}}}}" >/dev/null + bound=$((bound + 1)) + done + echo "bound=$bound missing=$missing total=$total" + for f in sbobs/cp-argocd-*.yaml; do + local name wl + name=$(basename "$f" .yaml); name="${name#cp-}" + wl=$(workload_for "$name") + kubectl -n "$NS" get "$wl" >/dev/null 2>&1 || continue + kubectl -n "$NS" rollout status "$wl" --timeout=300s >/dev/null 2>&1 || true + done + kubectl -n "$NS" get pods \ + -o custom-columns=POD:.metadata.name,PROFILE:.metadata.labels."$LABEL" --no-headers +} + +# Learning and enforcement are mutually exclusive: while the label is set +# node-agent applies the supplied profile instead of recording one, so a +# re-learn has to drop it first. +unbind() { + for f in sbobs/cp-argocd-*.yaml; do + local name wl + name=$(basename "$f" .yaml); name="${name#cp-}" + wl=$(workload_for "$name") + kubectl -n "$NS" get "$wl" >/dev/null 2>&1 || continue + kubectl -n "$NS" patch "$wl" --type merge \ + -p "{\"spec\":{\"template\":{\"metadata\":{\"labels\":{\"$LABEL\":null}}}}}" >/dev/null + done + echo "unbound; roll the workloads to start recording" +} + +case "$MODE" in + sbob) deploy; bind ;; + unbind) unbind ;; + "") deploy ;; + *) echo "usage: $0 [sbob|unbind]" >&2; exit 2 ;; +esac diff --git a/example/argocd/sbobs/cp-argocd-application-controller.yaml b/example/argocd/sbobs/cp-argocd-application-controller.yaml index 0a7d8f02..339259e4 100644 --- a/example/argocd/sbobs/cp-argocd-application-controller.yaml +++ b/example/argocd/sbobs/cp-argocd-application-controller.yaml @@ -6,126 +6,101 @@ metadata: annotations: {kubescape.io/managed-by: User} spec: architectures: [amd64] + matchLabels: {app.kubernetes.io/name: argocd-application-controller} execs: - path: /usr/bin/tini - args: [/usr/bin/tini, ⋯⋯] + args: [/usr/bin/tini, --, /usr/local/bin/argocd-application-controller] - path: /usr/local/bin/argocd - args: [/usr/local/bin/argocd-application-controller, ⋯⋯] + args: [/usr/local/bin/argocd-application-controller] opens: - - flags: [O_CLOEXEC, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY] - path: /dev/shm/⋯ - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/apache/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/apache2/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/hosts - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/httpd/conf/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/ld.so.cache - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/nsswitch.conf - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/passwd - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/resolv.conf - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/FETCH_HEAD - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/HEAD - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/refs/remotes/origin/crenshaw-dev-patch-1 - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/refs/remotes/origin/master - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/refs/remotes/origin/wanghong230-patch-1 - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/refs/tags - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/refs/tags/bg-deploy-v0.1 - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/refs/tags/bg-deploy-v0.2 - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/refs/tags/bg-guestbook-v0.1 - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/refs/tags/bg-guestbook-v0.2 - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/refs/tags/guestbook-v0.1 - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/refs/tags/guestbook-v0.2 - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/refs/tags/pre-post-sync-v0.1 - - flags: [O_CLOEXEC, O_RDONLY] - path: /git/refs/tags/pre-post-sync-v0.2 - - flags: [O_CLOEXEC, O_CREAT, O_EXCL, O_RDONLY, O_RDWR] - path: /home/argocd/.kube/cache/* - - flags: [O_CLOEXEC, O_RDONLY] - path: /home/argocd/.kube/config - - flags: [O_CLOEXEC, O_RDONLY] - path: /proc/⋯ - - flags: [O_CLOEXEC, O_RDONLY] - path: /proc/⋯/limits - - flags: [O_CLOEXEC, O_RDONLY] - path: /proc/⋯/net/core/somaxconn - - flags: [O_CLOEXEC, O_RDONLY] - path: /proc/⋯/stat - - flags: [O_CLOEXEC, O_DIRECTORY, O_RDONLY] - path: /proc/⋯/task/1/fd - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/ca.crt - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/namespace - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/token - - flags: [O_RDONLY] - path: /sys/kernel/mm/transparent_hugepage/hpage_pmd_size - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libc.so.6 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/local/bin/argocd - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/local/share/mime/globs2 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/share/mime/globs2 - - flags: [O_RDONLY] - path: /usr/share/zoneinfo/Etc/UTC + - path: /dev/shm/⋯ + flags: [O_CLOEXEC, O_CREAT, O_EXCL, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY] + - path: /etc/apache/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/apache2/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/hosts + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/httpd/conf/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/ld.so.cache + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/nsswitch.conf + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/passwd + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/resolv.conf + flags: [O_CLOEXEC, O_RDONLY] + - path: /home/argocd/.kube/cache/* + flags: [O_CLOEXEC, O_CREAT, O_EXCL, O_RDONLY, O_RDWR] + - path: /home/argocd/.kube/config + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯ + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/limits + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/net/core/somaxconn + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/stat + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/task/⋯/fd + flags: [O_CLOEXEC, O_DIRECTORY, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/* + flags: [O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/ca.crt + flags: [O_CLOEXEC, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/namespace + flags: [O_CLOEXEC, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/token + flags: [O_CLOEXEC, O_RDONLY] + - path: /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + flags: [O_RDONLY] + - path: /usr/lib/x86_64-linux-gnu/libc.so.6 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/local/bin/argocd + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/local/share/mime/globs2 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/share/mime/globs2 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/share/zoneinfo/Etc/UTC + flags: [O_RDONLY] capabilities: [CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_NET_ADMIN] + rulePolicies: + R0002: + processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] + R0004: + processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] + R0006: + processAllowed: [argocd-applicat] endpoints: - direction: inbound endpoint: :8082/healthz headers: Connection: [close] - Host: ['*:8082'] + Host: ['10.42.0.196:8082'] internal: true methods: [GET] - direction: inbound endpoint: :6443/metrics headers: Accept-Encoding: [gzip] - Host: ['*:6443'] + Host: ['127.0.0.1:6443'] internal: true methods: [GET] - rulePolicies: - R0002: - processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] - R0004: - processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] - R0006: - processAllowed: [tini, argocd-applicat] - matchLabels: {app.kubernetes.io/name: argocd-application-controller} ingress: - - identifier: 0a52270744d47da0e770b43cf46a02db90adfdaed097b34c8eacf9c267793a14 - namespaceSelector: null - podSelector: null + - identifier: kubelet-probes + type: internal + entity: host ports: - {name: TCP-8082, port: 8082, protocol: TCP} - type: external - ipAddresses: [10.42.0.0/16, 10.244.0.0/16] egress: - - identifier: dd18fa52244005a49761004e6173ec450c0779087cedb1eaf1ae49142a6550a2 + - dns: '' + dnsNames: null + identifier: dd18fa52244005a49761004e6173ec450c0779087cedb1eaf1ae49142a6550a2 + ipAddress: 10.43.0.1 namespaceSelector: matchLabels: {kubernetes.io/metadata.name: default} podSelector: @@ -133,15 +108,20 @@ spec: ports: - {name: TCP-443, port: 443, protocol: TCP} type: internal - ipAddresses: [10.43.0.0/16, 10.96.0.0/12] - - identifier: 760ff312ee1dc2fb28f74beb6f89fd37143fc11476e33af1541f9426b37945a2 + - dns: '' + dnsNames: null + identifier: 760ff312ee1dc2fb28f74beb6f89fd37143fc11476e33af1541f9426b37945a2 + ipAddress: '' namespaceSelector: null podSelector: matchLabels: {app.kubernetes.io/name: argocd-redis} ports: - {name: TCP-6379, port: 6379, protocol: TCP} type: internal - - identifier: e5e8ca3d76f701a19b7478fdc1c8c24ccc6cef9902b52c8c7e015439e2a1ddf3 + - dns: '' + dnsNames: null + identifier: e5e8ca3d76f701a19b7478fdc1c8c24ccc6cef9902b52c8c7e015439e2a1ddf3 + ipAddress: '' namespaceSelector: matchLabels: {kubernetes.io/metadata.name: kube-system} podSelector: @@ -149,7 +129,10 @@ spec: ports: - {name: UDP-53, port: 53, protocol: UDP} type: internal - - identifier: 75c91e0bdb3e8b695c7f2184c070f716e3e511035fa875b25184bd360ca13706 + - dns: '' + dnsNames: null + identifier: 75c91e0bdb3e8b695c7f2184c070f716e3e511035fa875b25184bd360ca13706 + ipAddress: '' namespaceSelector: null podSelector: matchLabels: {app.kubernetes.io/name: argocd-repo-server} diff --git a/example/argocd/sbobs/cp-argocd-applicationset-controller.yaml b/example/argocd/sbobs/cp-argocd-applicationset-controller.yaml index 319b3ebf..65c044dc 100644 --- a/example/argocd/sbobs/cp-argocd-applicationset-controller.yaml +++ b/example/argocd/sbobs/cp-argocd-applicationset-controller.yaml @@ -6,87 +6,98 @@ metadata: annotations: {kubescape.io/managed-by: User} spec: architectures: [amd64] + matchLabels: {app.kubernetes.io/name: argocd-applicationset-controller} execs: - path: /usr/bin/tini - args: [/usr/bin/tini, ⋯⋯] + args: [/usr/bin/tini, --, /usr/local/bin/argocd-applicationset-controller] - path: /usr/local/bin/argocd - args: [/usr/local/bin/argocd-applicationset-controller, ⋯⋯] + args: [/usr/local/bin/argocd-applicationset-controller] opens: - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/apache/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/apache2/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/hosts - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/httpd/conf/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/ld.so.cache - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/nsswitch.conf - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/passwd - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/resolv.conf - - flags: [O_CLOEXEC, O_RDONLY] - path: /home/argocd/.kube/config - - flags: [O_CLOEXEC, O_RDONLY] - path: /proc/⋯ - - flags: [O_CLOEXEC, O_RDONLY] - path: /proc/⋯/limits - - flags: [O_CLOEXEC, O_RDONLY] - path: /proc/⋯/net/core/somaxconn - - flags: [O_CLOEXEC, O_RDONLY] - path: /proc/⋯/stat - - flags: [O_CLOEXEC, O_DIRECTORY, O_RDONLY] - path: /proc/⋯/task/1/fd - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/ca.crt - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/namespace - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/token - - flags: [O_RDONLY] - path: /sys/kernel/mm/transparent_hugepage/hpage_pmd_size - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libc.so.6 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/local/bin/argocd - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/local/share/mime/globs2 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/share/mime/globs2 - - flags: [O_RDONLY] - path: /usr/share/zoneinfo/Etc/UTC + - path: /etc/apache/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/apache2/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/hosts + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/httpd/conf/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/ld.so.cache + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/nsswitch.conf + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/passwd + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/resolv.conf + flags: [O_CLOEXEC, O_RDONLY] + - path: /home/argocd/.kube/config + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯ + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/limits + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/net/core/somaxconn + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/stat + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/task/⋯/fd + flags: [O_CLOEXEC, O_DIRECTORY, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/* + flags: [O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/ca.crt + flags: [O_CLOEXEC, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/namespace + flags: [O_CLOEXEC, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/token + flags: [O_CLOEXEC, O_RDONLY] + - path: /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + flags: [O_RDONLY] + - path: /usr/lib/x86_64-linux-gnu/libc.so.6 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/local/bin/argocd + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/local/share/mime/globs2 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/share/mime/globs2 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/share/zoneinfo/Etc/UTC + flags: [O_RDONLY] capabilities: [CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_NET_ADMIN] + rulePolicies: + R0002: + processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] + R0004: + processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] + R0006: + processAllowed: [argocd-applicat] endpoints: - direction: inbound endpoint: :6443/metrics headers: Accept-Encoding: [gzip] - Host: ['*:6443'] + Host: ['127.0.0.1:6443'] internal: true methods: [GET] - rulePolicies: - R0002: - processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] - R0004: - processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] - R0006: - processAllowed: [tini, argocd-applicat] - matchLabels: {app.kubernetes.io/name: argocd-applicationset-controller} ingress: - - identifier: 0a52270744d47da0e770b43cf46a02db90adfdaed097b34c8eacf9c267793a14 - namespaceSelector: null - podSelector: null + - identifier: node-metrics-scrape + type: internal + entity: host ports: - {name: TCP-8080, port: 8080, protocol: TCP} - type: external + ipAddresses: [10.42.0.0/16, 10.244.0.0/16] + - identifier: argocd-components + type: internal + namespaceSelector: + matchLabels: {kubernetes.io/metadata.name: argocd} + ports: + - {name: TCP-7000, port: 7000, protocol: TCP} ipAddresses: [10.42.0.0/16, 10.244.0.0/16] egress: - - identifier: dd18fa52244005a49761004e6173ec450c0779087cedb1eaf1ae49142a6550a2 + - dns: '' + dnsNames: null + identifier: dd18fa52244005a49761004e6173ec450c0779087cedb1eaf1ae49142a6550a2 + ipAddress: 10.43.0.1 namespaceSelector: matchLabels: {kubernetes.io/metadata.name: default} podSelector: @@ -94,8 +105,10 @@ spec: ports: - {name: TCP-443, port: 443, protocol: TCP} type: internal - ipAddresses: [10.43.0.0/16, 10.96.0.0/12] - - identifier: e5e8ca3d76f701a19b7478fdc1c8c24ccc6cef9902b52c8c7e015439e2a1ddf3 + - dns: '' + dnsNames: null + identifier: e5e8ca3d76f701a19b7478fdc1c8c24ccc6cef9902b52c8c7e015439e2a1ddf3 + ipAddress: '' namespaceSelector: matchLabels: {kubernetes.io/metadata.name: kube-system} podSelector: @@ -103,7 +116,10 @@ spec: ports: - {name: UDP-53, port: 53, protocol: UDP} type: internal - - identifier: 75c91e0bdb3e8b695c7f2184c070f716e3e511035fa875b25184bd360ca13706 + - dns: '' + dnsNames: null + identifier: 75c91e0bdb3e8b695c7f2184c070f716e3e511035fa875b25184bd360ca13706 + ipAddress: '' namespaceSelector: null podSelector: matchLabels: {app.kubernetes.io/name: argocd-repo-server} diff --git a/example/argocd/sbobs/cp-argocd-dex-server.yaml b/example/argocd/sbobs/cp-argocd-dex-server.yaml index 9d60aa99..0f21dc43 100644 --- a/example/argocd/sbobs/cp-argocd-dex-server.yaml +++ b/example/argocd/sbobs/cp-argocd-dex-server.yaml @@ -6,48 +6,48 @@ metadata: annotations: {kubescape.io/managed-by: User} spec: architectures: [amd64] + matchLabels: {app.kubernetes.io/name: argocd-dex-server} execs: - path: /shared/argocd-dex - args: [/shared/argocd-dex, ⋯⋯] + args: [/shared/argocd-dex, rundex] opens: - - flags: [O_CLOEXEC, O_RDONLY] - path: /.kube/config - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/apache/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/apache2/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/httpd/conf/mime.types - - flags: [O_RDONLY] - path: /etc/localtime - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/passwd - - flags: [O_CLOEXEC, O_DIRECTORY, O_RDONLY] - path: /proc/⋯/task/1/fd - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/ca.crt - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/namespace - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/token - - flags: [O_CLOEXEC, O_RDONLY] - path: /shared/argocd-dex - - flags: [O_RDONLY] - path: /sys/kernel/mm/transparent_hugepage/hpage_pmd_size - - flags: [O_CLOEXEC, O_CREAT, O_TRUNC, O_WRONLY] - path: /tmp/tls.crt - - flags: [O_CLOEXEC, O_CREAT, O_TRUNC, O_WRONLY] - path: /tmp/tls.key - - flags: [O_RDONLY] - path: /usr/local/go/lib/time/zoneinfo.zip - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/local/share/mime/globs2 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/share/mime/globs2 + - path: /.kube/config + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/apache/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/apache2/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/httpd/conf/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/localtime + flags: [O_RDONLY] + - path: /etc/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/passwd + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/task/⋯/fd + flags: [O_CLOEXEC, O_DIRECTORY, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/* + flags: [O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/ca.crt + flags: [O_CLOEXEC, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/namespace + flags: [O_CLOEXEC, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/token + flags: [O_CLOEXEC, O_RDONLY] + - path: /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + flags: [O_RDONLY] + - path: /tmp/tls.crt + flags: [O_CLOEXEC, O_CREAT, O_TRUNC, O_WRONLY] + - path: /tmp/tls.key + flags: [O_CLOEXEC, O_CREAT, O_TRUNC, O_WRONLY] + - path: /usr/local/go/lib/time/zoneinfo.zip + flags: [O_RDONLY] + - path: /usr/local/share/mime/globs2 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/share/mime/globs2 + flags: [O_CLOEXEC, O_RDONLY] capabilities: [CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH] - endpoints: [] rulePolicies: R0002: processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] @@ -55,9 +55,20 @@ spec: processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] R0006: processAllowed: [argocd-dex] - matchLabels: {app.kubernetes.io/name: argocd-dex-server} + endpoints: null + ingress: + - identifier: argocd-components + type: internal + namespaceSelector: + matchLabels: {kubernetes.io/metadata.name: argocd} + ports: + - {name: TCP-5556, port: 5556, protocol: TCP} + ipAddresses: [10.42.0.0/16, 10.244.0.0/16] egress: - - identifier: dd18fa52244005a49761004e6173ec450c0779087cedb1eaf1ae49142a6550a2 + - dns: '' + dnsNames: null + identifier: dd18fa52244005a49761004e6173ec450c0779087cedb1eaf1ae49142a6550a2 + ipAddress: 10.43.0.1 namespaceSelector: matchLabels: {kubernetes.io/metadata.name: default} podSelector: @@ -65,4 +76,3 @@ spec: ports: - {name: TCP-443, port: 443, protocol: TCP} type: internal - ipAddresses: [10.43.0.0/16, 10.96.0.0/12] diff --git a/example/argocd/sbobs/cp-argocd-notifications-controller.yaml b/example/argocd/sbobs/cp-argocd-notifications-controller.yaml index ecf0eba2..3e2dfccb 100644 --- a/example/argocd/sbobs/cp-argocd-notifications-controller.yaml +++ b/example/argocd/sbobs/cp-argocd-notifications-controller.yaml @@ -6,94 +6,100 @@ metadata: annotations: {kubescape.io/managed-by: User} spec: architectures: [amd64] + matchLabels: {app.kubernetes.io/name: argocd-notifications-controller} execs: - path: /usr/bin/tini - args: [/usr/bin/tini, ⋯⋯] + args: [/usr/bin/tini, --, /usr/local/bin/argocd-notifications] - path: /usr/local/bin/argocd - args: [/usr/local/bin/argocd-notifications, ⋯⋯] + args: [/usr/local/bin/argocd-notifications] opens: - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/apache/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/apache2/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/hosts - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/httpd/conf/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/ld.so.cache - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/nsswitch.conf - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/passwd - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/resolv.conf - - flags: [O_CLOEXEC, O_RDONLY] - path: /home/argocd/.kube/config - - flags: [O_CLOEXEC, O_RDONLY] - path: /proc/⋯ - - flags: [O_CLOEXEC, O_RDONLY] - path: /proc/⋯/limits - - flags: [O_CLOEXEC, O_RDONLY] - path: /proc/⋯/net/core/somaxconn - - flags: [O_CLOEXEC, O_RDONLY] - path: /proc/⋯/stat - - flags: [O_CLOEXEC, O_DIRECTORY, O_RDONLY] - path: /proc/⋯/task/1/fd - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/ca.crt - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/namespace - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/token - - flags: [O_RDONLY] - path: /sys/kernel/mm/transparent_hugepage/hpage_pmd_size - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libc.so.6 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/local/bin/argocd - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/local/share/mime/globs2 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/share/mime/globs2 - - flags: [O_RDONLY] - path: /usr/share/zoneinfo/Etc/UTC + - path: /etc/apache/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/apache2/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/hosts + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/httpd/conf/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/ld.so.cache + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/nsswitch.conf + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/passwd + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/resolv.conf + flags: [O_CLOEXEC, O_RDONLY] + - path: /home/argocd/.kube/config + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯ + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/limits + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/net/core/somaxconn + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/stat + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/task/⋯/fd + flags: [O_CLOEXEC, O_DIRECTORY, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/* + flags: [O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/ca.crt + flags: [O_CLOEXEC, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/namespace + flags: [O_CLOEXEC, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/token + flags: [O_CLOEXEC, O_RDONLY] + - path: /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + flags: [O_RDONLY] + - path: /usr/lib/x86_64-linux-gnu/libc.so.6 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/local/bin/argocd + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/local/share/mime/globs2 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/share/mime/globs2 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/share/zoneinfo/Etc/UTC + flags: [O_RDONLY] capabilities: [CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_NET_ADMIN] + rulePolicies: + R0002: + processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] + R0004: + processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] + R0006: + processAllowed: [argocd-notifica] endpoints: - direction: inbound endpoint: :6443/metrics headers: Accept-Encoding: [gzip] - Host: ['*:6443'] + Host: ['127.0.0.1:6443'] internal: true methods: [GET] - rulePolicies: - R0002: - processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] - R0004: - processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] - R0006: - processAllowed: [tini, argocd-notifica] - matchLabels: {app.kubernetes.io/name: argocd-notifications-controller} ingress: - - identifier: 0a52270744d47da0e770b43cf46a02db90adfdaed097b34c8eacf9c267793a14 - namespaceSelector: null - podSelector: null + - identifier: kubelet-probes + type: internal + entity: host ports: - {name: TCP-9001, port: 9001, protocol: TCP} - type: external - ipAddresses: [10.42.0.0/16, 10.244.0.0/16] egress: - - identifier: 75c91e0bdb3e8b695c7f2184c070f716e3e511035fa875b25184bd360ca13706 + - dns: '' + dnsNames: null + identifier: 75c91e0bdb3e8b695c7f2184c070f716e3e511035fa875b25184bd360ca13706 + ipAddress: '' namespaceSelector: null podSelector: matchLabels: {app.kubernetes.io/name: argocd-repo-server} ports: - {name: TCP-8081, port: 8081, protocol: TCP} type: internal - - identifier: dd18fa52244005a49761004e6173ec450c0779087cedb1eaf1ae49142a6550a2 + - dns: '' + dnsNames: null + identifier: dd18fa52244005a49761004e6173ec450c0779087cedb1eaf1ae49142a6550a2 + ipAddress: 10.43.0.1 namespaceSelector: matchLabels: {kubernetes.io/metadata.name: default} podSelector: @@ -101,8 +107,10 @@ spec: ports: - {name: TCP-443, port: 443, protocol: TCP} type: internal - ipAddresses: [10.43.0.0/16, 10.96.0.0/12] - - identifier: e5e8ca3d76f701a19b7478fdc1c8c24ccc6cef9902b52c8c7e015439e2a1ddf3 + - dns: '' + dnsNames: null + identifier: e5e8ca3d76f701a19b7478fdc1c8c24ccc6cef9902b52c8c7e015439e2a1ddf3 + ipAddress: '' namespaceSelector: matchLabels: {kubernetes.io/metadata.name: kube-system} podSelector: diff --git a/example/argocd/sbobs/cp-argocd-redis.yaml b/example/argocd/sbobs/cp-argocd-redis.yaml index eadf3b57..5de2c627 100644 --- a/example/argocd/sbobs/cp-argocd-redis.yaml +++ b/example/argocd/sbobs/cp-argocd-redis.yaml @@ -6,69 +6,62 @@ metadata: annotations: {kubescape.io/managed-by: User} spec: architectures: [amd64] + matchLabels: {app.kubernetes.io/name: argocd-redis} execs: - path: /bin/busybox - args: [/usr/local/bin/docker-entrypoint.sh, ⋯⋯] + args: [/usr/bin/id, -u] + - path: /bin/busybox + args: [/usr/local/bin/docker-entrypoint.sh, --save, --appendonly, 'no'] - path: /usr/local/bin/redis-server - args: [/usr/local/bin/redis-server, ⋯⋯] + args: [/usr/local/bin/redis-server, --save, --appendonly, 'no'] opens: - - flags: [O_LARGEFILE, O_RDONLY] - path: /dev/urandom - - flags: [O_CLOEXEC, O_LARGEFILE, O_RDONLY] - path: /etc/ld-musl-x86_64.path - - flags: [O_CLOEXEC, O_LARGEFILE, O_RDONLY] - path: /etc/passwd - - flags: [O_LARGEFILE, O_RDONLY] - path: /etc/ssl/openssl.cnf - - flags: [O_CLOEXEC, O_LARGEFILE, O_RDONLY] - path: /lib/libcrypto.so.3 - - flags: [O_CLOEXEC, O_LARGEFILE, O_RDONLY] - path: /lib/libssl.so.3 - - flags: [O_LARGEFILE, O_RDONLY] - path: /proc/⋯/net/core/somaxconn - - flags: [O_LARGEFILE, O_RDONLY] - path: /proc/⋯/stat - - flags: [O_CLOEXEC, O_DIRECTORY, O_RDONLY] - path: /proc/⋯/task/1/fd - - flags: [O_CLOEXEC, O_LARGEFILE, O_RDONLY] - path: /proc/⋯/vm/overcommit_memory - - flags: [O_CLOEXEC, O_LARGEFILE, O_NONBLOCK, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/token - - flags: [O_LARGEFILE, O_RDONLY] - path: /sys/devices/system/clocksource/clocksource0/current_clocksource - - flags: [O_LARGEFILE, O_RDONLY] - path: /sys/kernel/mm/transparent_hugepage/enabled - - flags: [O_LARGEFILE, O_RDONLY] - path: /trol.apiserver.k8s.io/v1/serverresources.json - - flags: [O_CLOEXEC, O_LARGEFILE, O_RDONLY] - path: /usr/local/bin/docker-entrypoint.sh + - path: /data/* + flags: [O_LARGEFILE, O_RDONLY] + - path: /dev/urandom + flags: [O_LARGEFILE, O_RDONLY] + - path: /etc/ld-musl-x86_64.path + flags: [O_CLOEXEC, O_LARGEFILE, O_RDONLY] + - path: /etc/localtime + flags: [O_CLOEXEC, O_LARGEFILE, O_NONBLOCK, O_RDONLY] + - path: /etc/passwd + flags: [O_CLOEXEC, O_LARGEFILE, O_RDONLY] + - path: /etc/ssl/openssl.cnf + flags: [O_LARGEFILE, O_RDONLY] + - path: /lib/libcrypto.so.3 + flags: [O_CLOEXEC, O_LARGEFILE, O_RDONLY] + - path: /lib/libssl.so.3 + flags: [O_CLOEXEC, O_LARGEFILE, O_RDONLY] + - path: /proc/⋯/net/core/somaxconn + flags: [O_LARGEFILE, O_RDONLY] + - path: /proc/⋯/stat + flags: [O_LARGEFILE, O_RDONLY] + - path: /proc/⋯/task/⋯/fd + flags: [O_CLOEXEC, O_DIRECTORY, O_RDONLY] + - path: /proc/⋯/vm/overcommit_memory + flags: [O_CLOEXEC, O_LARGEFILE, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/* + flags: [O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/token + flags: [O_RDONLY] + - path: /sys/devices/system/clocksource/clocksource0/current_clocksource + flags: [O_LARGEFILE, O_RDONLY] + - path: /sys/kernel/mm/transparent_hugepage/enabled + flags: [O_LARGEFILE, O_RDONLY] + - path: /usr/local/bin/docker-entrypoint.sh + flags: [O_CLOEXEC, O_LARGEFILE, O_RDONLY] capabilities: [CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_NET_ADMIN, CAP_SETGID, CAP_SETUID] - endpoints: [] rulePolicies: R0002: processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] R0004: processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] - matchLabels: {app.kubernetes.io/name: argocd-redis} + endpoints: null ingress: - - identifier: 75c91e0bdb3e8b695c7f2184c070f716e3e511035fa875b25184bd360ca13706 - namespaceSelector: null - podSelector: - matchLabels: {app.kubernetes.io/name: argocd-repo-server} - ports: - - {name: TCP-6379, port: 6379, protocol: TCP} + - identifier: argocd-components type: internal - - identifier: 387f17282c7b5a9d037c85b282e5ada9eb9a9e352fbfe81710ce4f07fc9a96be - namespaceSelector: null - podSelector: - matchLabels: {app.kubernetes.io/name: argocd-server} + namespaceSelector: + matchLabels: {kubernetes.io/metadata.name: argocd} ports: - {name: TCP-6379, port: 6379, protocol: TCP} - type: internal - - identifier: d93b79fbec160a0515c1632b70a626152653f142c3a311111243c31b0b0d1ce7 - namespaceSelector: null - podSelector: - matchLabels: {app.kubernetes.io/name: argocd-application-controller, apps.kubernetes.io/pod-index: '0', statefulset.kubernetes.io/pod-name: argocd-application-controller-0} - ports: - - {name: TCP-6379, port: 6379, protocol: TCP} - type: internal + ipAddresses: [10.42.0.0/16, 10.244.0.0/16] + egress: null diff --git a/example/argocd/sbobs/cp-argocd-repo-server.yaml b/example/argocd/sbobs/cp-argocd-repo-server.yaml index 7cfb03dd..2c26c1a5 100644 --- a/example/argocd/sbobs/cp-argocd-repo-server.yaml +++ b/example/argocd/sbobs/cp-argocd-repo-server.yaml @@ -6,205 +6,334 @@ metadata: annotations: {kubescape.io/managed-by: User} spec: architectures: [amd64] + matchLabels: {app.kubernetes.io/name: argocd-repo-server} execs: - path: /usr/bin/dash - args: [/usr/local/bin/gpg-wrapper.sh, ⋯⋯] + args: [/usr/local/bin/gpg-wrapper.sh, --no-permission-warning, --list-secret-keys, 3ADA3B1F32E4163D] + - path: /usr/bin/git + args: [/usr/bin/git, clean, -ffdx] + - path: /usr/bin/git + args: [/usr/bin/git, rev-parse, HEAD] + - path: /usr/bin/git + args: [/usr/bin/git, checkout, --force, 8088f4c0d970abb09e250248cc97e35623447cb5] - path: /usr/bin/git - args: [/usr/bin/git, ⋯⋯] + args: [/usr/bin/git, fetch, origin, --tags, --force, --prune] - path: /usr/bin/gpg - args: [/usr/bin/gpg, ⋯⋯] + args: [/usr/bin/gpg, --no-permission-warning, --list-public-keys] + - path: /usr/bin/gpg + args: [/usr/bin/gpg, --no-permission-warning, --list-secret-keys, 3ADA3B1F32E4163D] + - path: /usr/bin/gpg + args: [/usr/bin/gpg, --no-permission-warning, -a, --export, 3ADA3B1F32E4163D] + - path: /usr/bin/gpg + args: [/usr/bin/gpg, --no-permission-warning, --logger-fd, '1', --batch, --gen-key, /tmp/gpg-key-recipe2773991082] - path: /usr/bin/gpg-agent - args: [/usr/bin/gpg-agent, ⋯⋯] + args: [/usr/bin/gpg-agent] - path: /usr/bin/tini - args: [/usr/bin/tini, ⋯⋯] + args: [/usr/bin/tini, --, /usr/local/bin/argocd-repo-server] + - path: /usr/lib/git-core/git + args: [/usr/lib/git-core/git, remote-https, origin, 'https://github.com/argoproj/argocd-example-apps.git'] - path: /usr/lib/git-core/git - args: [/usr/lib/git-core/git, ⋯⋯] + args: [/usr/lib/git-core/git, maintenance, run, --auto, --no-quiet] + - path: /usr/lib/git-core/git + args: [/usr/lib/git-core/git, index-pack, --stdin, --fix-thin, --keep=fetch-pack 27 on argocd-repo-server-775878bb66-b2mzh, '--pack_header=2,595'] + - path: /usr/lib/git-core/git + args: [/usr/lib/git-core/git, rev-list, --objects, --stdin, --not, --all, --quiet, --alternate-refs] - path: /usr/lib/git-core/git-remote-http - args: [/usr/lib/git-core/git-remote-https, ⋯⋯] + args: [/usr/lib/git-core/git-remote-https, origin, 'https://github.com/argoproj/argocd-example-apps.git'] - path: /usr/local/bin/argocd - args: [/usr/local/bin/argocd-repo-server, ⋯⋯] + args: [/usr/local/bin/argocd-repo-server] + - path: /usr/local/bin/helm + args: [/usr/local/bin/helm, dependency, build] + - path: /usr/local/bin/helm + args: [/usr/local/bin/helm, repo, add, 'https:--charts.helm.sh-stable', 'https://charts.helm.sh/stable'] + - path: /usr/local/bin/helm + args: [/usr/local/bin/helm, template, ., --name-template, bob-app-of-apps, --namespace, gitops-demo, --kube-version, '1.35', --api-versions, admissionregistration.k8s.io/v1, --api-versions, admissionregistration.k8s.io/v1/MutatingWebhookConfiguration, --api-versions, admissionregistration.k8s.io/v1/ValidatingAdmissionPolicy, --api-versions, admissionregistration.k8s.io/v1/ValidatingAdmissionPolicyBinding, --api-versions, admissionregistration.k8s.io/v1/ValidatingWebhookConfiguration, --api-versions] + - path: /usr/local/bin/helm + args: [/usr/local/bin/helm, template, ., --name-template, helm-dependency, --namespace, gitops-demo, --kube-version, '1.35', --api-versions, admissionregistration.k8s.io/v1, --api-versions, admissionregistration.k8s.io/v1/MutatingWebhookConfiguration, --api-versions, admissionregistration.k8s.io/v1/ValidatingAdmissionPolicy, --api-versions, admissionregistration.k8s.io/v1/ValidatingAdmissionPolicyBinding, --api-versions, admissionregistration.k8s.io/v1/ValidatingWebhookConfiguration, --api-versions] + - path: /usr/local/bin/helm + args: [/usr/local/bin/helm, template, ., --name-template, helm-guestbook, --namespace, gitops-demo, --kube-version, '1.35', --api-versions, admissionregistration.k8s.io/v1, --api-versions, admissionregistration.k8s.io/v1/MutatingWebhookConfiguration, --api-versions, admissionregistration.k8s.io/v1/ValidatingAdmissionPolicy, --api-versions, admissionregistration.k8s.io/v1/ValidatingAdmissionPolicyBinding, --api-versions, admissionregistration.k8s.io/v1/ValidatingWebhookConfiguration, --api-versions] + - path: /usr/local/bin/helm + args: [/usr/local/bin/helm, template, ., --name-template, helm-params, --namespace, gitops-demo, --kube-version, '1.35', --set, replicaCount=1, --api-versions, admissionregistration.k8s.io/v1, --api-versions, admissionregistration.k8s.io/v1/MutatingWebhookConfiguration, --api-versions, admissionregistration.k8s.io/v1/ValidatingAdmissionPolicy, --api-versions, admissionregistration.k8s.io/v1/ValidatingAdmissionPolicyBinding, --api-versions] - path: /usr/local/bin/helm - args: [/usr/local/bin/helm, ⋯⋯] + args: [/usr/local/bin/helm, template, ., --name-template, helm-params, --namespace, gitops-demo, --kube-version, '1.35', --set, replicaCount=2, --api-versions, admissionregistration.k8s.io/v1, --api-versions, admissionregistration.k8s.io/v1/MutatingWebhookConfiguration, --api-versions, admissionregistration.k8s.io/v1/ValidatingAdmissionPolicy, --api-versions, admissionregistration.k8s.io/v1/ValidatingAdmissionPolicyBinding, --api-versions] - path: /usr/local/bin/kustomize - args: [/usr/local/bin/kustomize, ⋯⋯] + args: [/usr/local/bin/kustomize, build, ⋯, --enable-alpha-plugins, --enable-exec] opens: - - path: /app/config/* - flags: [O_APPEND, O_CLOEXEC, O_CREAT, O_DIRECTORY, O_EXCL, O_NOFOLLOW, O_NONBLOCK, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY] - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/host.conf + - path: /app/config/gpg/keys/.argocd-generated + flags: [O_CLOEXEC, O_CREAT, O_TRUNC, O_WRONLY] + - path: /app/config/gpg/keys/gpg-agent.conf + flags: [O_RDONLY] + - path: /app/config/gpg/keys/gpg.conf + flags: [O_RDONLY] + - path: /app/config/gpg/keys/openpgp-revocs.d/⋯ + flags: [O_CREAT, O_TRUNC, O_WRONLY] + - path: /app/config/gpg/keys/private-keys-v1.d/⋯ + flags: [O_CREAT, O_EXCL, O_RDONLY, O_TRUNC, O_WRONLY] + - path: /app/config/gpg/keys/pubring.gpg + flags: [O_RDONLY] + - path: /app/config/gpg/keys/pubring.kbx + flags: [O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY] + - path: /app/config/gpg/keys/pubring.kbx.lock + flags: [O_RDONLY] + - path: /app/config/gpg/keys/pubring.kbx.tmp + flags: [O_CREAT, O_TRUNC, O_WRONLY] + - path: /app/config/gpg/keys/trustdb.gpg + flags: [O_CREAT, O_RDWR, O_TRUNC, O_WRONLY] + - path: /app/config/gpg/keys/trustdb.gpg.lock + flags: [O_RDONLY] + - path: /app/config/gpg/keys/⋯ + flags: [O_CREAT, O_EXCL, O_WRONLY] + - path: /app/config/gpg/source + flags: [O_CLOEXEC, O_RDONLY] + - path: /app/config/gpg/source/⋯ + flags: [O_CLOEXEC, O_RDONLY] + - path: /app/config/tls/charts.helm.sh + flags: [O_CLOEXEC, O_RDONLY] + - path: /app/config/tls/github.com + flags: [O_CLOEXEC, O_RDONLY] + - path: /dev/null + flags: [O_CLOEXEC, O_RDONLY, O_RDWR, O_WRONLY] + - path: /dev/null/.config/git/attributes + flags: [O_RDONLY] + - path: /dev/null/.netrc + flags: [O_RDONLY] + - path: /dev/urandom + flags: [O_RDONLY] + - path: /etc/apache/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/apache2/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/gcrypt/hwf.deny + flags: [O_RDONLY] + - path: /etc/gcrypt/random.conf + flags: [O_RDONLY] + - path: /etc/gitattributes + flags: [O_RDONLY] + - path: /etc/gitconfig + flags: [O_RDONLY] + - path: /etc/gnupg/gpg-agent.conf + flags: [O_RDONLY] + - path: /etc/gnupg/gpg.conf + flags: [O_RDONLY] + - path: /etc/host.conf + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/hosts + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/httpd/conf/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/ld.so.cache + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/mailname + flags: [O_RDONLY] + - path: /etc/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/nsswitch.conf + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/passwd + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/pki/tls/certs + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/resolv.conf + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/ssl/certs + flags: [O_CLOEXEC, O_DIRECTORY, O_NONBLOCK, O_RDONLY] + - path: /etc/ssl/certs/ca-certificates.crt + flags: [O_CLOEXEC, O_RDONLY] - path: /helm-working-dir/* + flags: [O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY] + - path: /home/argocd + flags: [O_CLOEXEC, O_DIRECTORY, O_NONBLOCK, O_RDONLY] + - path: /home/argocd/.docker/config.json + flags: [O_CLOEXEC, O_RDONLY] + - path: /home/argocd/.kube/config + flags: [O_CLOEXEC, O_RDONLY] + - path: /home/argocd/cmp-server/plugins + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯ + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/fd + flags: [O_CLOEXEC, O_DIRECTORY, O_NONBLOCK, O_RDONLY] + - path: /proc/⋯/limits + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/net/core/somaxconn + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/stat + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/task/⋯/fd + flags: [O_CLOEXEC, O_DIRECTORY, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/* + flags: [O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/token + flags: [O_RDONLY] + - path: /sys/devices/system/cpu/online + flags: [O_CLOEXEC, O_RDONLY] + - path: /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + flags: [O_RDONLY] + - path: /system/etc/security/cacerts + flags: [O_RDONLY, O_RDWR, O_CREAT] + - path: /system/etc/security/cacerts/* + flags: [O_RDONLY, O_RDWR, O_CREAT] + - path: /tmp + flags: [O_CLOEXEC, O_RDONLY] + - path: /tmp/_argocd-repo/* flags: [O_APPEND, O_CLOEXEC, O_CREAT, O_DIRECTORY, O_EXCL, O_NOFOLLOW, O_NONBLOCK, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY] - - path: /tmp/* - flags: [O_APPEND, O_CLOEXEC, O_CREAT, O_DIRECTORY, O_EXCL, O_NOFOLLOW, O_NONBLOCK, O_RDONLY, O_RDWR, O_TRUNC, O_WRONLY] - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libassuan.so.0.8.5 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libbrotlicommon.so.1.0.9 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libbrotlidec.so.1.0.9 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libbz2.so.1.0.4 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libc.so.6 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libcom_err.so.2.1 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libcrypto.so.3 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libcurl-gnutls.so.4.7.0 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libffi.so.8.1.0 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libgcrypt.so.20.3.4 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libgmp.so.10.4.1 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libgnutls.so.30.31.0 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libgpg-error.so.0.32.1 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libgssapi_krb5.so.2.2 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libhogweed.so.6.4 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libidn2.so.0.3.7 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libk5crypto.so.3.1 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libkeyutils.so.1.9 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libkrb5.so.3.3 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libkrb5support.so.0.1 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/liblber-2.5.so.0.1.11 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libldap-2.5.so.0.1.11 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libm.so.6 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libnettle.so.8.4 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libnghttp2.so.14.20.1 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libnpth.so.0.1.2 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libp11-kit.so.0.3.0 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libpcre2-8.so.0.10.4 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libpsl.so.5.3.2 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libresolv.so.2 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/librtmp.so.1 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libsasl2.so.2.0.25 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libsqlite3.so.0.8.6 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libssh.so.4.8.7 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libtasn1.so.6.6.2 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libtinfo.so.6.3 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libunistring.so.2.2.0 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libz.so.1.2.11 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libzstd.so.1.4.8 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/local/bin/argocd - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/local/bin/helm - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/local/bin/kustomize - - path: /usr/share/ca-certificates/mozilla/⋯ - flags: [O_CLOEXEC, O_RDONLY] - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/share/zoneinfo/Etc/UTC + - path: /tmp/gpg-key-recipe2773991082 + flags: [O_CLOEXEC, O_CREAT, O_EXCL, O_RDONLY, O_RDWR] + - path: /tmp/helm1535808063/config/repositories.yaml + flags: [O_CLOEXEC, O_RDONLY] + - path: /tmp/helm1654610576/config/repositories.yaml + flags: [O_CLOEXEC, O_RDONLY] + - path: /tmp/helm1763221834 + flags: [O_CLOEXEC, O_RDONLY] + - path: /tmp/helm1763221834/config + flags: [O_CLOEXEC, O_RDONLY] + - path: /tmp/helm1763221834/config/repositories.lock + flags: [O_CLOEXEC, O_CREAT, O_RDONLY] + - path: /tmp/helm1763221834/config/repositories.yaml + flags: [O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY] + - path: /tmp/helm231796979/config/repositories.yaml + flags: [O_CLOEXEC, O_RDONLY] + - path: /tmp/helm2895966378/config/repositories.yaml + flags: [O_CLOEXEC, O_RDONLY] + - path: /tmp/helm3027478267/config/repositories.yaml + flags: [O_CLOEXEC, O_RDONLY] + - path: /tmp/helm455056191/config/repositories.yaml + flags: [O_CLOEXEC, O_RDONLY] + - path: /tmp/helm520669351/config/repositories.yaml + flags: [O_CLOEXEC, O_RDONLY] + - path: /tmp/helm562088449/config/repositories.yaml + flags: [O_CLOEXEC, O_RDONLY] + - path: /tmp/helm868611370 + flags: [O_CLOEXEC, O_RDONLY] + - path: /tmp/helm868611370/config + flags: [O_CLOEXEC, O_RDONLY] + - path: /tmp/helm868611370/config/repositories.lock + flags: [O_CLOEXEC, O_CREAT, O_RDONLY] + - path: /tmp/helm868611370/config/repositories.yaml + flags: [O_CLOEXEC, O_CREAT, O_RDONLY, O_TRUNC, O_WRONLY] + - path: /tmp/helm⋯ + flags: [O_RDONLY, O_RDWR, O_CREAT] + - path: /tmp/helm⋯/* + flags: [O_RDONLY, O_RDWR, O_CREAT] + - path: /tmp/helm⋯/⋯/* + flags: [O_RDONLY, O_RDWR, O_CREAT] + - path: /tmp/⋯/config/* + flags: [O_RDONLY, O_RDWR, O_CREAT] + - path: /tmp/⋯/⋯/* + flags: [O_RDONLY, O_RDWR, O_CREAT] + - path: /usr/lib/x86_64-linux-gnu/* + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/local/bin/argocd + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/local/bin/gpg-wrapper.sh + flags: [O_RDONLY] + - path: /usr/local/bin/helm + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/local/bin/kustomize + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/local/share/mime/globs2 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/share/ca-certificates/mozilla/* + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/share/mime/globs2 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/share/zoneinfo/Etc/UTC + flags: [O_CLOEXEC, O_RDONLY] capabilities: [CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_NET_ADMIN] + rulePolicies: + R0002: + processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] + R0004: + processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] + R0006: + processAllowed: [argocd-repo-ser] endpoints: - direction: inbound endpoint: :8084/healthz headers: Connection: [close] - Host: ['*:8084'] + Host: ['10.42.0.192:8084'] internal: true methods: [GET] - direction: inbound endpoint: :6443/metrics headers: Accept-Encoding: [gzip] - Host: ['*:6443'] + Host: ['127.0.0.1:6443'] internal: true methods: [GET] - rulePolicies: - R0002: - processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] - R0004: - processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] - matchLabels: {app.kubernetes.io/name: argocd-repo-server} ingress: - - identifier: 0a52270744d47da0e770b43cf46a02db90adfdaed097b34c8eacf9c267793a14 - namespaceSelector: null - podSelector: null + - identifier: kubelet-probes + type: internal + entity: host ports: - {name: TCP-8084, port: 8084, protocol: TCP} - type: external - ipAddresses: [10.42.0.0/16, 10.244.0.0/16] - - identifier: d93b79fbec160a0515c1632b70a626152653f142c3a311111243c31b0b0d1ce7 - namespaceSelector: null - podSelector: - matchLabels: {app.kubernetes.io/name: argocd-application-controller, apps.kubernetes.io/pod-index: '0', statefulset.kubernetes.io/pod-name: argocd-application-controller-0} - ports: - - {name: TCP-8081, port: 8081, protocol: TCP} + - identifier: argocd-components type: internal - - identifier: 31d7c34d1bf8a60b5bbb32a25efd7bdd86ed364ef9618683438efd4d6a4b7962 - namespaceSelector: null - podSelector: - matchLabels: {app.kubernetes.io/name: argocd-applicationset-controller} + namespaceSelector: + matchLabels: {kubernetes.io/metadata.name: argocd} ports: - {name: TCP-8081, port: 8081, protocol: TCP} - type: internal + ipAddresses: [10.42.0.0/16, 10.244.0.0/16] egress: - - identifier: 760ff312ee1dc2fb28f74beb6f89fd37143fc11476e33af1541f9426b37945a2 + - dns: charts.helm.sh. + dnsNames: [charts.helm.sh.] + identifier: 241919dfab60476109181f40a9631fca0a3b120b6fbf60ee053dfb9a656ebaa3 + ipAddress: 185.199.110.153 namespaceSelector: null - podSelector: - matchLabels: {app.kubernetes.io/name: argocd-redis} + podSelector: null ports: - - {name: TCP-6379, port: 6379, protocol: TCP} - type: internal - - dnsNames: [charts.helm.sh] + - {name: TCP-443, port: 443, protocol: TCP} + type: external + - dns: charts.helm.sh. + dnsNames: [charts.helm.sh.] + identifier: 416834682cb3faa275475bb568d44e33d16fee516f3c6e74743060eb584db5a2 + ipAddress: 185.199.109.153 + namespaceSelector: null + podSelector: null + ports: + - {name: TCP-443, port: 443, protocol: TCP} + type: external + - dns: charts.helm.sh. + dnsNames: [charts.helm.sh.] identifier: e14947516f312d9c20b5aaedac27e8e861f3c87f28ac60c43b207d1eac70e1ff + ipAddress: 185.199.108.153 namespaceSelector: null podSelector: null ports: - {name: TCP-443, port: 443, protocol: TCP} type: external - - dnsNames: [charts.helm.sh] + - dns: charts.helm.sh. + dnsNames: [charts.helm.sh.] identifier: 71fd766bcc234a020a04683c509a8a663e44601363bddceda35429bfd6942a76 + ipAddress: 185.199.111.153 namespaceSelector: null podSelector: null ports: - {name: TCP-443, port: 443, protocol: TCP} type: external - - dnsNames: [charts.helm.sh] - identifier: 416834682cb3faa275475bb568d44e33d16fee516f3c6e74743060eb584db5a2 + - dns: github.com. + dnsNames: [github.com.] + identifier: b5b004e9170d84c35c473466761979f8805df92aa0becb5f5d8a13886292cf94 + ipAddress: 140.82.121.4 namespaceSelector: null podSelector: null ports: - {name: TCP-443, port: 443, protocol: TCP} type: external - - dnsNames: [charts.helm.sh] - identifier: 241919dfab60476109181f40a9631fca0a3b120b6fbf60ee053dfb9a656ebaa3 + - dns: github.com. + dnsNames: [github.com.] + identifier: 1e2c3b8253ef65bbb38bb96e642bd3c6d40827f27a1f6c41435805be35b74c77 + ipAddress: 140.82.121.3 namespaceSelector: null podSelector: null ports: - {name: TCP-443, port: 443, protocol: TCP} type: external - - identifier: e5e8ca3d76f701a19b7478fdc1c8c24ccc6cef9902b52c8c7e015439e2a1ddf3 + - dns: '' + dnsNames: null + identifier: e5e8ca3d76f701a19b7478fdc1c8c24ccc6cef9902b52c8c7e015439e2a1ddf3 + ipAddress: '' namespaceSelector: matchLabels: {kubernetes.io/metadata.name: kube-system} podSelector: @@ -212,17 +341,13 @@ spec: ports: - {name: UDP-53, port: 53, protocol: UDP} type: internal - - dnsNames: [github.com] - identifier: 1e2c3b8253ef65bbb38bb96e642bd3c6d40827f27a1f6c41435805be35b74c77 - namespaceSelector: null - podSelector: null - ports: - - {name: TCP-443, port: 443, protocol: TCP} - type: external - - dnsNames: [github.com] - identifier: b5b004e9170d84c35c473466761979f8805df92aa0becb5f5d8a13886292cf94 + - dns: '' + dnsNames: null + identifier: 760ff312ee1dc2fb28f74beb6f89fd37143fc11476e33af1541f9426b37945a2 + ipAddress: '' namespaceSelector: null - podSelector: null + podSelector: + matchLabels: {app.kubernetes.io/name: argocd-redis} ports: - - {name: TCP-443, port: 443, protocol: TCP} - type: external + - {name: TCP-6379, port: 6379, protocol: TCP} + type: internal diff --git a/example/argocd/sbobs/cp-argocd-server.yaml b/example/argocd/sbobs/cp-argocd-server.yaml index ed2b42f6..6be57b04 100644 --- a/example/argocd/sbobs/cp-argocd-server.yaml +++ b/example/argocd/sbobs/cp-argocd-server.yaml @@ -6,74 +6,84 @@ metadata: annotations: {kubescape.io/managed-by: User} spec: architectures: [amd64] + matchLabels: {app.kubernetes.io/name: argocd-server} execs: - path: /usr/bin/tini - args: [/usr/bin/tini, ⋯⋯] + args: [/usr/bin/tini, --, /usr/local/bin/argocd-server] - path: /usr/local/bin/argocd - args: [/usr/local/bin/argocd-server, ⋯⋯] + args: [/usr/local/bin/argocd-server] opens: - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/apache/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/apache2/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/hosts - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/httpd/conf/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/ld.so.cache - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/mime.types - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/nsswitch.conf - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/passwd - - flags: [O_CLOEXEC, O_RDONLY] - path: /etc/resolv.conf - - flags: [O_CLOEXEC, O_RDONLY] - path: /home/argocd/.kube/config - - flags: [O_CLOEXEC, O_RDONLY] - path: /proc/⋯/net/core/somaxconn - - flags: [O_CLOEXEC, O_DIRECTORY, O_RDONLY] - path: /proc/⋯/task/1/fd - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/ca.crt - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/namespace - - flags: [O_CLOEXEC, O_RDONLY] - path: /run/secrets/kubernetes.io/serviceaccount/⋯/token - - flags: [O_RDONLY] - path: /sys/kernel/mm/transparent_hugepage/hpage_pmd_size - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/lib/x86_64-linux-gnu/libc.so.6 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/local/bin/argocd - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/local/share/mime/globs2 - - flags: [O_CLOEXEC, O_RDONLY] - path: /usr/share/mime/globs2 - - flags: [O_RDONLY] - path: /usr/share/zoneinfo/Etc/UTC + - path: /etc/apache/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/apache2/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/hosts + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/httpd/conf/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/ld.so.cache + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/mime.types + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/nsswitch.conf + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/passwd + flags: [O_CLOEXEC, O_RDONLY] + - path: /etc/resolv.conf + flags: [O_CLOEXEC, O_RDONLY] + - path: /home/argocd/.kube/config + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/net/core/somaxconn + flags: [O_CLOEXEC, O_RDONLY] + - path: /proc/⋯/task/⋯/fd + flags: [O_CLOEXEC, O_DIRECTORY, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/* + flags: [O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/ca.crt + flags: [O_CLOEXEC, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/namespace + flags: [O_CLOEXEC, O_RDONLY] + - path: /run/secrets/kubernetes.io/serviceaccount/⋯/token + flags: [O_CLOEXEC, O_RDONLY] + - path: /sys/kernel/mm/transparent_hugepage/hpage_pmd_size + flags: [O_RDONLY] + - path: /usr/lib/x86_64-linux-gnu/libc.so.6 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/local/bin/argocd + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/local/share/mime/globs2 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/share/mime/globs2 + flags: [O_CLOEXEC, O_RDONLY] + - path: /usr/share/zoneinfo/Etc/UTC + flags: [O_RDONLY] capabilities: [CAP_DAC_OVERRIDE, CAP_DAC_READ_SEARCH, CAP_NET_ADMIN] - endpoints: [] rulePolicies: R0002: processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] R0004: processAllowed: ['runc:[1:INIT]', 'runc:[1:CHILD]', 'runc:[2:INIT]', 'runc:[3:INIT]'] R0006: - processAllowed: [tini, argocd-server] - matchLabels: {app.kubernetes.io/name: argocd-server} + processAllowed: [argocd-server] + endpoints: null ingress: - - identifier: 0a52270744d47da0e770b43cf46a02db90adfdaed097b34c8eacf9c267793a14 - namespaceSelector: null - podSelector: null + - identifier: kubelet-probes + type: internal + entity: host + ports: + - {name: TCP-8080, port: 8080, protocol: TCP} + - identifier: argocd-components + type: internal + namespaceSelector: + matchLabels: {kubernetes.io/metadata.name: argocd} ports: - {name: TCP-8080, port: 8080, protocol: TCP} - type: external ipAddresses: [10.42.0.0/16, 10.244.0.0/16] egress: - - identifier: dd18fa52244005a49761004e6173ec450c0779087cedb1eaf1ae49142a6550a2 + - dns: '' + dnsNames: null + identifier: dd18fa52244005a49761004e6173ec450c0779087cedb1eaf1ae49142a6550a2 + ipAddress: 10.43.0.1 namespaceSelector: matchLabels: {kubernetes.io/metadata.name: default} podSelector: @@ -81,15 +91,10 @@ spec: ports: - {name: TCP-443, port: 443, protocol: TCP} type: internal - ipAddresses: [10.43.0.0/16, 10.96.0.0/12] - - identifier: 760ff312ee1dc2fb28f74beb6f89fd37143fc11476e33af1541f9426b37945a2 - namespaceSelector: null - podSelector: - matchLabels: {app.kubernetes.io/name: argocd-redis} - ports: - - {name: TCP-6379, port: 6379, protocol: TCP} - type: internal - - identifier: e5e8ca3d76f701a19b7478fdc1c8c24ccc6cef9902b52c8c7e015439e2a1ddf3 + - dns: '' + dnsNames: null + identifier: e5e8ca3d76f701a19b7478fdc1c8c24ccc6cef9902b52c8c7e015439e2a1ddf3 + ipAddress: '' namespaceSelector: matchLabels: {kubernetes.io/metadata.name: kube-system} podSelector: @@ -97,7 +102,20 @@ spec: ports: - {name: UDP-53, port: 53, protocol: UDP} type: internal - - identifier: 75c91e0bdb3e8b695c7f2184c070f716e3e511035fa875b25184bd360ca13706 + - dns: '' + dnsNames: null + identifier: 760ff312ee1dc2fb28f74beb6f89fd37143fc11476e33af1541f9426b37945a2 + ipAddress: '' + namespaceSelector: null + podSelector: + matchLabels: {app.kubernetes.io/name: argocd-redis} + ports: + - {name: TCP-6379, port: 6379, protocol: TCP} + type: internal + - dns: '' + dnsNames: null + identifier: 75c91e0bdb3e8b695c7f2184c070f716e3e511035fa875b25184bd360ca13706 + ipAddress: '' namespaceSelector: null podSelector: matchLabels: {app.kubernetes.io/name: argocd-repo-server} diff --git a/kubescape/default-rule-binding.yaml b/kubescape/default-rule-binding.yaml index be6028f6..36bf465b 100644 --- a/kubescape/default-rule-binding.yaml +++ b/kubescape/default-rule-binding.yaml @@ -39,6 +39,7 @@ spec: - ruleName: "Exec to pod" - ruleName: "Port forward" - ruleName: "Unexpected Egress Network Traffic" + - ruleName: "Unexpected Ingress Network Traffic" - ruleName: "Malicious Ptrace Usage" - ruleName: "Unexpected io_uring Operation Detected" - ruleName: "Kubelet TLS Exec Request Detected" diff --git a/kubescape/default-rules.yaml b/kubescape/default-rules.yaml index eba22c33..2f313e42 100644 --- a/kubescape/default-rules.yaml +++ b/kubescape/default-rules.yaml @@ -55,7 +55,7 @@ spec: mitreTechnique: T1059 name: Unexpected process arguments profileDependency: 0 - severity: 1 + severity: 3 supportPolicy: false tags: - anomaly @@ -154,7 +154,7 @@ spec: mitreTechnique: T1071.004 name: DNS Anomalies in container profileDependency: 0 - severity: 1 + severity: 5 supportPolicy: false tags: - dns @@ -211,8 +211,8 @@ spec: event.dstAddr id: R0007 isTriggerAlert: false - mitreTactic: TA0008 - mitreTechnique: T1210 + mitreTactic: TA0007 + mitreTechnique: T1613 name: Workload uses Kubernetes API unexpectedly profileDependency: 0 severity: 5 @@ -262,8 +262,8 @@ spec: uniqueId: event.comm + '_' + 'bpf' + '_' + string(event.cmd) id: R0009 isTriggerAlert: true - mitreTactic: TA0005 - mitreTechnique: T1218 + mitreTactic: TA0002 + mitreTechnique: T1106 name: eBPF Program Load profileDependency: 1 severity: 5 @@ -308,8 +308,11 @@ spec: ruleExpression: - eventType: network expression: >- - event.pktType == 'OUTGOING' && !net.is_private_ip(event.dstAddr) - && !cp.was_address_in_egress(event.containerId, event.dstAddr) + event.pktType == 'OUTGOING' + && !event.dstAddr.startsWith('127.') + && event.dstAddr != '::1' + && !cp.was_address_port_protocol_in_egress(event.containerId, event.dstAddr, event.dstPort, event.proto) + && !cp.was_selector_in_egress(event.containerId, event.dstNamespace, event.dstPodLabels) uniqueId: event.dstAddr + '_' + string(event.dstPort) + '_' + event.proto id: R0011 isTriggerAlert: true @@ -317,7 +320,38 @@ spec: mitreTechnique: T1041 name: Unexpected Egress Network Traffic profileDependency: 0 - severity: 5 + severity: 8 + supportPolicy: false + tags: + - whitelisted + - network + - anomaly + - networkprofile + - description: >- + Detecting unexpected ingress network traffic — an inbound connection whose + peer is not whitelisted by the application profile ingress. + enabled: true + expressions: + message: >- + 'Unexpected ingress network communication from: ' + event.dstAddr + ':' + + string(event.dstPort) + ' using ' + event.proto + ' to: ' + + event.containerName + ruleExpression: + - eventType: network + expression: >- + event.pktType == 'HOST' + && !event.dstAddr.startsWith('127.') + && event.dstAddr != '::1' + && !cp.was_address_port_protocol_in_ingress(event.containerId, event.dstAddr, event.dstPort, event.proto) + && !cp.was_selector_in_ingress(event.containerId, event.dstNamespace, event.dstPodLabels) + uniqueId: event.dstAddr + '_' + string(event.dstPort) + '_' + event.proto + id: R0012 + isTriggerAlert: true + mitreTactic: TA0010 + mitreTechnique: T1041 + name: Unexpected Ingress Network Traffic + profileDependency: 0 + severity: 8 supportPolicy: false tags: - whitelisted @@ -394,7 +428,7 @@ spec: id: R1002 isTriggerAlert: true mitreTactic: TA0005 - mitreTechnique: T1547.006 + mitreTechnique: T1014 name: Process tries to load a kernel module profileDependency: 2 severity: 10 @@ -423,7 +457,7 @@ spec: mitreTechnique: T1021.001 name: Disallowed ssh connection profileDependency: 1 - severity: 5 + severity: 8 supportPolicy: false tags: - ssh @@ -472,7 +506,7 @@ spec: id: R1005 isTriggerAlert: true mitreTactic: TA0005 - mitreTechnique: T1055 + mitreTechnique: T1620 name: Fileless execution detected profileDependency: 2 severity: 8 @@ -520,7 +554,7 @@ spec: id: R1007 isTriggerAlert: true mitreTactic: TA0040 - mitreTechnique: T1496 + mitreTechnique: T1496.001 name: Crypto miner launched profileDependency: 2 severity: 10 @@ -613,11 +647,11 @@ spec: uniqueId: event.comm + '_' + string(event.dstPort) id: R1009 isTriggerAlert: false - mitreTactic: TA0011 - mitreTechnique: T1071 + mitreTactic: TA0040 + mitreTechnique: T1496.001 name: Crypto Mining Related Port Communication profileDependency: 1 - severity: 3 + severity: 8 supportPolicy: false tags: - network @@ -716,7 +750,7 @@ spec: id: R1015 isTriggerAlert: true mitreTactic: TA0005 - mitreTechnique: T1622 + mitreTechnique: T1055.008 name: Malicious Ptrace Usage profileDependency: 2 severity: 5 @@ -739,8 +773,8 @@ spec: uniqueId: string(event.opcode) + '_' + event.comm id: R1030 isTriggerAlert: true - mitreTactic: TA0002 - mitreTechnique: T1218 + mitreTactic: TA0005 + mitreTechnique: T1014 name: Unexpected io_uring Operation Detected profileDependency: 0 severity: 5 @@ -788,7 +822,7 @@ spec: id: R1016 isTriggerAlert: false mitreTactic: TA0005 - mitreTechnique: T1565 + mitreTechnique: T1562 name: Signed profile tampered profileDependency: 2 severity: 10 diff --git a/kubescape/post-render.sh b/kubescape/post-render.sh index 3bd4d0f3..3cc4b450 100755 --- a/kubescape/post-render.sh +++ b/kubescape/post-render.sh @@ -1,3 +1,4 @@ +#!/usr/bin/env bash set -euo pipefail python3 -c ' diff --git a/kubescape/rule-coverage.yaml b/kubescape/rule-coverage.yaml index 5cd2553a..a4a948c9 100644 --- a/kubescape/rule-coverage.yaml +++ b/kubescape/rule-coverage.yaml @@ -96,3 +96,36 @@ apps: # loads. R1001: this node-agent build emits zero R1001 cluster-wide — the # drifted exec trips R1000 instead, which IS asserted. probe: [R0009, R1001] + + # Redis-protocol distros — same attack surface bound to each vendor SBoB, so + # the four cards show the SAME coverage: contrast is a property of the SBoB, not + # the distro. R0002/R0004 fire as verified-but-unasserted (runc + attack side + # effects). Live-verified with each SBoB bound at deploy (./deploy-distros.sh sbob). + - name: redis-oss + title: redis (bitnami) + out: example/redis/distros/rule-coverage/redis-oss.gif + suites: [example/redis/distros/attacks/redis-oss.yaml] + observed: [R0002, R0004] + probe: [R0009, R0040, R1001, R1002, R1003, R1006, R1011, R1015, R2000] + note: bitnami/redis native install, SBoB bound at deploy + - name: valkey + title: valkey (bitnami) + out: example/redis/distros/rule-coverage/valkey.gif + suites: [example/redis/distros/attacks/valkey.yaml] + observed: [R0002, R0004] + probe: [R0009, R0040, R1001, R1002, R1003, R1006, R1011, R1015, R2000] + note: bitnami/valkey native install, SBoB bound at deploy + - name: keydb + title: keydb (enapter) + out: example/redis/distros/rule-coverage/keydb.gif + suites: [example/redis/distros/attacks/keydb.yaml] + observed: [R0002, R0004] + probe: [R0009, R0040, R1001, R1002, R1003, R1006, R1011, R1015, R2000] + note: enapter/keydb native install, SBoB bound at deploy + - name: dragonfly + title: dragonfly (operator) + out: example/redis/distros/rule-coverage/dragonfly.gif + suites: [example/redis/distros/attacks/dragonfly.yaml] + observed: [R0002, R0004] + probe: [R0009, R0040, R1001, R1002, R1003, R1006, R1011, R1015, R2000] + note: dragonfly-operator native install, SBoB bound at deploy diff --git a/kubescape/set-signature-verification.sh b/kubescape/set-signature-verification.sh new file mode 100755 index 00000000..4e849791 --- /dev/null +++ b/kubescape/set-signature-verification.sh @@ -0,0 +1,56 @@ +#!/usr/bin/env bash +# Toggle node-agent ContainerProfile signature verification. +# +# bobctl emits UNSIGNED user-defined ContainerProfiles (SBoBs). With signature +# verification ON, node-agent silently refuses to enforce them and falls back to +# learning mode — no detection, no error. This repo's demos rely on applying +# those SBoBs, so the default is OFF (production should instead `bobctl sign` and +# turn it ON). +# +# The upstream kubescape-operator chart does not template this config key, and +# --post-renderer is not helm-4 safe, so the value is applied to the node-agent +# ConfigMap after helm. node-agent reads config only at start, so a single roll +# is needed to pick it up; the roll is skipped when the value is already correct. +# +# Usage: set-signature-verification.sh (env: KS_NAMESPACE, default honey) +set -euo pipefail + +MODE="${1:-off}" +NS="${KS_NAMESPACE:-honey}" +CM="${KS_NODE_AGENT_CM:-node-agent}" + +case "$MODE" in + on) WANT=true ;; + off) WANT=false ;; + *) echo "usage: $(basename "$0") " >&2; exit 2 ;; +esac + +cur="$(kubectl -n "$NS" get configmap "$CM" -o jsonpath='{.data.config\.json}' 2>/dev/null || true)" +if [ -z "$cur" ]; then + echo "set-signature-verification: configmap $NS/$CM not found; skipping" >&2 + exit 0 +fi + +have="$(printf '%s' "$cur" | python3 -c 'import json,sys; print(str(json.load(sys.stdin).get("enableSignatureVerification", False)).lower())')" +if [ "$have" = "$WANT" ]; then + echo "set-signature-verification: already enableSignatureVerification=$WANT in $NS/$CM" + exit 0 +fi + +new="$(WANT="$WANT" python3 - "$cur" <<'PY' +import json, os, sys +d = json.loads(sys.argv[1]) +d["enableSignatureVerification"] = (os.environ["WANT"] == "true") +print(json.dumps(d)) +PY +)" + +kubectl -n "$NS" patch configmap "$CM" --type merge -p "$(python3 - "$new" <<'PY' +import json, sys +print(json.dumps({"data": {"config.json": sys.argv[1]}})) +PY +)" + +echo "set-signature-verification: enableSignatureVerification=$WANT in $NS/$CM ; rolling node-agent to apply" +kubectl -n "$NS" rollout restart daemonset/node-agent +kubectl -n "$NS" rollout status daemonset/node-agent --timeout=300s diff --git a/kubescape/values.yaml b/kubescape/values.yaml index 0872e267..d13f19db 100644 --- a/kubescape/values.yaml +++ b/kubescape/values.yaml @@ -1,11 +1,12 @@ storage: image: repository: ghcr.io/k8sstormcenter/storage - tag: sbob-rc5s + tag: net-v2s-f6fa47d6 nodeAgent: image: repository: ghcr.io/k8sstormcenter/node-agent - tag: sbob-rc5s-fpo + tag: net-v2s-f6fa47d6 + pullPolicy: Always config: alertManagerExporterUrls: - alertmanager.honey.svc.cluster.local:9093 @@ -19,11 +20,6 @@ nodeAgent: ruleCooldownMaxSize: 20000 capabilities: runtimeDetection: enable - # The SBoB demo is runtime-detection only. Disable every scanning capability - # the chart enables by default so those components are never installed — this - # is both a big install-time speedup (no kubescape config-scanner, no kubevuln, - # no host node-scanner, no SBOM generation, no schedulers) and removes work the - # demo does not use. Only storage + node-agent + the alert wiring remain. configurationScan: disable nodeScan: disable nodeSbomGeneration: disable diff --git a/pkg b/pkg index 2a2b3376..2eeb44f2 160000 --- a/pkg +++ b/pkg @@ -1 +1 @@ -Subproject commit 2a2b33766816ab5b96bf19f8b74e70c0fbbc32ff +Subproject commit 2eeb44f258d3bb84ed70959104625a9a973a72a4 diff --git a/scripts/clean-profile.py b/scripts/clean-profile.py index 3de22d8d..058b4b77 100755 --- a/scripts/clean-profile.py +++ b/scripts/clean-profile.py @@ -27,6 +27,34 @@ ) +# Mirrors declareRuncInitAllowed in pkg/autotune/tuner.go. The tuner writes this +# into the profile it emits, but local-ci rebuilds the shipped file from the +# per-iteration snapshot, which predates that step — so the last writer has to +# apply it too or the allowlist silently disappears from what actually ships. +RUNC_INIT_COMMS = ["runc:[1:INIT]", "runc:[1:CHILD]", "runc:[2:INIT]", "runc:[3:INIT]"] + + +def ensure_runc_init_allowed(spec): + if not isinstance(spec, dict): + return + pol = spec.get("rulePolicies") + if not isinstance(pol, dict): + pol = {} + spec["rulePolicies"] = pol + for rule_id in ("R0002", "R0004"): + entry = pol.get(rule_id) + if not isinstance(entry, dict): + entry = {} + pol[rule_id] = entry + allowed = entry.get("processAllowed") + if not isinstance(allowed, list): + allowed = [] + entry["processAllowed"] = allowed + for comm in RUNC_INIT_COMMS: + if comm not in allowed: + allowed.append(comm) + + def clean(p): p["apiVersion"] = "spdx.softwarecomposition.kubescape.io/v1beta1" # CP migration: the tuner emits ContainerProfiles (unified flat spec with @@ -55,6 +83,7 @@ def clean(p): m.pop("labels", None) p.pop("status", None) + ensure_runc_init_allowed(p.get("spec")) return {k: p[k] for k in ("apiVersion", "kind", "metadata", "spec") if k in p} diff --git a/scripts/generalise-sbob.py b/scripts/generalise-sbob.py new file mode 100755 index 00000000..2f0b100d --- /dev/null +++ b/scripts/generalise-sbob.py @@ -0,0 +1,629 @@ +#!/usr/bin/env python3 +"""Turn a learned/tuned ContainerProfile into a shippable SBoB. + +A learn describes one run on one cluster. Shipping it unchanged means shipping +every accident of that run: the OIDs postgres happened to allocate, the shm +segment it happened to map, the pod IP the kubelet happened to probe from. Each +of those is a guaranteed false positive everywhere else. + +Four passes, in this order: + + 1. drop head-truncated paths they cannot match at runtime + 2. volatile segments -> ellipsis one segment, so the rest stays literal + 3. merge + union flags lossless + 4. network -> the storage#42 schema + +The fourth is the new part. Before storage#42 the only way to admit a kubelet +health probe was to list the pod CIDR, which admits every pod on the node as a +side effect. `entity: host` names the node itself, and `serviceRef` names a +Service rather than whatever ClusterIP it had at learn time. +""" +import argparse +import glob +import json +import os +import re +import sys + +import yaml + + +class NoAliasDumper(yaml.SafeDumper): + def ignore_aliases(self, data): + return True + + +class FlowDict(dict): + """Serialises inline: {path: ..., flags: [...]} — one entry per line.""" + + +NoAliasDumper.add_representer( + FlowDict, + lambda d, x: d.represent_mapping("tag:yaml.org,2002:map", x, flow_style=True), +) + +ELLIPSIS = "⋯" +WILDCARDS = {"*", "**", ELLIPSIS, "⋯⋯"} + +# Real filesystem roots. Anything else at segment 0 is a head-truncated fragment +# from the node-agent bug: "/ation.k8s.io/v1/serverresources.json" is the tail of +# ".../discovery//.io/v1/serverresources.json" and matches nothing. +REAL_ROOTS = { + "bin", "boot", "data", "dev", "etc", "home", "lib", "lib32", "lib64", + "media", "mnt", "opt", "proc", "root", "run", "sbin", "srv", "sys", "tmp", + "usr", "var", + # image-specific roots seen in these workloads + "bitnami", "controller", "docker-entrypoint-initdb.d", "app", "base", + "global", "pg_wal", "pg_stat", "pg_stat_tmp", "helm-working-dir", +} + +VOLATILE = [ + re.compile(r"^\.\.\d{4}_\d{2}_\d{2}_\d{2}_\d{2}_\d{2}\.\d+$"), # projected volume + re.compile(r"^\.\.\d"), # other .. + re.compile(r"^\d+$"), # PIDs, postgres OIDs + re.compile(r"^[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}$", re.I), # UUID + re.compile(r"^[0-9a-f]{40}(\.[A-Za-z0-9.]+)?$", re.I), # git sha1 + re.compile(r"^[0-9a-f]{64}(\.[A-Za-z0-9.]+)?$", re.I), # sha256 + re.compile(r"^sha256[:-][0-9a-f]{64}$", re.I), + re.compile(r".*[-.]\d{6,}(\.[A-Za-z0-9]+)*$"), # foo-4064072570[.tmp] + # postgres relation files: [.][_fsm|_vm|_init] + re.compile(r"^\d+(\.\d+)?(_fsm|_vm|_init)?$"), + re.compile(r"^PostgreSQL\.\d+$"), # /dev/shm segment + re.compile(r"^pgsql_tmp\d+\.\d+$"), +] + +# A trailing "." is almost always a per-process or per-generation +# suffix: postgres rewrites pg_internal.init as pg_internal.init., so a +# learn captures .58 .62 .71 .72 .73 and every restart invents new ones. +# +# Shared-object sonames look identical (libc.so.6, libicudata.so.76.1) but are +# STABLE — they are the library ABI version, not a per-run value. Ellipsising +# them would be wrong and would also lose the library identity, so ".so." is +# excluded and those are handled by directory collapse instead. +NUMERIC_SUFFIX = re.compile(r"^.+\.\d+$") + + +def numeric_suffix_volatile(seg): + return bool(NUMERIC_SUFFIX.match(seg)) and ".so." not in seg + + +def is_truncated_root(path): + segs = [s for s in path.split("/") if s] + if not segs: + return True + head = segs[0] + if head.startswith("."): # /.docker/config.json with HOME=/ + return False + return head not in REAL_ROOTS + + +def volatile(seg): + return any(p.match(seg) for p in VOLATILE) or numeric_suffix_volatile(seg) + + +def normalise(path): + """Ellipsis every volatile segment EXCEPT the first. + + A wildcard in segment 0 matches /etc/shadow, which makes cp.was_path_opened() + true and silently disables R0010, R1010 and R1012. That is a worse bug than + the one being fixed, so the head is never rewritten. + """ + segs = path.split("/") + first = next((i for i, s in enumerate(segs) if s), None) + return "/".join( + s if i == first or not s else (ELLIPSIS if volatile(s) else s) + for i, s in enumerate(segs) + ) + + +def leading_wildcard(path): + segs = [s for s in path.split("/") if s] + return bool(segs) and segs[0] in WILDCARDS + + +def generalise_opens(opens, report): + kept, by_path, order = [], {}, [] + for o in opens: + p = o.get("path", "") + if is_truncated_root(p): + report["truncated"].append(p) + continue + np = normalise(p) + if np != p: + report["normalised"] += 1 + if leading_wildcard(np): + report["overbroad"].append(np) + continue + if np in by_path: + by_path[np]["flags"] = sorted(set(by_path[np].get("flags") or []) | set(o.get("flags") or [])) + else: + by_path[np] = {"path": np, "flags": sorted(set(o.get("flags") or []))} + order.append(np) + for p in sorted(order): + kept.append(FlowDict(by_path[p])) + return kept + + + +# ── directory collapse ────────────────────────────────────────────────────── +# +# A learn lists every file it happened to touch. 20 .mo catalogues under +# /usr/share/locale and 36 sonames under /usr/lib/x86_64-linux-gnu are not +# behaviour worth discriminating: they are read-only static image content, and +# an attacker reading one of them has achieved nothing. Collapsing them to +# /* keeps the profile legible and portable across image rebuilds, where +# the exact soname versions change. +# +# The danger is collapsing a directory that CAN hold something sensitive: baseline +# it and the rule that would have caught a read there goes blind. So collapse is +# allowed only under roots that are read-only static content, and never under the +# ones an attacker actually targets. +COLLAPSIBLE_ROOTS = ( + "/usr/share/", "/usr/lib/", "/lib/", "/usr/local/share/", "/usr/local/lib/", +) + +# Never collapse under these, whatever the sibling count. /etc holds shadow and +# passwd; the data directory holds the database itself; /run/secrets holds the +# SA token; procfs and sysfs are how container escapes are staged. +NEVER_COLLAPSE = ( + "/etc", "/root", "/home", "/run/secrets", "/proc", "/sys", "/dev", + "/var/lib/postgresql/data", "/bitnami/postgresql/data", "/var/lib/kubelet", +) + +READ_ONLY_FLAGS = {"O_RDONLY", "O_CLOEXEC", "O_NOFOLLOW", "O_DIRECTORY", "O_NONBLOCK"} + + +def collapsible(directory, entries, min_siblings): + """A directory is collapsible when it is static read-only image content. + + Three conditions, all required: + - under a root that holds image content rather than state + - not under any path an attacker would target + - every observed access is read-only; a single write means the directory + is state, not content, and collapsing it would baseline writes too + """ + if any(directory == n or directory.startswith(n + "/") for n in NEVER_COLLAPSE): + return False + if not any(directory.startswith(r) for r in COLLAPSIBLE_ROOTS): + return False + if len(entries) < min_siblings: + return False + return all(set(e.get("flags") or []) <= READ_ONLY_FLAGS for e in entries) + + +def collapse_directories(opens, min_siblings, report): + """Replace >= min_siblings read-only leaves in one directory with /*.""" + by_dir = {} + for o in opens: + by_dir.setdefault(os.path.dirname(o["path"]), []).append(o) + + out, collapsed_dirs = [], [] + for directory in sorted(by_dir): + entries = by_dir[directory] + if collapsible(directory, entries, min_siblings): + flags = sorted({f for e in entries for f in (e.get("flags") or [])}) + out.append(FlowDict({"path": directory + "/*", "flags": flags})) + collapsed_dirs.append((directory, len(entries))) + else: + out.extend(entries) + report["collapsed"] = collapsed_dirs + # Drop leaves now covered by a collapsed parent, and the bare directory entry. + prefixes = [d + "/" for d, _ in collapsed_dirs] + kept = [] + for o in out: + p = o["path"] + if p.endswith("/*"): + kept.append(o) + continue + if any(p.startswith(pre) for pre in prefixes) or any(p == d for d, _ in collapsed_dirs): + continue + kept.append(o) + return sorted(kept, key=lambda x: x["path"]) + + + +# ── exec arguments ────────────────────────────────────────────────────────── +# +# Collapsing every exec to [path, ⋯⋯] throws away all argument discrimination: +# a profile that legitimately runs `psql -c "SELECT 1"` then also permits +# `psql -c "COPY x TO PROGRAM 'sh'"`. It was also wrong about args[0] — dash +# really runs as /bin/sh, env as docker-entrypoint.sh, perl as psql via +# pg_wrapper — and matching is anchored, so that only went unnoticed because +# ⋯⋯ absorbed the mismatch. +# +# Binaries whose arguments can encode a command. Their args are never merged: +# any wildcard in a command position hands back exactly what the profile is +# supposed to constrain. +# The workload's OWN main executable. It takes many arguments, several of them +# node-dependent (postgres probes shared_buffers and emits 16384 here, 1000 +# there), so pinning them literally is a portability false positive waiting to +# happen. Its identity IS the discriminant: an attacker running the postgres +# binary with different flags has not achieved anything, whereas an attacker +# running bash has. So the core binary gets a genuine ⋯⋯ and the utilities do +# not — which is the opposite of treating them all alike. +CORE_BINARY_ARGS = ["⋯⋯"] + +NO_MERGE_BINARIES = { + "sh", "bash", "dash", "ash", "zsh", "ksh", "busybox", + "perl", "python", "python3", "ruby", "node", "env", "gosu", "su-exec", + "psql", "mysql", "mariadb", "redis-cli", "xargs", "nsenter", +} + +# A wildcard immediately after one of these is a command-injection hole. +COMMAND_FLAGS = {"-c", "-e", "--command", "-exec", "--eval", "-execdir"} + + +def normalise_arg(arg): + """Ellipsis volatile segments inside path-shaped arguments only. + + initdb is invoked with --pwfile=/dev/fd/63; the fd number is per-run. The + matcher compares ⋯ as a WHOLE path segment, so a partial-segment token would + never match at runtime — only whole segments are ever rewritten. + """ + if "/" not in arg: + return arg + head, sep, path = arg.partition("=") + if sep and path.startswith("/"): + return head + "=" + normalise(path) + if arg.startswith("/"): + return normalise(arg) + return arg + + +def merge_arg_vectors(vectors): + """Position-wise merge of same-arity vectors, or None if unsafe. + + Refuses when too much of the vector would become wildcard, or when a + wildcard would land right after a command-introducing flag. + """ + arity = len(vectors[0]) + merged, wildcarded = [], 0 + for i in range(arity): + values = {v[i] for v in vectors} + if len(values) == 1: + merged.append(vectors[0][i]) + continue + if i > 0 and merged[i - 1] in COMMAND_FLAGS: + return None + merged.append(ELLIPSIS) + wildcarded += 1 + if wildcarded > max(1, arity // 3): + return None + return merged + + +def generalise_execs(execs, report, core_binaries=()): + """Emit the narrowest arg patterns covering what was observed. + + Never merges across arities and never emits a trailing ⋯⋯: that catch-all + subsumes every narrower sibling, which is the same failure the opens side + guards against with the leading-wildcard check. + """ + by_path = {} + for e in execs: + args = [normalise_arg(a) for a in (e.get("args") or [])] + # Normalise the exec path the same way as its arguments. Leaving the path + # literal while args[0] carries ⋯ makes the two fields disagree about the + # same binary — the SBoB would be version-portable in one and pinned in + # the other. + path = normalise(e["path"]) + if not args: + args = [path] + by_path.setdefault(path, []).append(tuple(args)) + + core = set(core_binaries) + out = [] + for path in sorted(by_path): + vectors = sorted(set(by_path[path])) + binary = os.path.basename(path) + if path in core or binary in core: + argv0 = sorted({v[0] for v in vectors}) + for a0 in argv0: + out.append(FlowDict({"path": path, "args": [a0] + CORE_BINARY_ARGS})) + report["core"].append((path, len(vectors), len(argv0))) + continue + if binary in NO_MERGE_BINARIES or len(vectors) == 1: + for v in vectors: + out.append(FlowDict({"path": path, "args": list(v)})) + if binary in NO_MERGE_BINARIES and len(vectors) > 1: + report["literal_interpreters"].append((path, len(vectors))) + continue + by_arity = {} + for v in vectors: + by_arity.setdefault(len(v), []).append(v) + for arity in sorted(by_arity): + group = by_arity[arity] + merged = merge_arg_vectors(group) if len(group) > 1 else list(group[0]) + if merged is None: + for v in group: + out.append(FlowDict({"path": path, "args": list(v)})) + else: + out.append(FlowDict({"path": path, "args": merged})) + return out + + +def host_entity_ingress(ports): + """The kubelet/readiness probe peer, named rather than CIDR-matched.""" + return { + "identifier": "kubelet-probes", + "type": "internal", + "entity": "host", + "ports": [{"name": "TCP-%d" % p, "port": p, "protocol": "TCP"} for p in sorted(ports)], + } + + +# A learned exec carries the process environment, and that is where apps put +# credentials (PGPASSWORD, *_TOKEN, *_KEY). An SBoB is committed to a public +# repo, so the env never ships: it is dropped here, not redacted. +SECRET_ENV = re.compile(r"(PASS|PWD|SECRET|TOKEN|KEY|CRED)", re.I) + + +def strip_exec_envs(spec): + dropped = 0 + for e in spec.get("execs") or []: + if e.pop("envs", None) is not None: + dropped += 1 + return dropped + + +def secretish_args(spec): + out = [] + for e in spec.get("execs") or []: + for a in e.get("args") or []: + a = str(a) + if "=" in a and SECRET_ENV.search(a.split("=", 1)[0]) and a.split("=", 1)[1]: + out.append(a) + return out + + +# An ingress/egress entry with ports but NO peer — no dnsNames, ipAddresses, +# podSelector, serviceRef or entity — matches every destination on that port. +# It is the network form of a leading wildcard: a learned egress of TCP-443 with +# no peer makes R0011 blind to any C2 on 443, and UDP-53 with no peer does the +# same to R0005. The tuner produces these when its normaliser drops a +# cluster-internal address and no DNS discriminant was observed to replace it. +# A leaf can carry the volatile token INSIDE its name rather than as its own +# segment: source-controller writes /tmp/helmchart--.tgz and then +# /tmp/.tgz. Those never recur, so shipping them literally +# guarantees an R0002 on every helm fetch. normalise() only ever considered whole +# segments, so it left all ten in place. +# +# storage's "*" matches zero-or-more whole SEGMENTS (glob **), not a prefix, so +# /tmp/helmchart-* cannot be expressed. The tightest form available is one +# wildcard segment. Confirmed no flux attack asserts R0002/R0010 on a /tmp path, +# so this costs no detection the suites rely on. +EMBEDDED_VOLATILE = re.compile(r"(?:^|[-_.])[0-9a-f]{16,}(?:[-_.]|$)|\.tgz\d{4,}$|\.\w+\d{6,}$") + + +def collapse_embedded_volatile_leaves(opens, report): + out, seen = [], {} + for o in opens: + path = o.get("path", "") + head, _, leaf = path.rpartition("/") + if head and EMBEDDED_VOLATILE.search(leaf): + path = head + "/" + ELLIPSIS + report.setdefault("embedded_volatile", []).append(o.get("path")) + # Dedup on the path ALONE and union the flags. Keying on (path, flags) + # let two collapsed entries with different flags both survive, and the + # CRD rejects the object outright: duplicate entries for key [path=...]. + if path in seen: + tgt = seen[path] + for fl in o.get("flags") or []: + if fl not in tgt["flags"]: + tgt["flags"].append(fl) + continue + e = dict(o) + e["path"] = path + e["flags"] = sorted(set(o.get("flags") or [])) + seen[path] = e + out.append(e) + for e in out: + e["flags"] = sorted(set(e.get("flags") or [])) + return out + + +def peerless(entry): + for k in ("dnsNames", "ipAddresses", "podSelector", "serviceRefName", "entity"): + if entry.get(k): + return False + return not (entry.get("dns") or "").strip() + + +def drop_peerless(spec, report): + for direction in ("ingress", "egress"): + entries = spec.get(direction) + if not entries: + continue + kept = [e for e in entries if not peerless(e)] + for e in entries: + if peerless(e): + report.setdefault("peerless", []).append( + "%s %s" % (direction, [p.get("name") for p in (e.get("ports") or [])])) + spec[direction] = kept or None + + +def service_ref(identifier, namespace, name, ports, protocol="TCP"): + # Ports may be given as 53/UDP; kube-dns is the case that forced this, and a + # TCP-only entry silently fails to admit it. + out = [] + for p in sorted(ports): + proto = protocol + if isinstance(p, str) and "/" in p: + p, proto = p.split("/", 1) + p = int(p) + proto = proto.upper() + out.append({"name": "%s-%d" % (proto, p), "port": p, "protocol": proto}) + return { + "identifier": identifier, + "type": "internal", + "serviceRefNamespace": namespace, + "serviceRefName": name, + "ports": out, + } + + + + +# Node/pod CIDRs of the common distributions. An ingress entry whose ONLY peer +# specification is one of these is the blunt pre-storage#42 way of admitting a +# kubelet probe: it admits every pod on the node as a side effect. entity: host +# replaces it, so the CIDR entry must be REMOVED, not merely accompanied. +PROBE_CIDRS = {"10.42.0.0/16", "10.244.0.0/16", "10.43.0.0/16", "10.96.0.0/12", + "192.168.0.0/16", "172.16.0.0/12"} + + +def is_cidr_probe_entry(e): + if any(e.get(k) for k in ("podSelector", "namespaceSelector", "serviceSelector", + "serviceRefName", "dnsNames", "dns", "entity")): + return False + addrs = list(e.get("ipAddresses") or []) + if e.get("ipAddress"): + addrs.append(e["ipAddress"]) + return bool(addrs) and all(a in PROBE_CIDRS for a in addrs) + + +def dedup_neighbors(entries): + """Collapse entries that are identical once generalised. + + Generalisation REWRITES peers — several learned IPs of one Service become a + single serviceRef, several pod-CIDR probe entries become one entity: host — + so duplicates that did not exist in the learn are created by this pass. The + identifier is excluded from the comparison because it is a per-entry hash in + a learned profile and says nothing about what the entry admits; ports are + unioned so collapsing never narrows what was allowed. + """ + out, index = [], {} + for e in entries: + sig = json.dumps({k: v for k, v in e.items() if k not in ("identifier", "ports")}, + sort_keys=True, default=str) + if sig in index: + keep = index[sig] + seen = {(p.get("port"), p.get("protocol")) for p in (keep.get("ports") or [])} + for port in e.get("ports") or []: + if (port.get("port"), port.get("protocol")) not in seen: + keep.setdefault("ports", []).append(port) + seen.add((port.get("port"), port.get("protocol"))) + continue + index[sig] = e + out.append(e) + for e in out: + if e.get("ports"): + e["ports"] = sorted(e["ports"], key=lambda p: (p.get("port") or 0, p.get("protocol") or "")) + return out + + +def main(): + ap = argparse.ArgumentParser() + ap.add_argument("files", nargs="+") + ap.add_argument("--name") + ap.add_argument("--namespace") + ap.add_argument("--probe-ports", default="", + help="comma-separated ports the node probes; emits entity: host") + ap.add_argument("--service-ref", action="append", default=[], + help="id=ns/name:port[,port] — emits a serviceRef egress entry") + ap.add_argument("--strip-syscalls", action="store_true", default=True) + ap.add_argument("--core-binary", action="append", default=[], + help="path (or basename) of the workload's own executable; its " + "args collapse to ⋯⋯ because its identity is the discriminant") + ap.add_argument("--collapse-min", type=int, default=4, + help="collapse a read-only static directory once it has this " + "many observed leaves (0 disables)") + args = ap.parse_args() + + rc = 0 + for f in args.files: + doc = yaml.safe_load(open(f)) + spec = doc.get("spec") or {} + report = {"truncated": [], "normalised": 0, "overbroad": [], "cidr_probes": 0, + "collapsed": [], "literal_interpreters": [], "core": []} + + drop_peerless(spec, report) + dropped_envs = strip_exec_envs(spec) + if dropped_envs: + report["envs"] = dropped_envs + leaked = secretish_args(spec) + if leaked: + sys.stderr.write( + "REFUSING to emit %s: exec args carry what look like credential " + "values, which would be committed in the clear:\n" % f) + for a in leaked[:5]: + sys.stderr.write(" %s\n" % a) + sys.exit(2) + + before = len(spec.get("opens") or []) + spec["opens"] = generalise_opens(spec.get("opens") or [], report) + spec["opens"] = collapse_embedded_volatile_leaves(spec["opens"], report) + if args.collapse_min: + spec["opens"] = collapse_directories(spec["opens"], args.collapse_min, report) + + if args.strip_syscalls: + spec.pop("syscalls", None) + + if spec.get("capabilities"): + spec["capabilities"] = sorted(set(spec["capabilities"])) + + if spec.get("execs"): + spec["execs"] = generalise_execs(spec["execs"], report, args.core_binary) + + ingress = list(spec.get("ingress") or []) + if args.probe_ports: + # Replace the pod-CIDR probe stanza, in whatever identifier it was + # learned under, with the named-entity form. Leaving it in place + # would keep admitting every pod on the node. + dropped = [e for e in ingress + if e.get("identifier") == "kubelet-probes" or is_cidr_probe_entry(e)] + if dropped: + report["cidr_probes"] = len(dropped) + ingress = [e for e in ingress if e not in dropped] + if args.probe_ports: + ports = [int(p) for p in args.probe_ports.split(",") if p.strip()] + ingress.insert(0, host_entity_ingress(ports)) + spec["ingress"] = dedup_neighbors(ingress) or None + + egress = list(spec.get("egress") or []) + for sr in args.service_ref: + ident, rest = sr.split("=", 1) + nsname, ports = rest.split(":", 1) + ns, name = nsname.split("/", 1) + egress = [e for e in egress if e.get("identifier") != ident] + egress.append(service_ref(ident, ns, name, [int(p) for p in ports.split(",")])) + spec["egress"] = dedup_neighbors(egress) or None + + meta = doc.setdefault("metadata", {}) + if args.name: + meta["name"] = args.name + if args.namespace: + meta["namespace"] = args.namespace + meta.pop("labels", None) + meta["annotations"] = {"kubescape.io/managed-by": "User"} + for k in ("creationTimestamp", "resourceVersion", "uid", "generation", + "managedFields", "ownerReferences"): + meta.pop(k, None) + doc.pop("status", None) + doc["spec"] = spec + + with open(f, "w") as fh: + yaml.dump(doc, fh, Dumper=NoAliasDumper, sort_keys=False, + width=4096, allow_unicode=True, default_flow_style=None) + + print(" %-52s opens %d -> %d (dropped %d truncated, %d normalised)" + % (os.path.basename(f), before, len(spec["opens"]), + len(report["truncated"]), report["normalised"])) + for d, n in report["collapsed"]: + print(" collapsed %-52s (%d leaves) -> %s/*" % (d, n, d)) + for path, n, a0 in report["core"]: + print(" %s is the core binary — %d invocation(s) -> %d entr(ies) with ⋯⋯" + % (path, n, a0)) + for path, n in report["literal_interpreters"]: + print(" %s kept as %d literal invocation(s) — args can encode a command" + % (path, n)) + if report["cidr_probes"]: + print(" replaced %d pod-CIDR probe entr(ies) with entity: host" % report["cidr_probes"]) + if report["overbroad"]: + print(" REFUSED leading-wildcard: %s" % ", ".join(sorted(set(report["overbroad"])))) + rc = 1 + return rc + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/local-ci.sh b/scripts/local-ci.sh index 2d9b790f..a40f9b41 100755 --- a/scripts/local-ci.sh +++ b/scripts/local-ci.sh @@ -120,6 +120,10 @@ if $SETUP_ONLY || ! $TUNE_ONLY; then # ── wait for kubescape components ────────────────────────────────────────── log "=== Wait for kubescape components ===" + # rollout status FIRST: waiting on a ready node-agent pod matches the OUTGOING + # one while helm rolls the DaemonSet, so the app deploys into the gap and is + # never hooked. + kubectl -n "$KS_NS" rollout status ds/node-agent --timeout=300s kubectl wait --for=condition=ready pod -l app=node-agent -n "$KS_NS" --timeout=180s kubectl wait --for=condition=ready pod -l app=storage -n "$KS_NS" --timeout=180s kubectl wait --for=condition=ready pod -l app=alertmanager -n "$KS_NS" --timeout=120s @@ -133,6 +137,10 @@ fi # ── deploy and learn app ───────────────────────────────────────────────────── if ! $TUNE_ONLY; then + # kubectl apply does not restart a pod left Failed by an eviction, so the + # readiness wait would time out on a corpse. + kubectl delete pod -n "$APP_NS" --field-selector status.phase=Failed --ignore-not-found >/dev/null 2>&1 || true + kubectl delete pod -n "$APP_NS" --field-selector status.phase=Succeeded --ignore-not-found >/dev/null 2>&1 || true log "=== Deploy $APP via: make deploy-$APP ===" make deploy-"$APP" log "Deploy complete. Pods in $APP_NS:" @@ -174,9 +182,15 @@ if ! $TUNE_ONLY; then ELAPSED=0 PROFILE="" while [ $ELAPSED -lt $TIMEOUT ]; do + # Gate on completion, not status: the consolidated profile sits at "ready" + # until aggregation finishes, while the per-report shards named + # -<32 hex> reach "completed" first and hold a partial view. ALL_COMPLETED=$(kubectl get containerprofiles -n "$APP_NS" \ - -o jsonpath='{range .items[?(@.metadata.annotations.kubescape\.io/status=="completed")]}{.metadata.name}{"\n"}{end}' \ - 2>/dev/null | grep -v "^ug-" | grep -v "^job-" || true) + -o jsonpath='{range .items[*]}{.metadata.name}{" "}{.metadata.annotations.kubescape\.io/completion}{" "}{.metadata.annotations.kubescape\.io/status}{"\n"}{end}' \ + 2>/dev/null \ + | awk '$2 == "complete" || $3 == "completed" { print $1 }' \ + | grep -v "^ug-" | grep -v "^job-" \ + | grep -vE -- '-[0-9a-f]{32}(-|$)' || true) PROFILE=$(echo "$ALL_COMPLETED" | grep -i "$MATCH" | grep -v "client" | head -1) [[ -z "$PROFILE" ]] && PROFILE=$(echo "$ALL_COMPLETED" | grep -i "$MATCH" | head -1) [[ -z "$PROFILE" ]] && PROFILE=$(echo "$ALL_COMPLETED" | head -1)