Kubernetes Security Cheat Sheet¶
Remember: The "minimum viable security" for a Kubernetes pod:
runAsNonRoot: true,readOnlyRootFilesystem: true,allowPrivilegeEscalation: false,capabilities.drop: ["ALL"]. These four settings prevent the most common container escape vectors. Mnemonic: "NRAD" — Non-root, Read-only, no privilege Ascension, Drop caps.Gotcha:
readOnlyRootFilesystem: truebreaks applications that write to/tmp,/var/run, or other directories. MountemptyDirvolumes for those paths. Failing to do this is the #1 reason teams remove this security setting instead of fixing their app.
Pod Security Context¶
spec:
securityContext: # Pod level
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
securityContext: # Container level
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]
RBAC¶
# Role (namespace-scoped)
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: pod-reader
namespace: production
rules:
- apiGroups: [""]
resources: ["pods", "pods/log"]
verbs: ["get", "list", "watch"]
---
# Bind role to ServiceAccount
kind: RoleBinding
metadata:
name: read-pods
subjects:
- kind: ServiceAccount
name: monitoring
namespace: production
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
# Check permissions
kubectl auth can-i get pods -n prod --as=system:serviceaccount:prod:mysa
kubectl auth can-i --list --as=system:serviceaccount:prod:mysa
Image Security¶
# Scan for vulnerabilities
trivy image myapp:latest
trivy image --severity CRITICAL --exit-code 1 myapp:latest
# Sign image
cosign sign --key cosign.key registry.example.com/myapp:v1
# Verify signature
cosign verify --key cosign.pub registry.example.com/myapp:v1
Pod Security Standards¶
# Namespace labels
metadata:
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
| Level | Allows |
|---|---|
| privileged | Everything (unrestricted) |
| baseline | Blocks known escalations (hostNetwork, privileged) |
| restricted | Non-root, drop caps, seccomp, no hostPath |
Network Security¶
# Default deny all traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
Then add explicit allow rules per service pair.
Secret Hygiene¶
DO:
✓ Encrypt secrets at rest (etcd encryption)
✓ Use external secret stores (Vault, AWS SM, ESO)
✓ Rotate credentials regularly
✓ Audit secret access (K8s audit logging)
✓ Use short-lived credentials (IRSA, Workload Identity)
DON'T:
✗ Commit secrets to Git
✗ Log secrets (mask in app logs)
✗ Pass secrets as CLI arguments (visible in /proc)
✗ Use default ServiceAccount
✗ Store secrets in ConfigMaps
Audit Logging¶
# audit-policy.yaml
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: RequestResponse
resources:
- group: ""
resources: ["secrets"]
verbs: ["get", "list"]
- level: Metadata
resources:
- group: ""
resources: ["pods"]
Supply Chain Checklist¶
[ ] Minimal base images (distroless, alpine)
[ ] Pin image digests, not tags
[ ] Scan in CI (Trivy, Grype)
[ ] Sign images (cosign)
[ ] Verify signatures at admission (Kyverno, Gatekeeper)
[ ] Private registry with access control
[ ] No package managers in production images
[ ] SBOM generation for all images
Quick Security Audit¶
# Find pods running as root
kubectl get pods -A -o json | jq -r '
.items[] | select(.spec.containers[].securityContext.runAsNonRoot != true) |
"\(.metadata.namespace)/\(.metadata.name)"'
# Find pods with host networking
kubectl get pods -A -o json | jq -r '
.items[] | select(.spec.hostNetwork == true) |
"\(.metadata.namespace)/\(.metadata.name)"'
# Find pods with privileged containers
kubectl get pods -A -o json | jq -r '
.items[] | select(.spec.containers[].securityContext.privileged == true) |
"\(.metadata.namespace)/\(.metadata.name)"'
# Check for default SA usage
kubectl get pods -A -o json | jq -r '
.items[] | select(.spec.serviceAccountName == "default") |
"\(.metadata.namespace)/\(.metadata.name)"'