Apply Security Context To 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 Critical Security Controls v8
- CIS GKE
- 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
-
List pods and identify those missing securityContext
- Run on: any machine with
kubectlaccess
kubectl get pods --all-namespaces -o json \| jq -r '.items[]| {ns: .metadata.namespace,pod: .metadata.name,podSC: .spec.securityContext,cSC: ([.spec.containers[].securityContext] // []),iSC: ([.spec.initContainers[].securityContext] // [])}| select(.podSC == null and ( .cSC | all(. == null) ) and ( .iSC | all(. == null) ))| [.ns, .pod] | @tsv' | column -t- This lists pods with no pod-level or container-level securityContext defined.
- Run on: any machine with
-
Inspect security context details for a specific pod
- Run on: any machine with
kubectlaccess
NAMESPACE=defaultPOD_NAME=example-podkubectl get pod "$POD_NAME" -n "$NAMESPACE" -o yaml \| yq '.spec | {securityContext, containers: [.containers[].name, .containers[].securityContext], initContainers: [.initContainers[].name, .initContainers[].securityContext]}'- Review which settings (runAsUser, runAsNonRoot, readOnlyRootFilesystem, capabilities, privileged, hostNetwork, hostPID, hostIPC, seccompProfile, etc.) are missing or overly permissive.
- Run on: any machine with
-
Review workload owners (Deployments, etc.) for missing or weak security contexts
- Run on: any machine with
kubectlaccess
kubectl get deploy,sts,ds,job,cronjob -A -o yaml \| yq '.items[]| {kind: .kind,ns: .metadata.namespace,name: .metadata.name,podSC: .spec.template.spec.securityContext,containers: [.spec.template.spec.containers[].securityContext],initContainers: [.spec.template.spec.initContainers[].securityContext]}'- Identify templates that lack securityContext or set privileged: true, allow privilege escalation, broad capabilities, or host namespaces/paths.
- Run on: any machine with
-
Decide required security context based on app needs and org policy
- For each workload identified in steps 1–3, review with the app owner whether it can:
- Run as non-root (runAsNonRoot: true, runAsUser: non-0 UID, runAsGroup)
- Use a read-only root filesystem
- Drop all capabilities and add back only specific required ones
- Avoid privileged: true and avoid hostPath, hostNetwork, hostPID, hostIPC where possible
- Refer to your org’s baseline and the CIS Google Container-Optimized OS Benchmark to choose appropriate defaults.
- For each workload identified in steps 1–3, review with the app owner whether it can:
-
Update manifests to add or tighten securityContext
- Run on: any machine with
kubectlaccess - Export current manifest, edit locally, then apply:
NAMESPACE=defaultWORKLOAD=example-deploymentkubectl get deploy "$WORKLOAD" -n "$NAMESPACE" -o yaml > /tmp/"$WORKLOAD".yaml- In
/tmp/"$WORKLOAD".yaml, under.spec.template.specadd or adjust, for example:
securityContext:runAsNonRoot: truerunAsUser: 1000runAsGroup: 1000fsGroup: 2000containers:- name: appsecurityContext:allowPrivilegeEscalation: falsereadOnlyRootFilesystem: trueprivileged: falsecapabilities:drop: ["ALL"]- Then apply:
kubectl apply -f /tmp/"$WORKLOAD".yaml - Run on: any machine with
-
Re-verify that pods now have appropriate security contexts
- Run on: any machine with
kubectlaccess
kubectl get pods --all-namespaces -o json \| jq -r '.items[]| select(.spec.securityContext == nulland ([.spec.containers[].securityContext] // [] | all(. == null))and ([.spec.initContainers[].securityContext] // [] | all(. == null)))| [.metadata.namespace, .metadata.name] | @tsv' | column -t- Confirm that critical workloads are no longer listed, and spot-check updated pods with:
kubectl get pod "$POD_NAME" -n "$NAMESPACE" -o yaml | yq '.spec' - Run on: any machine with
Using kubectl
# 1) List all pods and their security context usage (namespaces, pod & container level)
# Run on: any machine with kubectl access
kubectl get pods --all-namespaces -o custom-columns=\
NAMESPACE:.metadata.namespace,\
POD:.metadata.name,\
POD_SC:.spec.securityContext,\
CONTAINERS_SC:.spec.containers[*].securityContext
Problem indication:
POD_SCis<none>for most pods, andCONTAINERS_SCis<none>or{}(empty) for most containers.
This shows security context is largely not defined.
# 2) Inspect full spec for specific pods (e.g., application namespaces)
# Replace <namespace> and <pod-name> with actual values from your cluster
kubectl -n <namespace> get pod <pod-name> -o yaml
In the output, focus on:
.spec.securityContext(pod-level).spec.containers[*].securityContext.spec.initContainers[*].securityContext(if present)
Problem indication:
- Missing
securityContextblocks entirely. - Present but empty securityContext
{}with no meaningful fields. - Risky settings, for example:
runAsUser: 0orrunAsNonRoot: falseprivileged: trueallowPrivilegeEscalation: true- Broad
capabilities.add(e.g.,SYS_ADMIN,NET_ADMIN).
# 3) Identify containers running as root or lacking non-root guarantees
kubectl get pods --all-namespaces -o jsonpath='
{range .items[*]}
{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}
{range .spec.containers[*]}
{"runAsUser="}{.securityContext.runAsUser}{" "}
{"runAsNonRoot="}{.securityContext.runAsNonRoot}{" "}
{"privileged="}{.securityContext.privileged}{" "}
{"allowPrivilegeEscalation="}{.securityContext.allowPrivilegeEscalation}{"; "}
{end}{"\n"}
{end}'
Problem indication (requires human review):
runAsUser=is empty andrunAsNonRoot=is empty/false (likely root).privileged=trueorallowPrivilegeEscalation=truewithout a strong justification.
# 4) Show pods with any privileged container
kubectl get pods --all-namespaces -o json | \
jq -r '
.items[]
| {ns: .metadata.namespace, pod: .metadata.name, containers: .spec.containers}
| select(.containers[]?.securityContext?.privileged == true)
| "\(.ns)\t\(.pod)"
'
Problem indication:
- Any application or multi-tenant workload appears in this list without a clear, documented need to be privileged.
# 5) Show pods where allowPrivilegeEscalation is true or unset
kubectl get pods --all-namespaces -o json | \
jq -r '
.items[]
| {ns: .metadata.namespace, pod: .metadata.name, containers: .spec.containers}
| select(
[.containers[]? | .securityContext.allowPrivilegeEscalation]
| any(. == true or . == null)
)
| "\(.ns)\t\(.pod)"
'
Problem indication:
- Application pods listed here generally need stricter
securityContext(e.g.,allowPrivilegeEscalation: false) unless there is a specific requirement.
These commands only surface current state. A human must decide, per workload, which security context fields to add or tighten based on application needs and your organization’s security policy.
Automation
#!/usr/bin/env bash
#
# Report pods and containers that lack recommended securityContext settings.
# Run on any machine with kubectl access and current context set.
#
# This is a read-only assessment. It does NOT change the cluster.
set -euo pipefail
# Fail fast if kubectl is not available or cannot reach the cluster
kubectl version --short >/dev/null 2>&1 || {
echo "ERROR: kubectl not available or cannot reach the cluster" >&2
exit 1
}
echo "Collecting pod securityContext information from all namespaces..."
echo
# Headers
printf "%-32s %-24s %-40s %-12s %-7s %-7s %-9s %-11s %-11s %-11s %-9s %-9s\n" \
"NAMESPACE" "POD" "CONTAINER" "TYPE" "UID" "GID" "PRIVILEGED" "HOSTPID" "HOSTIPC" "HOSTNET" "ROFS" "DROPCAPS"
# Explanation of columns (for later review)
cat <<'EOF'
COLUMN MEANING (a blank/empty value is what you need to review):
UID : runAsUser (effective user ID)
GID : runAsGroup or fsGroup (effective group ID)
PRIVILEGED : container.securityContext.privileged
HOSTPID : pod.spec.hostPID
HOSTIPC : pod.spec.hostIPC
HOSTNET : pod.spec.hostNetwork
ROFS : container.securityContext.readOnlyRootFilesystem
DROPCAPS : whether NET_RAW (and other caps) are dropped
Rows where UID/GID/ROFS/DROPCAPS are empty or PRIVILEGED/HOST* are "true"
indicate items to review against your security policy.
EOF
# Use a single JSON query and format in bash for readability at scale
kubectl get pods -A -o json \
| jq -r '
.items[]
| . as $pod
| (
# pod-level fields
.metadata.namespace as $ns
| .metadata.name as $podName
| (.spec.securityContext // {}) as $psc
| (.spec.hostPID // false) as $hostPID
| (.spec.hostIPC // false) as $hostIPC
| (.spec.hostNetwork // false) as $hostNet
| ($psc.runAsUser // empty) as $podUid
| ($psc.runAsGroup // empty) as $podGid
| ($psc.fsGroup // empty) as $podFsGroup
| .spec.containers[]
| .name as $cname
| (.securityContext // {}) as $csc
| ($csc.runAsUser // $podUid // empty) as $uid
| ($csc.runAsGroup // $podGid // empty) as $gid
| ($psc.fsGroup // $podFsGroup // empty) as $fsgid
| ($csc.privileged // false) as $priv
| ($csc.readOnlyRootFilesystem // empty) as $rofs
| (($csc.capabilities.drop // []) | map(select(. == "NET_RAW")) | length > 0) as $dropNetRaw
| (($csc.capabilities.drop // []) | length > 0) as $dropAnyCap
| [
$ns,
$podName,
$cname,
"main",
(if $uid|tostring == "null" then "" else ($uid|tostring) end),
(if $gid|tostring == "null" then "" else ($gid|tostring) end),
(if $priv then "true" else "false" end),
(if $hostPID then "true" else "false" end),
(if $hostIPC then "true" else "false" end),
(if $hostNet then "true" else "false" end),
(if $rofs|tostring == "null" then "" else ($rofs|tostring) end),
(if $dropAnyCap then (if $dropNetRaw then "yes+NET_RAW" else "yes" end) else "" end)
]
)
| @tsv
' \
| while IFS=$'\t' read -r ns pod cname type uid gid priv hostpid hostipc hostnet rofs dropcaps; do
printf "%-32s %-24s %-40s %-12s %-7s %-7s %-9s %-11s %-11s %-11s %-9s %-9s\n" \
"$ns" "$pod" "$cname" "$type" "$uid" "$gid" "$priv" "$hostpid" "$hostipc" "$hostnet" "$rofs" "$dropcaps"
done
cat <<'EOF'
HOW TO INTERPRET POTENTIAL PROBLEMS:
Review rows where:
- UID and/or GID are empty:
No explicit runAsUser/runAsGroup configured at pod or container level.
- ROFS is empty or "false":
Root filesystem is writable for the container.
- DROPCAPS is empty:
No Linux capabilities explicitly dropped (e.g., NET_RAW).
- PRIVILEGED is "true":
Privileged container – only allow if strongly justified.
- HOSTPID / HOSTIPC / HOSTNET are "true":
Pod shares host namespaces – only allow if strongly justified.
Use this report to:
1. Identify high‑risk pods/containers.
2. Review their manifests/Helm charts against your security standards.
3. Add or tighten securityContext where appropriate (pod and/or container level).
Note: This script only reports state; applying securityContext changes must be done
manually through your normal deployment workflow (manifests, Helm, GitOps, etc.).
EOF