Skip to main content

Apply Security Context To Your Pods And Containers

More Info:

Security contexts restrict privileges, capabilities, volumes, and root access for pods and containers. Apply restrictive policies broadly and scope privileged access to specific service accounts and namespaces.

Risk Level

Low

Address

Security

Compliance Standards

  • CIS OKE

Triage and Remediation

Remediation

Manual Steps
  1. Identify pods and namespaces with missing or weak securityContext

    • On any machine with kubectl access:
      # Pods missing any pod-level or container-level securityContext
      kubectl get pods --all-namespaces -o json \
      | jq -r '.items[]
      | select((.spec.securityContext // {} | length) == 0
      or ([.spec.containers[].securityContext] | map(select(. != null)) | length) == 0)
      | [.metadata.namespace, .metadata.name]
      | @tsv'
      # Pods allowing privilege escalation or running privileged / as root
      kubectl get pods --all-namespaces -o json \
      | jq -r '.items[]
      | select(
      [.spec.containers[]?][]
      | (
      (.securityContext.privileged // false) == true
      or (.securityContext.allowPrivilegeEscalation // true) == true
      or ((.securityContext.runAsUser // 0) == 0)
      )
      )
      | [.metadata.namespace, .metadata.name]
      | @tsv'
  2. Review securityContext details for suspicious pods

    • For each namespace/pod pair from step 1, inspect full spec:
      kubectl -n <namespace> get pod <pod-name> -o yaml
    • Manually check for:
      • securityContext.privileged: true
      • allowPrivilegeEscalation: true or unset
      • runAsUser: 0 or runAsNonRoot: false/unset
      • Use of hostNetwork, hostPID, hostIPC, and hostPath volumes
      • Absence of capabilities.drop: ["ALL"]
  3. Decide which workloads truly need elevated privileges and scope them

    • Classify each privileged/high‑risk pod as:
      • System-critical (needs elevated privileges) – e.g. CNI, kube-proxy, node/logging/monitoring agents. Prefer to run these in a dedicated namespace like kube-system or another clearly named “system” namespace.
      • Business application (should be restricted) – typical app workloads that should run non-root and non-privileged.
    • For system-critical pods:
      • Ensure they use dedicated service accounts and namespaces.
      • Plan to bind any future privileged policies only to those service accounts/namespaces.
  4. Define or tighten restrictive security policies for normal workloads

    • On any machine with kubectl access, create a baseline “restricted” policy object aligned with the remediation (adapted to your cluster’s API support; PodSecurityPolicy is deprecated in newer versions, so you may instead implement equivalent Pod Security Admission or OPA/Gatekeeper):
      cat << 'EOF' > restricted-psp.yaml
      apiVersion: policy/v1beta1
      kind: PodSecurityPolicy
      metadata:
      name: restricted
      annotations:
      seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'docker/default,runtime/default'
      apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
      seccomp.security.alpha.kubernetes.io/defaultProfileName: 'runtime/default'
      apparmor.security.beta.kubernetes.io/defaultProfileName: 'runtime/default'
      spec:
      privileged: false
      allowPrivilegeEscalation: false
      requiredDropCapabilities:
      - ALL
      volumes:
      - configMap
      - emptyDir
      - projected
      - secret
      - downwardAPI
      - persistentVolumeClaim
      hostNetwork: false
      hostIPC: false
      hostPID: false
      runAsUser:
      rule: MustRunAsNonRoot
      seLinux:
      rule: RunAsAny
      supplementalGroups:
      rule: MustRunAs
      ranges:
      - min: 1
      max: 65535
      fsGroup:
      rule: MustRunAs
      ranges:
      - min: 1
      max: 65535
      readOnlyRootFilesystem: false
      EOF

      kubectl apply -f restricted-psp.yaml
    • Bind this policy only to non-privileged service accounts/namespaces via RBAC (cluster roles/rolebindings) after confirming PodSecurityPolicy is enabled in your cluster.
  5. Refactor pod specs for non-privileged workloads

    • For each business/application workload identified in step 3, update the Deployment/DaemonSet/StatefulSet (not the live pod) to add safe security contexts, for example:
      kubectl -n <namespace> edit deployment <deployment-name>
      • Under spec.template.spec.securityContext and each container’s securityContext, add fields such as:
        securityContext:
        runAsNonRoot: true
        runAsUser: 1000
        allowPrivilegeEscalation: false
        capabilities:
        drop:
        - ALL
      • Remove privileged: true, hostPath volumes, hostNetwork: true, hostPID: true, hostIPC: true unless strictly required.
    • Save to trigger a rolling restart.
  6. Verify improved posture and monitor regressions

    • Re-run discovery to ensure no remaining obviously insecure pods:
      kubectl get pods --all-namespaces -o json \
      | jq -r '.items[]
      | select(
      [.spec.containers[]?][]
      | (
      (.securityContext.privileged // false) == true
      or (.securityContext.allowPrivilegeEscalation // true) == true
      or ((.securityContext.runAsUser // 0) == 0)
      )
      )
      | [.metadata.namespace, .metadata.name]
      | @tsv'
    • Optionally, enforce admission controls (Pod Security Admission “restricted” level or equivalent policy engine) to prevent future pods without appropriate security contexts from being admitted.
Using kubectl
# 1) List all namespaces
# Run on: any machine with kubectl access
kubectl get ns

Look for non-system namespaces hosting your workloads (not kube-system, kube-public, kube-node-lease).


# 2) For each relevant namespace, list pods and show basic securityContext fields
# Example for namespace "default"
kubectl get pods -n default -o custom-columns=\
NAME:.metadata.name,\
SC_POD:.spec.securityContext,\
SC_CONT:.spec.containers[*].securityContext,\
SA:.spec.serviceAccountName

Red flags in the output:

  • SC_POD=<none> and SC_CONT=<none> (no securityContext at all on pod or containers).
  • Service account is generic (e.g., default) and used by many pods that may need different privilege levels.

# 3) Inspect full spec for specific pods that look suspicious or important
# Replace <pod> and <ns>
kubectl get pod <pod> -n <ns> -o yaml

In the YAML, look for:

Pod-level (under .spec.securityContext):

  • Missing entirely (no securityContext: block).
  • fsGroup / supplementalGroups with values including 0.
  • runAsUser: 0 or runAsGroup: 0 without strong need.

Container-level (for each entry under .spec.containers[] and .spec.initContainers[]):

  • securityContext missing completely.
  • privileged: true.
  • allowPrivilegeEscalation: true or not set.
  • runAsUser: 0 or runAsNonRoot: false or not set.
  • capabilities.add with broad capabilities (or no capabilities.drop).
  • readOnlyRootFilesystem: false or not set.

Host-level access (still under each container):

  • hostNetwork: true, hostPID: true, or hostIPC: true (these are at pod spec level).
  • Volumes with hostPath: under .spec.volumes[] without strong justification.

These indicate pods/containers lack restrictive security contexts or have excessive privileges compared to the benchmark example.


# 4) Check how many pods are running privileged or allowing privilege escalation
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .spec.containers[*]}{.name}{" "}{.securityContext.privileged}{" "}{.securityContext.allowPrivilegeEscalation}{"\n"}{end}{end}' \
| sort

Concerning lines:

  • true in the privileged position.
  • true or <no value> in the allowPrivilegeEscalation position for general application workloads.

# 5) Check for containers running as root or without non-root enforcement
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .spec.containers[*]}{.name}{" "}{.securityContext.runAsUser}{" "}{.securityContext.runAsNonRoot}{"\n"}{end}{end}' \
| sort

Concerning lines:

  • 0 for runAsUser.
  • false or <no value> for runAsNonRoot on general workloads.

# 6) Check volume types per pod
kubectl get pods --all-namespaces -o jsonpath='{range .items[*]}{.metadata.namespace}{"\t"}{.metadata.name}{"\t"}{range .spec.volumes[*]}{.name}{"="}{.hostPath}{";"}{end}{"\n"}{end}' \
| sort

Concerning output:

  • Any hostPath object (not null) used by regular application pods, instead of more controlled volume types (configMap, secret, PVC, etc.), unless clearly justified (e.g., logging agents, node-level tooling).

# 7) Review service accounts bound to elevated permissions
kubectl get clusterrolebindings.rbac.authorization.k8s.io -o wide
kubectl get rolebindings.rbac.authorization.k8s.io --all-namespaces -o wide

Then for suspicious bindings (especially those granting broad or cluster-admin-like roles), inspect them:

# Example for one binding
kubectl get clusterrolebinding <name> -o yaml
kubectl get rolebinding <name> -n <ns> -o yaml

Concerning situations:

  • Service accounts outside tightly controlled namespaces (e.g., not kube-system) bound to highly privileged roles.
  • Generic service accounts (default) having elevated roles, which may be used by pods lacking restrictive security contexts.

# 8) (If using Pod Security Admission labels instead of PodSecurityPolicy) list namespace policies
kubectl get ns -o 'custom-columns=NAME:.metadata.name,PSA:.metadata.labels'

Concerning output:

  • Workload namespaces without pod-security.kubernetes.io/enforce labels.
  • Enforce levels weaker than restricted for general application namespaces (e.g., enforce=privileged or missing).
Automation
#!/usr/bin/env bash
# Purpose: Cluster-wide securityContext review for pods/containers.
# Runs with: any machine with kubectl access and cluster‑admin or equivalent.

set -euo pipefail

# 1) Basic environment info
echo "=== Cluster Info ==="
kubectl version --short || true
kubectl config current-context || true
echo

# 2) List namespaces without any Pod-level or Container-level securityContext
echo "=== Namespaces with pods missing ANY securityContext ==="
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
podSC: .spec.securityContext,
containers: (.spec.containers // []),
initContainers: (.spec.initContainers // [])
}
| select(
# pod has no pod-level securityContext
( .podSC == null )
and
# AND every container AND initContainer has no securityContext
( ([.containers[], .initContainers[]] | length == 0)
or
( [(.containers[]?, .initContainers[]?)
| select(.securityContext != null)
] | length == 0
)
)
)
| "\(.ns) \(.pod)"
' | sort -u || echo "jq not installed or no pods found"
echo

# 3) Pods/containers with obviously risky settings
echo "=== Pods/containers with risky securityContext fields ==="
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| . as $pod
| ($pod.spec.containers // []) + ($pod.spec.initContainers // [])
| map({
ns: $pod.metadata.namespace,
pod: $pod.metadata.name,
cname: .name,
sc: .securityContext
})
| .[]
| select(.sc != null)
| . as $c
| (
if ($c.sc.privileged == true) then
"PRIVILEGED|\($c.ns)|\($c.pod)|\($c.cname)"
elif ($c.sc.allowPrivilegeEscalation == true) then
"ALLOW_PRIV_ESC|\($c.ns)|\($c.pod)|\($c.cname)"
elif ($c.sc.runAsUser == 0) or ($c.sc.runAsNonRoot == false) then
"ROOT_USER|\($c.ns)|\($c.pod)|\($c.cname)"
elif ($c.sc.readOnlyRootFilesystem == false) then
"RW_ROOTFS|\($c.ns)|\($c.pod)|\($c.cname)"
else
empty
end
)
' | sort -u || echo "jq not installed or no risky containers found"
echo

# 4) Pods using host features (hostNetwork/IPC/PID) or hostPath volumes
echo "=== Pods using host features or hostPath volumes ==="
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
pod: .metadata.name,
hostNetwork: (.spec.hostNetwork // false),
hostIPC: (.spec.hostIPC // false),
hostPID: (.spec.hostPID // false),
hostPathVolumes: (
(.spec.volumes // [])
| map(select(.hostPath != null) | .name)
)
}
| select(.hostNetwork or .hostIPC or .hostPID or (.hostPathVolumes | length > 0))
| "\(.ns)|\(.pod)|hostNetwork=\(.hostNetwork)|hostIPC=\(.hostIPC)|hostPID=\(.hostPID)|hostPathVolumes=\((.hostPathVolumes | join(\",\")))"
' | sort -u || echo "jq not installed or no pods with host features"
echo

# 5) Summary of securityContext usage per namespace
echo "=== Per-namespace securityContext usage summary ==="
kubectl get pods --all-namespaces -o json \
| jq -r '
.items[]
| {
ns: .metadata.namespace,
podSC: .spec.securityContext,
containers: (.spec.containers // []),
initContainers: (.spec.initContainers // [])
}
| {
ns,
hasPodSC: (.podSC != null),
hasAnyContainerSC: (
[(.containers[]?, .initContainers[]?)
| select(.securityContext != null)
] | length > 0
)
}
| "\(.ns)|podSC=\(.hasPodSC)|containerSC=\(.hasAnyContainerSC)"
' | sort -u || echo "jq not installed or no pods"
echo

# 6) Optional: show any PodSecurity or PodSecurityPolicy objects, if present
echo "=== Cluster-level Pod security policies (if any) ==="
kubectl get psp 2>/dev/null || echo "No PodSecurityPolicy objects or PSP API disabled"
kubectl get podsecuritypolicies.policy 2>/dev/null || true
kubectl get ns --show-labels | grep -E 'pod-security|pod_security' || true

Explanation of problematic output to review manually:

  • Section “Namespaces with pods missing ANY securityContext”:

    • Any listed <namespace> <pod> means that pod and all its containers lack explicit securityContext. These are candidates to harden by adding non-root, dropping capabilities, read-only root filesystem, etc.
  • Section “Pods/containers with risky securityContext fields”:

    • Lines beginning with:
      • PRIVILEGED|... – containers running with securityContext.privileged: true (highest risk; should be tightly scoped to specific service accounts/namespaces like kube-system if truly required).
      • ALLOW_PRIV_ESC|... – containers that allow privilege escalation.
      • ROOT_USER|... – containers explicitly running as root (runAsUser: 0 or runAsNonRoot: false).
      • RW_ROOTFS|... – containers with readOnlyRootFilesystem: false (less restrictive; often acceptable but should be intentional).
  • Section “Pods using host features or hostPath volumes”:

    • Any line indicates a pod using hostNetwork, hostIPC, hostPID, or hostPath volumes, which breaks the isolation the benchmark’s example policy expects. These should be limited to well‑understood system workloads in tightly controlled namespaces.
  • Section “Per-namespace securityContext usage summary”:

    • For each namespace:
      • podSC=false|containerSC=false – all pods in that namespace lack explicit securityContext; namespace is a prime target for enforcing more restrictive defaults.
      • Namespaces with many false values should be prioritized for adding restrictive policies and explicit pod/container securityContext.

Use this script to identify where securityContext is missing or too permissive, then update manifests and apply namespace/service-account–scoped restrictive policies accordingly.