Skip to main content

Apply Security Context To Your Pods And Containers

More Info:

Security contexts constrain the privileges and access of pods and containers at runtime. Apply appropriate security contexts to all pods and containers.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS AKS

Triage and Remediation

Remediation

Manual Steps
  1. List pods missing or with weak securityContext

    • Run on: any machine with kubectl access
    • Command:
      kubectl get pods --all-namespaces -o json \
      | jq -r '
      .items[]
      | select(
      # Pod-level securityContext absent or empty
      (.spec.securityContext // {} | length == 0)
      or
      # Any container without securityContext
      ( [.spec.containers[], (.spec.initContainers // [])[]]
      | map(select((.securityContext // {} | length == 0))) | length > 0
      )
      )
      | [.metadata.namespace, .metadata.name]
      | @tsv
      '
  2. Gather full specs for candidate pods

    • Run on: any machine with kubectl access
    • For each <namespace> <pod> from step 1:
      kubectl get pod <pod-name> -n <namespace> -o yaml > pod-<namespace>-<pod-name>.yaml
  3. Review current securityContext settings

    • Open each pod-*.yaml and check at both pod and container levels for (examples of what to look for; you must decide what is appropriate for your workloads):
      • Missing securityContext: blocks
      • Privilege-related fields: privileged, allowPrivilegeEscalation, capabilities.add/drop, runAsUser, runAsGroup, runAsNonRoot, readOnlyRootFilesystem, seLinuxOptions, seccompProfile, procMount, hostPID, hostIPC, hostNetwork, hostPorts.
    • Identify pods/containers that run with unnecessary or overly broad privileges relative to their function.
  4. Decide appropriate securityContext per workload

    • For each pod/container, decide:
      • The minimal user/group IDs (runAsUser, runAsGroup, runAsNonRoot: true)
      • Whether privileged is truly needed; if not, set privileged: false
      • Whether allowPrivilegeEscalation can be disabled: allowPrivilegeEscalation: false
      • Required and removable Linux capabilities (capabilities.drop and capabilities.add)
      • Whether the root filesystem can be read-only: readOnlyRootFilesystem: true
      • Whether host namespaces/ports are truly required (avoid hostPID, hostIPC, hostNetwork, host ports where possible).
    • Use your application and Docker image requirements plus the CIS Docker Benchmark as guidance.
  5. Implement securityContext via manifests (preferred) or live edit

    • If workloads are managed by manifests/Helm/Kustomize:
      • Edit the deployment/statefulset/daemonset/job manifests (not the pods directly) to add/adjust securityContext at pod spec and container spec levels according to step 4. Example pattern (adapt as needed for each workload):
        spec:
        securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        runAsGroup: 1000
        containers:
        - name: <container-name>
        image: <image>
        securityContext:
        allowPrivilegeEscalation: false
        privileged: false
        readOnlyRootFilesystem: true
        capabilities:
        drop: ["ALL"]
      • Apply changes:
        kubectl apply -f <manifest-file>.yaml
    • If a pod is not managed by higher-level controllers (e.g., a standalone pod for debugging):
      • Edit directly:
        kubectl edit pod <pod-name> -n <namespace>
      • Add/adjust the securityContext blocks as decided in step 4; save to recreate the pod.
  6. Verify that security contexts are now applied

    • Re-run the evidence command and confirm no workload you intend to secure is listed:
      kubectl get pods --all-namespaces -o json \
      | jq -r '
      .items[]
      | select(
      (.spec.securityContext // {} | length == 0)
      or
      ( [.spec.containers[], (.spec.initContainers // [])[]]
      | map(select((.securityContext // {} | length == 0))) | length > 0
      )
      )
      | [.metadata.namespace, .metadata.name]
      | @tsv
      '
    • Spot-check a few pods:
      kubectl get pod <pod-name> -n <namespace> -o yaml | sed -n '/securityContext:/,/^ *[^ ]/p'
    • Confirm that the configured securityContext values match your decisions from step 4 and that applications still function as expected.
Using kubectl
# 1. List all pods and their securityContext at pod level
# Run on: any machine with kubectl access
kubectl get pods -A -o custom-columns=\
NAMESPACE:.metadata.namespace,\
POD:.metadata.name,\
POD_SC:.spec.securityContext \
| column -t

Problem indicators:

  • POD_SC column is empty (<none> or {}) for pods that should have restricted settings.
  • You see no common baseline (some pods with strict settings, others with none, without a clear reason).
# 2. Show container-level securityContext for all pods (summarized)
kubectl get pods -A -o jsonpath='
{@.items[*].metadata.namespace}{"\n"}{@.items[*].metadata.name}{"\n"}{@.items[*].spec.containers[*].name}{"\n"}{@.items[*].spec.containers[*].securityContext}{"\n---\n"}'

Problem indicators (per container):

  • securityContext is missing (null/[]) or {} for workloads that handle sensitive data or run user code.
  • Known risky patterns, for example:
    • "privileged": true
    • "runAsUser": 0 or "runAsNonRoot": false without a strong justification
    • "allowPrivilegeEscalation": true
    • Broad capabilities like "capAdd": ["ALL"] or many capabilities with no clear need
    • Host access options such as "hostNetwork": true, "hostPID": true, "hostIPC": true at pod spec level.
# 3. Inspect full spec for a specific pod with suspected issues
# Replace NAMESPACE and POD_NAME with actual values
kubectl get pod POD_NAME -n NAMESPACE -o yaml

What to look for in the YAML:

  • Missing securityContext at both .spec.securityContext and .spec.containers[].securityContext.
  • Any of:
    • privileged: true
    • runAsNonRoot: false or absent, combined with runAsUser: 0
    • allowPrivilegeEscalation: true or absent when the container has extra capabilities
    • capabilities.add including unnecessary or broad capabilities (e.g., SYS_ADMIN, NET_ADMIN, ALL)
    • hostNetwork: true, hostPID: true, hostIPC: true without a strict operational reason
    • readOnlyRootFilesystem: false or absent when the container does not need to write to the root FS.
# 4. Focus on pods most likely to be risky (privileged / host access)
kubectl get pods -A -o json | jq '
.items[]
| select(
(.spec.containers[]?.securityContext.privileged == true)
or (.spec.securityContext.privileged == true)
or (.spec.hostNetwork == true)
or (.spec.hostPID == true)
or (.spec.hostIPC == true)
)
| {namespace: .metadata.namespace, name: .metadata.name,
hostNetwork: .spec.hostNetwork,
hostPID: .spec.hostPID,
hostIPC: .spec.hostIPC,
podSC: .spec.securityContext,
containers: [.spec.containers[] | {name, sc: .securityContext}]}
'

Problem indicators:

  • Any application pod (not a low-level node/cluster agent) appearing in this list without a clear operational need.
  • Cluster-wide patterns where many pods use privileged mode or host namespace access.
# 5. Identify pods without any securityContext on containers
kubectl get pods -A -o json | jq '
.items[]
| {ns: .metadata.namespace, pod: .metadata.name,
containers: [.spec.containers[] | {name, sc: .securityContext}]}
| select( ([.containers[].sc] | all(. == null)) )
'

Problem indicators:

  • Business‑critical or multi‑tenant workloads listed here; they likely need explicit securityContext settings instead of inheriting defaults.
Automation
#!/usr/bin/env bash
# Report pods and containers missing recommended securityContext fields
# Run on: any machine with kubectl access and a current kube-context

set -euo pipefail

# Namespace filter (optional). Leave empty for all namespaces.
NAMESPACE_FILTER=""

if [[ -n "${NAMESPACE_FILTER}" ]]; then
NS_ARG="-n ${NAMESPACE_FILTER}"
else
NS_ARG="--all-namespaces"
fi

echo "Collecting pod securityContext summary..."
echo "Timestamp: $(date -Iseconds)"
echo

# 1) High-level summary: pods/containers missing securityContext
kubectl get pods ${NS_ARG} -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
podSC: .spec.securityContext,
containers: (
(.spec.containers // [])
+ (.spec.initContainers // [])
+ (.spec.ephemeralContainers // [])
)
}
| .containers[]
| {
ns: .ns,
pod: .pod,
cName: .name,
cSC: .securityContext
}
| [
.ns,
.pod,
.cName,
(if .cSC == null then "MISSING_SC" else "HAS_SC" end)
]
| @tsv
' \
| sort -u \
| awk -F'\t' '
BEGIN {
printf "NAMESPACE\tPOD\tCONTAINER\tSECURITY_CONTEXT\n"
}
{
print
}
'

echo
echo "Detail for containers missing any securityContext (one line per container)..."
echo "Columns:"
echo " NAMESPACE | POD | CONTAINER | privileged | runAsUser | runAsNonRoot | readOnlyRootFilesystem | allowPrivilegeEscalation | capabilities.add | capabilities.drop"
echo

kubectl get pods ${NS_ARG} -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
podSC: .spec.securityContext,
containers: (
(.spec.containers // [])
+ (.spec.initContainers // [])
+ (.spec.ephemeralContainers // [])
)
}
| .containers[]
| select(.securityContext == null)
| {
ns: .ns,
pod: .pod,
cName: .name,
sc: .securityContext
}
| [
.ns,
.pod,
.cName,
( .sc.privileged // "unset" ),
( .sc.runAsUser // "unset" ),
( .sc.runAsNonRoot // "unset" ),
( .sc.readOnlyRootFilesystem // "unset" ),
( .sc.allowPrivilegeEscalation // "unset" ),
( ( .sc.capabilities.add | join(",") ) // "unset" ),
( ( .sc.capabilities.drop | join(",") ) // "unset" )
]
| @tsv
' \
| sort -u

echo
echo "Detail for containers with a securityContext but missing specific restrictive fields..."
echo "Only pods where the container has securityContext but at least one key is unset."
echo

kubectl get pods ${NS_ARG} -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
containers: (
(.spec.containers // [])
+ (.spec.initContainers // [])
+ (.spec.ephemeralContainers // [])
)
}
| .containers[]
| select(.securityContext != null)
| {
ns: .ns,
pod: .pod,
cName: .name,
sc: .securityContext
}
| select(
(.sc.privileged == null)
or (.sc.runAsNonRoot == null)
or (.sc.readOnlyRootFilesystem == null)
or (.sc.allowPrivilegeEscalation == null)
or (.sc.capabilities == null)
or (.sc.capabilities.drop == null)
)
| [
.ns,
.pod,
.cName,
( .sc.privileged // "unset" ),
( .sc.runAsUser // "unset" ),
( .sc.runAsNonRoot // "unset" ),
( .sc.readOnlyRootFilesystem // "unset" ),
( .sc.allowPrivilegeEscalation // "unset" ),
( ( .sc.capabilities.add | join(",") ) // "unset" ),
( ( .sc.capabilities.drop | join(",") ) // "unset" )
]
| @tsv
' \
| sort -u

echo
echo "NOTE: This script is read-only. It highlights pods/containers that:"
echo " - have no container-level securityContext at all (MISSING_SC), or"
echo " - have a securityContext but are missing key restrictive fields."
echo
echo "You must review each workload and decide appropriate securityContext values"
echo "based on its function and your risk tolerance; there is no one-size-fits-all fix."

How to interpret the output

  • In the first table, any row with SECURITY_CONTEXT = MISSING_SC indicates a pod container with no securityContext defined at the container level. These need manual review and likely a securityContext added.
  • In the second and third sections:
    • Lines listed indicate containers of interest.
    • Fields with unset mean that aspect is not configured and should be reviewed (for example, consider setting runAsNonRoot: true, readOnlyRootFilesystem: true, allowPrivilegeEscalation: false, and dropping capabilities where possible).
  • Absence of rows in these sections means all containers have a securityContext and the inspected fields are set, but you still must review whether the chosen values are appropriate and restrictive enough.