add Kyverno and Falco security test workloads
Kube-bench CIS scan / Scan ephemeral K3s cluster (push) Successful in 1m5s
Kube-bench CIS scan / Scan ephemeral K3s cluster (push) Successful in 1m5s
This commit is contained in:
@@ -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 \
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
Reference in New Issue
Block a user