Skip to main content

Apply Security Context To Pods And Containers

More Info:

Security contexts constrain the privileges and access of pods and containers at runtime. They should be applied following the CIS Google Container-Optimized OS Benchmark guidance.

Risk Level

Medium

Address

Security

Compliance Standards

  • CIS GKE

Triage and Remediation

Remediation

Manual Steps
  1. List pods and identify targets (any machine with kubectl access)

    kubectl get pods -A -o wide

    Decide which namespaces/pods are in scope (exclude managed system namespaces only if your policy allows, e.g. kube-system, gke-system, etc., after confirming provider ownership).

  2. Review pod- and container-level securityContext usage (any machine with kubectl access)
    For each in-scope namespace, export full specs and check for missing or weak securityContext settings:

    # Example for one namespace
    kubectl get pods -n <namespace> -o yaml > pods-<namespace>.yaml

    In the YAML, look for:

    • Pod-level: .spec.securityContext
    • Container-level for each container and initContainer: .spec.containers[].securityContext and .spec.initContainers[].securityContext
      Note pods/containers where securityContext is absent or obviously over-privileged (e.g. privileged: true, runAsUser: 0, allowPrivilegeEscalation: true, readOnlyRootFilesystem: false, capabilities.add with broad capabilities).
  3. Classify pods by required privilege (manual decision)
    For each application/team:

    • Determine if it truly needs root, host access, or additional Linux capabilities (e.g. CNI, CSI, monitoring agents may require more privileges; most apps do not).
    • Group pods into: (A) can run as non-root, (B) need limited extra privileges, (C) must remain highly privileged (with explicit business/technical justification).
  4. Design appropriate securityContext settings per group (manual decision, guided by CIS GKE / COS)
    For each group, decide the target baseline, for example:

    • Common baseline for A/B (tightest feasible):
      • runAsNonRoot: true
      • runAsUser: <non-root UID>
      • allowPrivilegeEscalation: false
      • readOnlyRootFilesystem: true (if writable paths can move to volumes)
      • capabilities.drop: ["ALL"] and only minimal capabilities.add if needed
      • seccompProfile: type: RuntimeDefault (or Localhost profile if you maintain one)
    • For group B/C, document each exception (e.g. privileged: true, hostPath mounts, NET_ADMIN, SYS_ADMIN, etc.) with rationale and compensating controls.
  5. Update owning manifests and re-apply (any machine with kubectl access)

    • Locate the source manifests/Helm charts/Kustomize for the identified pods (Git repo, CI pipeline, or local files).
    • Edit them to add or refine securityContext at pod and/or container level, following the design in step 4. Example fragment for a container securityContext:
      securityContext:
      runAsNonRoot: true
      runAsUser: 1000
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
      drop: ["ALL"]
      seccompProfile:
      type: RuntimeDefault
    • Re-deploy via your normal workflow, e.g.:
      kubectl apply -f <updated-manifest>.yaml
    • Coordinate with app owners to run smoke tests; watch for failures due to tightened permissions and adjust only where strictly necessary.
  6. Verify and document residual risk (any machine with kubectl access)
    Re-run the inspection and confirm security contexts are present and aligned with your decisions:

    kubectl get pods -A -o yaml | grep -E "securityContext|privileged|allowPrivilegeEscalation|runAsUser|runAsNonRoot|capabilities|seccompProfile" -n

    For any remaining highly privileged pods in group C, record:

    • Namespace, pod name, containers
    • Required elevated settings
    • Business justification and review/expiry date
      Keep this as part of your exception register and periodically repeat steps 2–6.
Using kubectl
# 1) List all pods and their service accounts (for review scope)
# Run on: any machine with kubectl access
kubectl get pods -A -o custom-columns='NAMESPACE:.metadata.namespace,NAME:.metadata.name,SA:.spec.serviceAccountName'

# 2) Show securityContext at pod and container level for a specific pod
# Replace <namespace> and <pod-name> with values from the previous command
kubectl get pod <pod-name> -n <namespace> -o yaml | \
sed -n '/^spec:/,/^status:/p' | \
egrep -A3 'securityContext|containers:|name:'

# 3) List pods that have no pod-level securityContext
kubectl get pods -A -o json | \
jq -r '
.items[]
| select(.spec.securityContext == null)
| [.metadata.namespace, .metadata.name]
| @tsv
'

# 4) List containers that lack a container-level securityContext
kubectl get pods -A -o json | \
jq -r '
.items[]
| . as $pod
| $pod.spec.containers[]
| select(.securityContext == null)
| [$pod.metadata.namespace, $pod.metadata.name, .name]
| @tsv
'

# 5) Surface pods/containers that are explicitly privileged
kubectl get pods -A -o json | \
jq -r '
.items[]
| . as $pod
| $pod.spec.containers[]
| select(.securityContext.privileged == true)
| [$pod.metadata.namespace, $pod.metadata.name, .name, "privileged=true"]
| @tsv
'

# 6) Surface pods/containers that can escalate privileges
kubectl get pods -A -o json | \
jq -r '
.items[]
| . as $pod
| $pod.spec.containers[]
| select(.securityContext.allowPrivilegeEscalation != false)
| [$pod.metadata.namespace, $pod.metadata.name, .name, "allowPrivilegeEscalation!=false"]
| @tsv
'

# 7) Surface pods/containers that run as root (where explicitly set)
kubectl get pods -A -o json | \
jq -r '
.items[]
| . as $pod
| (
if $pod.spec.securityContext.runAsUser == 0
then [$pod.metadata.namespace, $pod.metadata.name, "POD", "runAsUser=0"]
else empty
end
),
(
$pod.spec.containers[]
| select(.securityContext.runAsUser == 0)
| [$pod.metadata.namespace, $pod.metadata.name, .name, "runAsUser=0"]
)
| @tsv
'

# 8) Surface pods without readOnlyRootFilesystem set to true
kubectl get pods -A -o json | \
jq -r '
.items[]
| . as $pod
| $pod.spec.containers[]
| select(.securityContext.readOnlyRootFilesystem != true)
| [$pod.metadata.namespace, $pod.metadata.name, .name, "readOnlyRootFilesystem!=true"]
| @tsv
'

Interpretation / what indicates a problem (requires human judgement):

  • Pods/containers listed by commands 3 and 4: no securityContext at pod or container level; review against your policy and the CIS guidance to decide if this is acceptable.
  • Entries from command 5: privileged=true almost always needs strong justification; likely non-compliant.
  • Entries from command 6: allowPrivilegeEscalation!=false means privilege escalation is allowed; typically should be set to false unless there is a clear need.
  • Entries from command 7: runAsUser=0 means explicitly running as root; generally undesirable except for carefully reviewed system workloads.
  • Entries from command 8: readOnlyRootFilesystem!=true means the root filesystem is writable; for many workloads CIS-like guidance prefers readOnlyRootFilesystem: true unless write access is required.

Use the detailed kubectl get pod ... -o yaml (command 2) for any flagged pod to review all securityContext fields in context (capabilities, seccompProfile, runAsNonRoot, fsGroup, etc.) and then decide specific changes in your manifests.

Automation
#!/usr/bin/env bash
#
# Report pods and containers without recommended securityContext settings.
# Run on: any machine with kubectl access
#
# Requirements: kubectl, jq

set -euo pipefail

# Namespace selector (empty = all)
NAMESPACE_SELECTOR="${1:-}"

if [[ -n "$NAMESPACE_SELECTOR" ]]; then
NS_ARG="-n $NAMESPACE_SELECTOR"
echo "Scanning namespace: $NAMESPACE_SELECTOR" >&2
else
NS_ARG="--all-namespaces"
echo "Scanning all namespaces" >&2
fi

# Fields we’ll check (aligned with common CIS guidance)
cat <<'HDR'
=== Security Context Report (per container) ===
Fields:
- ns : Namespace
- pod : Pod name
- ctr : Container name
- t : Type (C=container, I=initContainer)
- pr : privileged (true/false/empty)
- capAdd : Linux capabilities added (comma-separated)
- capDrop : Linux capabilities dropped (comma-separated)
- roFs : readOnlyRootFilesystem (true/false/empty)
- runAsNonRoot : runAsNonRoot (true/false/empty; pod or container)
- runAsUser : runAsUser (value/empty; pod or container)
- seLinux : SELinux options present (yes/no)
- allowPrivEsc : allowPrivilegeEscalation (true/false/empty)

POTENTIAL PROBLEMS (to review):
- pr=true
- allowPrivEsc=true or empty
- roFs=false or empty
- runAsNonRoot=false or empty AND runAsUser is 0 or empty
- capAdd has any value
- capDrop is empty
- seLinux=no
HDR

# Dump all pods with all fields, then process via jq
kubectl get pods ${NS_ARG} -o json | \
jq -r '
.items[]
| .metadata as $m
| .spec as $s
| .spec.securityContext as $psc
| [
# build an array of all containers and initContainers with type marker
(
( .spec.containers // [] | map(. + { _type: "C" }) ) +
( .spec.initContainers? // [] | map(. + { _type: "I" }) )
)[]
| . as $ctr
| $ctr.securityContext as $csc

# pod-level fields (can be inherited)
| $psc.runAsNonRoot as $podRunAsNonRoot
| $psc.runAsUser as $podRunAsUser
| $psc.seLinuxOptions as $podSeLinux

# container-level fields
| $csc.runAsNonRoot as $ctrRunAsNonRoot
| $csc.runAsUser as $ctrRunAsUser
| $csc.seLinuxOptions as $ctrSeLinux

# effective runAs* (container overrides pod)
| ($ctrRunAsNonRoot // $podRunAsNonRoot) as $effRunAsNonRoot
| ($ctrRunAsUser // $podRunAsUser) as $effRunAsUser
| ($ctrSeLinux // $podSeLinux) as $effSeLinux

# capabilities
| ($csc.capabilities.add // []) as $capAdd
| ($csc.capabilities.drop // []) as $capDrop

# other container-level fields
| $csc.privileged as $priv
| $csc.readOnlyRootFilesystem as $roFs
| $csc.allowPrivilegeEscalation as $allowPE

# derive simple flags
| ($effSeLinux | if . == null then "no" else "yes" end) as $seLinuxFlag
| ($capAdd | join(",")) as $capAddStr
| ($capDrop | join(",")) as $capDropStr

# output TSV line
| [
$m.namespace,
$m.name,
$ctr.name,
$ctr._type,
(if $priv == null then "" else $priv | tostring end),
$capAddStr,
$capDropStr,
(if $roFs == null then "" else $roFs | tostring end),
(if $effRunAsNonRoot == null then "" else $effRunAsNonRoot | tostring end),
(if $effRunAsUser == null then "" else $effRunAsUser | tostring end),
$seLinuxFlag,
(if $allowPE == null then "" else $allowPE | tostring end)
] | @tsv
]
' | \
awk '
BEGIN {
OFS="\t";
print "ns","pod","ctr","t","pr","capAdd","capDrop","roFs","runAsNonRoot","runAsUser","seLinux","allowPrivEsc","ISSUES";
}
{
ns=$1; pod=$2; ctr=$3; t=$4;
pr=$5; capAdd=$6; capDrop=$7; roFs=$8;
runAsNonRoot=$9; runAsUser=$10; seLinux=$11; allowPE=$12;

issues="";

# privileged
if (pr == "true") {
issues = issues "[privileged=true];";
}

# allowPrivilegeEscalation
if (allowPE == "true" || allowPE == "") {
issues = issues "[allowPrivilegeEscalation not false];";
}

# readOnlyRootFilesystem
if (roFs == "" || roFs == "false") {
issues = issues "[readOnlyRootFilesystem not true];";
}

# runAsNonRoot / runAsUser
if (runAsNonRoot == "" || runAsNonRoot == "false") {
if (runAsUser == "" || runAsUser == "0") {
issues = issues "[runs as root or not proven non-root];";
}
}

# capabilities
if (capAdd != "") {
issues = issues "[capabilities added: " capAdd "];";
}
if (capDrop == "") {
issues = issues "[no capabilities dropped];";
}

# SELinux
if (seLinux == "no") {
issues = issues "[no SELinux options];";
}

print ns,pod,ctr,t,pr,capAdd,capDrop,roFs,runAsNonRoot,runAsUser,seLinux,allowPE,issues;
}
'

cat <<'FOOT'

INTERPRETING OUTPUT:
- Each row is a single container or initContainer.
- The ISSUES column summarizes what needs review.

Examples of rows needing attention:
- ISSUES contains [privileged=true] -> container is privileged.
- ISSUES contains [allowPrivilegeEscalation not false] -> not explicitly disabled.
- ISSUES contains [readOnlyRootFilesystem not true] -> root FS writable.
- ISSUES contains [runs as root or not proven non-root]-> runAsNonRoot/runAsUser not set safely.
- ISSUES contains [capabilities added: ...] -> extra Linux capabilities granted.
- ISSUES contains [no capabilities dropped] -> consider dropping ALL or unneeded caps.
- ISSUES contains [no SELinux options] -> no SELinux confinement configured.

Use this report to:
- Identify pods/containers where securityContext is missing or too permissive.
- Prioritize high‑risk cases (privileged, root user, privilege escalation).
- Manually adjust pod specs/manifests according to your security policy and CIS guidance.
FOOT