Skip to main content

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

Manual Steps
  1. 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
  2. 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 -u

    Save 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
  3. 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; do
    echo "### ${ns}/${pod}"
    kubectl get pod "${pod}" -n "${ns}" -o yaml
    echo
    done < /tmp/pods-missing-securitycontext.txt > /tmp/pods-missing-securitycontext.yaml

    Review /tmp/pods-missing-securitycontext.yaml and, 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).

  4. 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 pod
    while read ns pod; do
    echo "### ${ns}/${pod}"
    kubectl get pod "${pod}" -n "${ns}" -o jsonpath='{.metadata.ownerReferences}' | jq .
    echo
    done < /tmp/pods-missing-securitycontext.txt

    Then, 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 settings
    kubectl apply -f /tmp/deploy.yaml
  5. 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:

    # Helm
    helm list -A
    # Find resources by label (replace key=value with known labels)
    kubectl get all -A -l app=<value> -o yaml

    Update the upstream definition to include the agreed securityContext settings, then redeploy via the tool.

  6. 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 -u

    The 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 securityContext field at all.
  • runAsUser: 0 or runAsNonRoot: false (or missing when image UID is root).
  • privileged: true.
  • allowPrivilegeEscalation: true (or unset when running as root).
  • capabilities.add including broad capabilities like ALL, NET_ADMIN, SYS_ADMIN, etc., without clear justification.
  • readOnlyRootFilesystem: false (or missing) when not strictly required to write to rootfs.
  • hostNetwork: true, hostPID: true, or hostIPC: true at pod level without a strong operational reason.
  • seLinuxOptions, seccompProfile, or AppArmor annotations 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:

  • securityContext present 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.securityContext
    • Containers 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 securityContext at that level, but you still must manually check whether the values are appropriate.

Additional Reading: