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.