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
Remediation
Manual Steps
-
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'
-
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
-
Review current securityContext settings
- Open each
pod-*.yamland 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.
- Missing
- Identify pods/containers that run with unnecessary or overly broad privileges relative to their function.
- Open each
-
Decide appropriate securityContext per workload
- For each pod/container, decide:
- The minimal user/group IDs (
runAsUser,runAsGroup,runAsNonRoot: true) - Whether
privilegedis truly needed; if not, setprivileged: false - Whether
allowPrivilegeEscalationcan be disabled:allowPrivilegeEscalation: false - Required and removable Linux capabilities (
capabilities.dropandcapabilities.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).
- The minimal user/group IDs (
- Use your application and Docker image requirements plus the CIS Docker Benchmark as guidance.
- For each pod/container, decide:
-
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
securityContextat pod spec and container spec levels according to step 4. Example pattern (adapt as needed for each workload):spec:securityContext:runAsNonRoot: truerunAsUser: 1000runAsGroup: 1000containers:- name: <container-name>image: <image>securityContext:allowPrivilegeEscalation: falseprivileged: falsereadOnlyRootFilesystem: truecapabilities:drop: ["ALL"] - Apply changes:
kubectl apply -f <manifest-file>.yaml
- Edit the deployment/statefulset/daemonset/job manifests (not the pods directly) to add/adjust
- 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
securityContextblocks as decided in step 4; save to recreate the pod.
- Edit directly:
- If workloads are managed by manifests/Helm/Kustomize:
-
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
securityContextvalues match your decisions from step 4 and that applications still function as expected.
- Re-run the evidence command and confirm no workload you intend to secure is listed:
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_SCcolumn 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):
securityContextis missing (null/[]) or{}for workloads that handle sensitive data or run user code.- Known risky patterns, for example:
"privileged": true"runAsUser": 0or"runAsNonRoot": falsewithout 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": trueat 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
securityContextat both.spec.securityContextand.spec.containers[].securityContext. - Any of:
privileged: truerunAsNonRoot: falseor absent, combined withrunAsUser: 0allowPrivilegeEscalation: trueor absent when the container has extra capabilitiescapabilities.addincluding unnecessary or broad capabilities (e.g.,SYS_ADMIN,NET_ADMIN,ALL)hostNetwork: true,hostPID: true,hostIPC: truewithout a strict operational reasonreadOnlyRootFilesystem: falseor 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_SCindicates a pod container with nosecurityContextdefined 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
unsetmean that aspect is not configured and should be reviewed (for example, consider settingrunAsNonRoot: true,readOnlyRootFilesystem: true,allowPrivilegeEscalation: false, and dropping capabilities where possible).
- Absence of rows in these sections means all containers have a
securityContextand the inspected fields are set, but you still must review whether the chosen values are appropriate and restrictive enough.