Apply Security Context Your Pods And Containers
More Info:
Apply Security Context to Your Pods and Containers
Risk Level
Medium
Address
Security
Compliance Standards
- APRA CPS 234 (Australia)
- BSI C5 (Germany)
- Brazil LGPD
- CCPA / CPRA (California)
- CIS AKS
- CIS Critical Security Controls v8
- CMMC 2.0
- CSA Cloud Controls Matrix v4
- DPDPA
- Digital Operational Resilience Act (EU)
- Essential 8
- ISO/IEC 27017
- ISO/IEC 27018
- ISO/IEC 27701
- KSA PDPL
- MAS Technology Risk Management (Singapore)
- MITRE ATT&CK (Cloud)
- NIS2 Directive
- NIST SP 800-171
- NYDFS 23 NYCRR 500
- SWIFT Customer Security Controls Framework
- Sarbanes-Oxley IT General Controls
- UK NCSC Cyber Assessment Framework
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
From any machine with kubectl access, list pods and identify those lacking securityContext at pod and container level (focus on non-system namespaces first):
kubectl get pods --all-namespaces -o json \| jq '.items[]| {namespace: .metadata.namespace,pod: .metadata.name,podSecurityContext: .spec.securityContext,containers: (.spec.containers[]| {name: .name,securityContext: .securityContext})}'Export the result for review:
kubectl get pods --all-namespaces -o json > /tmp/all-pods.json -
Identify pods and containers missing a securityContext block for targeted review:
cat /tmp/all-pods.json \| jq -r '.items[]| select((.spec.securityContext // {} ) == {} or([.spec.containers[].securityContext // {} == {}] | any))| [.metadata.namespace, .metadata.name]| @tsv' | sort -uSave this list as your remediation backlog:
cat /tmp/all-pods.json \| jq -r '.items[]| select((.spec.securityContext // {} ) == {} or([.spec.containers[].securityContext // {} == {}] | any))| [.metadata.namespace, .metadata.name]| @tsv' | sort -u > /tmp/pods-missing-securitycontext.txt -
For each listed pod, fetch and inspect its manifest to understand current behavior and what constraints are acceptable for the workload (e.g., need for root, capabilities, filesystem writes):
while read ns pod; doecho "### ${ns}/${pod}"kubectl get pod "${pod}" -n "${ns}" -o yamlechodone < /tmp/pods-missing-securitycontext.txt > /tmp/pods-missing-securitycontext.yamlReview
/tmp/pods-missing-securitycontext.yamland, for each workload, decide on appropriate security context settings (pod-level and per-container), using the Kubernetes documentation and CIS Docker Benchmark as guidance (e.g., runAsNonRoot, runAsUser, readOnlyRootFilesystem, dropping capabilities, seccompProfile). -
Locate the owning manifests or controllers (Deployment, StatefulSet, DaemonSet, Job, CronJob, or standalone Pod) and edit them to add the decided securityContext fields:
# Identify owner for each podwhile read ns pod; doecho "### ${ns}/${pod}"kubectl get pod "${pod}" -n "${ns}" -o jsonpath='{.metadata.ownerReferences}' | jq .echodone < /tmp/pods-missing-securitycontext.txtThen, for each owning object, export, edit, and re-apply (example for a Deployment):
kubectl get deploy <deployment-name> -n <namespace> -o yaml > /tmp/deploy.yaml# Edit /tmp/deploy.yaml to add spec.template.spec.securityContext and# spec.template.spec.containers[].securityContext with your chosen settingskubectl apply -f /tmp/deploy.yaml -
For workloads created by tools (Helm, GitOps, operators), make changes at the source (Helm values, Git manifests, operator config) rather than via direct kubectl edit, to ensure they persist. Use these commands to locate Helm or label-based ownership:
# Helmhelm list -A# Find resources by label (replace key=value with known labels)kubectl get all -A -l app=<value> -o yamlUpdate the upstream definition to include the agreed securityContext settings, then redeploy via the tool.
-
Verify that all targeted pods now have security contexts applied:
kubectl get pods --all-namespaces -o json \| jq -r '.items[]| select((.spec.securityContext // {} ) == {} or([.spec.containers[].securityContext // {} == {}] | any))| [.metadata.namespace, .metadata.name]| @tsv' | sort -uThe command should return no entries (or only those you have consciously excepted with documented justification).
Using kubectl
Using kubectl
Run these commands from any machine with kubectl access.
1. List pods and see if securityContext is set at pod level
kubectl get pods -A -o custom-columns=NS:metadata.namespace,NAME:metadata.name,SC:.spec.securityContext --sort-by=metadata.namespace
Problem indication:
Pods where the SC column is <none> or {} have no pod-level securityContext at all. These should be reviewed to see if they rely only on container-level settings or have no controls defined.
For a more focused list of pods missing a pod-level securityContext:
kubectl get pods -A -o json \
| jq -r '.items[]
| select(.spec.securityContext == null or .spec.securityContext == {})
| [.metadata.namespace, .metadata.name]
| @tsv'
2. Inspect container-level security contexts
First, get full YAML for a specific pod:
kubectl get pod <pod-name> -n <namespace> -o yaml
Review under:
.spec.securityContext(pod-level).spec.containers[].securityContext.spec.initContainers[].securityContext
Problem indication (examples that need review):
- Containers with no
securityContextfield at all. runAsUser: 0orrunAsNonRoot: false(or missing when image UID is root).privileged: true.allowPrivilegeEscalation: true(or unset when running as root).capabilities.addincluding broad capabilities likeALL,NET_ADMIN,SYS_ADMIN, etc., without clear justification.readOnlyRootFilesystem: false(or missing) when not strictly required to write to rootfs.hostNetwork: true,hostPID: true, orhostIPC: trueat pod level without a strong operational reason.seLinuxOptions,seccompProfile, orAppArmorannotations missing where your policy expects them.
3. Identify pods with high-risk security context settings
Pods running any privileged container:
kubectl get pods -A -o json \
| jq -r '
.items[]
| . as $pod
| ([$pod.spec.containers[], ($pod.spec.initContainers // [])[]] // [])
| map(select(.securityContext.privileged == true))
| select(length > 0)
| $pod.metadata.namespace + "\t" + $pod.metadata.name'
Pods allowing privilege escalation:
kubectl get pods -A -o json \
| jq -r '
.items[]
| . as $pod
| ([$pod.spec.containers[], ($pod.spec.initContainers // [])[]] // [])
| map(select(.securityContext.allowPrivilegeEscalation == true))
| select(length > 0)
| $pod.metadata.namespace + "\t" + $pod.metadata.name'
Pods using host networking, PID, or IPC:
kubectl get pods -A -o json \
| jq -r '
.items[]
| select(.spec.hostNetwork == true or .spec.hostPID == true or .spec.hostIPC == true)
| .metadata.namespace + "\t" + .metadata.name + "\t" +
"hostNetwork=" + ( .spec.hostNetwork|tostring ) + "," +
"hostPID=" + ( .spec.hostPID|tostring ) + "," +
"hostIPC=" + ( .spec.hostIPC|tostring )'
Problem indication:
Any pods returned by these commands use elevated privileges or host access that must be explicitly justified and tightly controlled.
4. Spot containers likely running as root without non-root constraints
kubectl get pods -A -o json \
| jq -r '
.items[]
| . as $pod
| ([$pod.spec.containers[], ($pod.spec.initContainers // [])[]] // [])
| map({
ns: $pod.metadata.namespace,
pod: $pod.metadata.name,
name: .name,
sc: .securityContext,
psc: $pod.spec.securityContext
})
| .[]
| select(
# no explicit non-root control at pod or container level
((.sc.runAsNonRoot == null) and (.psc.runAsNonRoot == null))
and
# no explicit non-root UID at pod or container level
((.sc.runAsUser == null) and (.psc.runAsUser == null))
)
| .ns + "\t" + .pod + "\t" + .name'
Problem indication:
Containers listed here have no enforcement that they run as a non-root user; they may be running as root depending on the image. These should be reviewed and typically updated to use runAsNonRoot: true and/or a non-root runAsUser, following your application requirements and Docker CIS guidance.
5. Verify after you update manifests
After you adjust deployments/statefulsets/daemonsets and re-apply them, confirm pods now have appropriate security contexts:
kubectl get pods -A -o yaml \
| grep -E '^(kind: Pod| securityContext:| runAsNonRoot:| runAsUser:| privileged:| allowPrivilegeEscalation:| readOnlyRootFilesystem:| capabilities:)' -n
You should see:
securityContextpresent at pod and/or container level for all relevant pods.- High-risk settings (
privileged: true,allowPrivilegeEscalation: true, host* fields, broad capabilities) either absent or used only where explicitly justified.
Automation
#!/usr/bin/env bash
# Report pods and containers without securityContext so they can be reviewed.
# Run on: any machine with kubectl access and appropriate RBAC.
set -euo pipefail
echo "### Cluster-wide securityContext report (namespaces, pods, containers) ###"
echo
# 1) Pods missing pod-level securityContext
echo "== Pods WITHOUT pod-level .spec.securityContext =="
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| select(.spec.securityContext == null)
| [.metadata.namespace, .metadata.name]
| @tsv
' | awk 'BEGIN {printf "NAMESPACE\tPOD\n"} {print}' || {
echo "Failed to query pods or parse JSON"; exit 1;
}
echo
# 2) Containers missing container-level securityContext
# (for both .spec.containers and .spec.initContainers)
echo "== Containers WITHOUT container-level .securityContext =="
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
containers: (
( .spec.containers // [] | map({name, type:"app", sc: .securityContext}) )
+
( .spec.initContainers // [] | map({name, type:"init", sc: .securityContext}) )
)
}
| .containers[]
| select(.sc == null)
| [.ns, .pod, .type, .name]
| @tsv
' | awk 'BEGIN {printf "NAMESPACE\tPOD\tCONTAINER_TYPE\tCONTAINER_NAME\n"} {print}' || {
echo "Failed to query pods or parse JSON"; exit 1;
}
echo
# 3) Optional: brief summary counts
echo "== Summary counts =="
echo "- Total pods:"
kubectl get pods --all-namespaces --no-headers 2>/dev/null | wc -l
echo "- Pods without pod-level securityContext:"
kubectl get pods --all-namespaces -o json \
| jq '[.items[] | select(.spec.securityContext == null)] | length'
echo "- Containers (including initContainers) without container-level securityContext:"
kubectl get pods --all-namespaces -o json \
| jq '
[
.items[]
| (
( .spec.containers // [] )
+
( .spec.initContainers // [] )
)
| .[]
| select(.securityContext == null)
] | length
'
How to interpret the output
- The sections listing:
Pods WITHOUT pod-level .spec.securityContextContainers WITHOUT container-level .securityContext
- Any line shown there indicates something to review manually against your security baseline and the CIS guidance (e.g. whether that pod/container should define fields like
runAsUser,runAsNonRoot,readOnlyRootFilesystem,allowPrivilegeEscalation, capabilities, etc.). - Empty sections (only the header line, no data rows) mean that all pods currently define a
securityContextat that level, but you still must manually check whether the values are appropriate.