Security cheatsheet.
Pod Security Admission (PSA)
apiVersion: v1
kind: Namespace
metadata:
name: prod
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
Levels: privileged < baseline < restricted.
restricted requires:
runAsNonRoot: true.allowPrivilegeEscalation: false.- All capabilities dropped.
- Seccomp profile.
- No host network/PID/IPC.
securityContext
spec:
securityContext:
runAsUser: 1000
runAsGroup: 1000
fsGroup: 1000
runAsNonRoot: true
seccompProfile: { type: RuntimeDefault }
containers:
- name: app
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: [ALL]
add: [NET_BIND_SERVICE]
NetworkPolicy
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: deny-all, namespace: prod }
spec:
podSelector: {}
policyTypes: [Ingress, Egress]
Default-deny baseline. Then allow specifics.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata: { name: web-allow, namespace: prod }
spec:
podSelector: { matchLabels: { app: web } }
policyTypes: [Ingress, Egress]
ingress:
- from:
- namespaceSelector: { matchLabels: { name: ingress-nginx } }
ports: [{ port: 8000 }]
egress:
- to:
- podSelector: { matchLabels: { app: db } }
ports: [{ port: 5432 }]
- ports: [{ port: 53, protocol: UDP }] # DNS
Requires CNI that supports policy (Calico, Cilium, etc).
Kyverno (policy engine)
helm install kyverno kyverno/kyverno -n kyverno --create-namespace
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata: { name: require-labels }
spec:
validationFailureAction: Enforce
rules:
- name: check-labels
match: { any: [{ resources: { kinds: [Deployment, Pod] } }] }
validate:
message: "label 'app' is required"
pattern:
metadata:
labels:
app: "?*"
Block latest tag
spec:
rules:
- name: no-latest
match: { any: [{ resources: { kinds: [Pod] } }] }
validate:
message: ":latest tag is forbidden"
pattern:
spec:
containers:
- image: "!*:latest"
OPA Gatekeeper (alternative)
Same idea, uses Rego DSL.
Image signing (cosign)
# Sign at build
cosign sign --key cosign.key myreg/myapp:v1
# Verify policy
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata: { name: verify-images }
spec:
validationFailureAction: Enforce
rules:
- name: verify
match: { any: [{ resources: { kinds: [Pod] } }] }
verifyImages:
- imageReferences: ["myreg/myapp:*"]
attestors:
- entries:
- keys: { publicKeys: |- ...cosign public key... }
Image scanning
- trivy operator: in-cluster vulnerability scanning.
- Falco: runtime detection (suspicious syscalls).
- kubescape: posture + scanning.
RBAC review
kubectl auth can-i --list --as system:serviceaccount:prod:default
Audit what SAs can do.
Secrets at rest encryption
# kube-apiserver flag
--encryption-provider-config=/etc/kubernetes/encryption.yaml
# encryption.yaml
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
- resources: [secrets]
providers:
- aescbc:
keys:
- { name: key1, secret: <base64-32-bytes> }
- identity: {}
Re-encrypt: kubectl get secrets -A -o json | kubectl replace -f -.
Audit logs
Already in RBAC cheatsheet. Enable + ship to SIEM.
Disable default ServiceAccount auto-mount
spec:
automountServiceAccountToken: false
Pod can’t reach API. Reduces blast radius.
ResourceQuotas
apiVersion: v1
kind: ResourceQuota
metadata: { name: prod-quota, namespace: prod }
spec:
hard:
requests.cpu: "10"
requests.memory: 20Gi
limits.cpu: "20"
limits.memory: 40Gi
pods: "50"
persistentvolumeclaims: "10"
Caps namespace usage. Prevents noisy neighbor.
LimitRange
apiVersion: v1
kind: LimitRange
metadata: { name: defaults }
spec:
limits:
- type: Container
default: { cpu: 500m, memory: 256Mi }
defaultRequest: { cpu: 100m, memory: 128Mi }
max: { cpu: 2, memory: 1Gi }
min: { cpu: 50m, memory: 64Mi }
Defaults + ceilings for pods without explicit resources.
Falco (runtime)
helm install falco falcosecurity/falco
Detects:
- Shell spawned in container.
- File writes to /etc.
- Unexpected outbound connections.
- Privileged escalations.
kube-bench (CIS benchmark)
docker run --rm --pid=host -v /etc:/etc:ro -v /var:/var:ro aquasec/kube-bench
Scans cluster against CIS Kubernetes Benchmark.
Cluster hardening checklist
- PSA
restrictedon workload namespaces. - Default-deny NetworkPolicy + explicit allow.
- RBAC: least privilege; per-app ServiceAccounts.
- Encryption at rest for Secrets.
- Image signing + verification.
- Pod security context: non-root, no privilege escalation.
- Audit logging enabled.
- Falco for runtime detection.
- Updated K8s + nodes.
- Resource quotas per namespace.
Common mistakes
privileged: truefor convenience.- Mounting docker.sock into pods.
- Default ServiceAccount with cluster-admin.
- No network policy → lateral movement.
- Plaintext secrets in YAML committed to git.
Read this next
If you want my Kyverno policy bundle + hardening checklist, it’s at rajpoot.dev .
Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .