The Kubernetes securityContext says what a container may do on the node: which user it runs as and which powers of root it keeps. Most fields are unset by default, and an unset field often means the least safe value.
A manifest with a safe baseline, which most application pods can use as is:
apiVersion: v1
kind: Pod
metadata:
name: app
spec:
securityContext: # pod level: applies to every container
runAsNonRoot: true
runAsUser: 10001
runAsGroup: 10001
fsGroup: 10001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: registry.example.com/app:1.4.2
securityContext: # container level: wins over pod level
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop: ["ALL"]This matches the Restricted Pod Security Standard. An app that writes files needs an emptyDir mounted where it writes, because of readOnlyRootFilesystem.
Pod level and container level
Some fields exist in both places: runAsUser, runAsGroup, runAsNonRoot, seLinuxOptions, seccompProfile and appArmorProfile. The container value wins when both are set. fsGroup, fsGroupChangePolicy, supplementalGroups and sysctls exist only at pod level. privileged, allowPrivilegeEscalation, capabilities, procMount and readOnlyRootFilesystem exist only at container level.
Every field, its safe value and the rule behind it
| Field | Safe value | What goes wrong | Rule |
|---|---|---|---|
| runAsNonRoot | true | The container may start as root | Non-root containers |
| runAsUser, runAsGroup | Not 0 | 0 is root | Non-root containers |
| privileged | false | Every device of the node | Privileged containers |
| allowPrivilegeEscalation | false | setuid binaries can gain root | Privileged containers |
| capabilities | drop ALL, add only NET_BIND_SERVICE | SYS_ADMIN and NET_RAW give root’s powers | Insecure capabilities |
| seccompProfile.type | RuntimeDefault or Localhost | Unconfined allows every syscall | Seccomp profile |
| appArmorProfile.type | RuntimeDefault or Localhost | Unconfined turns AppArmor off | AppArmor |
| seLinuxOptions | Unset, or a container type | A custom user or role breaks SELinux isolation | SELinux |
| procMount | Default | Unmasked shows kernel interfaces in /proc | procMount |
| sysctls | Only the safe list | Unsafe ones change the node for every pod | Sysctls |
| readOnlyRootFilesystem | true | An attacker can write tools into the container | – |
Pod spec keys that matter as much
These live next to securityContext in the pod spec, and Pod Security Standards check them too:
| Key | Safe value | Rule |
|---|---|---|
| hostNetwork, hostPID, hostIPC | false | Host namespaces |
| ports[].hostPort | Unset or 0 | hostPort |
| volumes[].hostPath | Not used | hostPath volumes |
| volumes | Eight allowed types | Volume types |
Check it before deploy
Pod Security Admission enforces Baseline or Restricted per namespace with one label:
kubectl label namespace app pod-security.kubernetes.io/enforce=restrictedThe Cloud (IaC) Security plugin for IntelliJ IDEA flags every rule in the tables while you edit the manifest. More on cluster setup: Kubernetes security best practices and the security guides.