add Kyverno and Falco security test workloads
Kube-bench CIS scan / Scan ephemeral K3s cluster (push) Successful in 1m5s

This commit is contained in:
2026-08-14 22:08:52 -04:00
parent 2bd22db276
commit 2506b61ae0
11 changed files with 204 additions and 2 deletions
@@ -17,6 +17,7 @@ Required environment variables:
Optional environment variables: Optional environment variables:
VM_NAME VM name, default flatcar-k3d-test VM_NAME VM name, default flatcar-k3d-test
CORES CPU cores, default 4 CORES CPU cores, default 4
CPU_TYPE Proxmox CPU model, default host passthrough
MEMORY_MB Memory in MiB, default 16384 MEMORY_MB Memory in MiB, default 16384
DISK_SIZE Final disk size, default 100G DISK_SIZE Final disk size, default 100G
BRIDGE Proxmox bridge, default vmbr0 BRIDGE Proxmox bridge, default vmbr0
@@ -56,6 +57,7 @@ done
VM_NAME="${VM_NAME:-flatcar-k3d-test}" VM_NAME="${VM_NAME:-flatcar-k3d-test}"
CORES="${CORES:-4}" CORES="${CORES:-4}"
CPU_TYPE="${CPU_TYPE:-host}"
MEMORY_MB="${MEMORY_MB:-16384}" MEMORY_MB="${MEMORY_MB:-16384}"
DISK_SIZE="${DISK_SIZE:-100G}" DISK_SIZE="${DISK_SIZE:-100G}"
BRIDGE="${BRIDGE:-vmbr0}" BRIDGE="${BRIDGE:-vmbr0}"
@@ -181,6 +183,7 @@ qm create "$VM_ID" \
--name "$VM_NAME" \ --name "$VM_NAME" \
--ostype l26 \ --ostype l26 \
--cores "$CORES" \ --cores "$CORES" \
--cpu "cputype=$CPU_TYPE" \
--memory "$MEMORY_MB" \ --memory "$MEMORY_MB" \
--net0 "virtio,bridge=$BRIDGE" \ --net0 "virtio,bridge=$BRIDGE" \
--ipconfig0 ip=dhcp \ --ipconfig0 ip=dhcp \
+3
View File
@@ -8,6 +8,9 @@ The default profile is 4 CPU cores, 16 GiB memory, and a 100 GiB disk. The
Ignition configuration enables Docker, creates `/opt/k3d-test/workspace`, and Ignition configuration enables Docker, creates `/opt/k3d-test/workspace`, and
installs pinned k3d and kubectl binaries on first boot. installs pinned k3d and kubectl binaries on first boot.
The VM defaults to Proxmox CPU passthrough. This is important for Falco and
modern container images that require x86-64-v2 CPU features.
Enable snippets on the Proxmox storage first: Enable snippets on the Proxmox storage first:
```bash ```bash
+32
View File
@@ -0,0 +1,32 @@
# Security test workloads
The two manifests in the Kustomization are intentionally allowed through
Kyverno. ArgoCD manages them so the baseline always has one admission-pass
workload and one runtime-alert workload.
The manifests under `blocked/` are negative tests. They are intentionally not
included in the Kustomization because ArgoCD would continuously report them as
failed syncs. Apply them manually and expect admission rejection:
```bash
kubectl apply --server-side --dry-run=server -f test-workloads/blocked/privileged-pod.yaml
kubectl apply --server-side --dry-run=server -f test-workloads/blocked/root-container.yaml
kubectl apply --server-side --dry-run=server -f test-workloads/blocked/host-network-pod.yaml
kubectl apply --server-side --dry-run=server -f test-workloads/blocked/unapproved-registry-pod.yaml
kubectl apply --server-side --dry-run=server -f test-workloads/blocked/default-namespace-pod.yaml
```
Expected policy coverage:
| Manifest | Expected result | Policy or detector |
| --- | --- | --- |
| `blocked/privileged-pod.yaml` | Rejected | `disallow-privileged`, CIS 5.2.2 |
| `blocked/root-container.yaml` | Rejected | `require-non-root`, CIS 5.2.7 |
| `blocked/host-network-pod.yaml` | Rejected | `disallow-host-network`, CIS 5.2.3 and 5.2.5 |
| `blocked/unapproved-registry-pod.yaml` | Rejected | `restrict-image-registries`, supplemental supply-chain control |
| `blocked/default-namespace-pod.yaml` | Rejected | `disallow-default-namespace`, supplemental namespace control |
| `compliant-workload.yaml` | Accepted | Admission baseline |
| `suspicious-shell.yaml` | Accepted, then alerted | Falco shell detection |
The suspicious workload is intentionally compliant at admission. Its shell
process is the runtime event that Falco should report.
@@ -0,0 +1,20 @@
apiVersion: v1
kind: Pod
metadata:
name: blocked-default-namespace
namespace: default
spec:
restartPolicy: Never
securityContext:
runAsNonRoot: true
containers:
- name: app
image: docker.io/library/busybox:1.36
command: ["sleep", "3600"]
securityContext:
runAsNonRoot: true
runAsUser: 1000
resources:
limits:
cpu: 100m
memory: 64Mi
@@ -0,0 +1,23 @@
apiVersion: v1
kind: Pod
metadata:
name: blocked-host-network
namespace: security-baseline
spec:
hostNetwork: true
hostPID: true
hostIPC: true
restartPolicy: Never
securityContext:
runAsNonRoot: true
containers:
- name: app
image: docker.io/library/busybox:1.36
command: ["sleep", "3600"]
securityContext:
runAsNonRoot: true
runAsUser: 1000
resources:
limits:
cpu: 100m
memory: 64Mi
@@ -0,0 +1,21 @@
apiVersion: v1
kind: Pod
metadata:
name: blocked-privileged
namespace: security-baseline
spec:
restartPolicy: Never
securityContext:
runAsNonRoot: true
containers:
- name: app
image: docker.io/library/busybox:1.36
command: ["sleep", "3600"]
securityContext:
privileged: true
runAsNonRoot: true
runAsUser: 1000
resources:
limits:
cpu: 100m
memory: 64Mi
@@ -0,0 +1,18 @@
apiVersion: v1
kind: Pod
metadata:
name: blocked-root
namespace: security-baseline
spec:
restartPolicy: Never
containers:
- name: app
image: docker.io/library/busybox:1.36
command: ["sleep", "3600"]
securityContext:
runAsNonRoot: false
runAsUser: 0
resources:
limits:
cpu: 100m
memory: 64Mi
@@ -0,0 +1,20 @@
apiVersion: v1
kind: Pod
metadata:
name: blocked-unapproved-registry
namespace: security-baseline
spec:
restartPolicy: Never
securityContext:
runAsNonRoot: true
containers:
- name: app
image: registry.example.com/security-test:1.0
command: ["sleep", "3600"]
securityContext:
runAsNonRoot: true
runAsUser: 1000
resources:
limits:
cpu: 100m
memory: 64Mi
+31
View File
@@ -0,0 +1,31 @@
apiVersion: v1
kind: Pod
metadata:
name: compliant-workload
namespace: security-baseline
labels:
app.kubernetes.io/name: compliant-workload
security-baseline/test: admission-pass
spec:
restartPolicy: Never
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: docker.io/library/busybox:1.36
command: ["sh", "-c", "echo compliant-workload-ready; sleep 3600"]
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
cpu: 100m
memory: 64Mi
+2 -2
View File
@@ -1,5 +1,5 @@
apiVersion: kustomize.config.k8s.io/v1beta1 apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization kind: Kustomization
resources: resources:
- phase-2-placeholder.yaml - compliant-workload.yaml
- suspicious-shell.yaml
+31
View File
@@ -0,0 +1,31 @@
apiVersion: v1
kind: Pod
metadata:
name: suspicious-shell
namespace: security-baseline
labels:
app.kubernetes.io/name: suspicious-shell
security-baseline/test: runtime-alert
spec:
restartPolicy: Never
securityContext:
runAsNonRoot: true
seccompProfile:
type: RuntimeDefault
containers:
- name: shell-test
image: docker.io/library/busybox:1.36
command: ["sh", "-c", "echo suspicious-shell-started; sleep 3600"]
securityContext:
runAsNonRoot: true
runAsUser: 1000
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
resources:
requests:
cpu: 10m
memory: 16Mi
limits:
cpu: 100m
memory: 64Mi