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
Remediation
Manual Steps
-
Identify pods and namespaces with missing or weak securityContext
- On any machine with kubectl access:
# Pods missing any pod-level or container-level securityContextkubectl get pods --all-namespaces -o json \| jq -r '.items[]| select((.spec.securityContext // {} | length) == 0or ([.spec.containers[].securityContext] | map(select(. != null)) | length) == 0)| [.metadata.namespace, .metadata.name]| @tsv'# Pods allowing privilege escalation or running privileged / as rootkubectl get pods --all-namespaces -o json \| jq -r '.items[]| select([.spec.containers[]?][]| ((.securityContext.privileged // false) == trueor (.securityContext.allowPrivilegeEscalation // true) == trueor ((.securityContext.runAsUser // 0) == 0)))| [.metadata.namespace, .metadata.name]| @tsv'
- On any machine with kubectl access:
-
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: trueallowPrivilegeEscalation: trueor unsetrunAsUser: 0orrunAsNonRoot: false/unset- Use of
hostNetwork,hostPID,hostIPC, andhostPathvolumes - Absence of
capabilities.drop: ["ALL"]
- For each namespace/pod pair from step 1, inspect full spec:
-
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-systemor another clearly named “system” namespace. - Business application (should be restricted) – typical app workloads that should run non-root and non-privileged.
- System-critical (needs elevated privileges) – e.g. CNI, kube-proxy, node/logging/monitoring agents. Prefer to run these in a dedicated namespace like
- 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.
- Classify each privileged/high‑risk pod as:
-
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.yamlapiVersion: policy/v1beta1kind: PodSecurityPolicymetadata:name: restrictedannotations: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: falseallowPrivilegeEscalation: falserequiredDropCapabilities:- ALLvolumes:- configMap- emptyDir- projected- secret- downwardAPI- persistentVolumeClaimhostNetwork: falsehostIPC: falsehostPID: falserunAsUser:rule: MustRunAsNonRootseLinux:rule: RunAsAnysupplementalGroups:rule: MustRunAsranges:- min: 1max: 65535fsGroup:rule: MustRunAsranges:- min: 1max: 65535readOnlyRootFilesystem: falseEOFkubectl 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.
- 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):
-
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.securityContextand each container’ssecurityContext, add fields such as:securityContext:runAsNonRoot: truerunAsUser: 1000allowPrivilegeEscalation: falsecapabilities:drop:- ALL - Remove
privileged: true,hostPathvolumes,hostNetwork: true,hostPID: true,hostIPC: trueunless strictly required.
- Under
- Save to trigger a rolling restart.
- 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:
-
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) == trueor (.securityContext.allowPrivilegeEscalation // true) == trueor ((.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.
- Re-run discovery to ensure no remaining obviously insecure pods:
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>andSC_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/supplementalGroupswith values including0.runAsUser: 0orrunAsGroup: 0without strong need.
Container-level (for each entry under .spec.containers[] and .spec.initContainers[]):
securityContextmissing completely.privileged: true.allowPrivilegeEscalation: trueor not set.runAsUser: 0orrunAsNonRoot: falseor not set.capabilities.addwith broad capabilities (or nocapabilities.drop).readOnlyRootFilesystem: falseor not set.
Host-level access (still under each container):
hostNetwork: true,hostPID: true, orhostIPC: 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:
truein theprivilegedposition.trueor<no value>in theallowPrivilegeEscalationposition 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:
0forrunAsUser.falseor<no value>forrunAsNonRooton 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
hostPathobject (notnull) 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/enforcelabels. - Enforce levels weaker than
restrictedfor general application namespaces (e.g.,enforce=privilegedor 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 explicitsecurityContext. These are candidates to harden by adding non-root, dropping capabilities, read-only root filesystem, etc.
- Any listed
-
Section “Pods/containers with risky securityContext fields”:
- Lines beginning with:
PRIVILEGED|...– containers running withsecurityContext.privileged: true(highest risk; should be tightly scoped to specific service accounts/namespaces likekube-systemif truly required).ALLOW_PRIV_ESC|...– containers that allow privilege escalation.ROOT_USER|...– containers explicitly running as root (runAsUser: 0orrunAsNonRoot: false).RW_ROOTFS|...– containers withreadOnlyRootFilesystem: false(less restrictive; often acceptable but should be intentional).
- Lines beginning with:
-
Section “Pods using host features or hostPath volumes”:
- Any line indicates a pod using
hostNetwork,hostIPC,hostPID, orhostPathvolumes, which breaks the isolation the benchmark’s example policy expects. These should be limited to well‑understood system workloads in tightly controlled namespaces.
- Any line indicates a pod using
-
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
falsevalues should be prioritized for adding restrictive policies and explicit pod/container securityContext.
- For each namespace:
Use this script to identify where securityContext is missing or too permissive, then update manifests and apply namespace/service-account–scoped restrictive policies accordingly.