DevOps Peach

Security (DevSecOps) · Kubernetes security · Lesson 1 of 5

securityContext for Pods and containers

By default a container may run as root with many Linux capabilities; securityContext forces a normal user, blocks privilege escalation, makes the filesystem read-only, drops capabilities and enables seccomp.

Practice interactively →

The defaults are riskier than you think

If an image declares no USER, the container runs as UID 0. Containers share the node's kernel, so root in a container plus a kernel bug or a bad volume mount can lead to taking over the node. securityContext is where you narrow those rights.

Recommended configuration

apiVersion: v1
kind: Pod
metadata:
  name: web
spec:
  securityContext:                 # Pod level
    runAsNonRoot: true
    runAsUser: 10001
    fsGroup: 10001
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: web
      image: registry.example.com/web:1.0
      securityContext:             # container level
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: ["ALL"]
      volumeMounts:
        - { name: tmp, mountPath: /tmp }
  volumes:
    - { name: tmp, emptyDir: {} }
Field Effect
runAsNonRoot The kubelet refuses to start the container if it would run as UID 0
allowPrivilegeEscalation: false Stops child processes gaining more rights (SUID, setuid)
readOnlyRootFilesystem The image filesystem cannot be written; write to an emptyDir when needed
capabilities.drop: [ALL] Drop every capability; add back only what is truly needed, such as NET_BIND_SERVICE
seccompProfile: RuntimeDefault Filter dangerous system calls with the runtime's default profile

Things to avoid

Setting Why it is dangerous
privileged: true The container has almost full control of the node
hostNetwork, hostPID, hostIPC It sees and affects the node's processes and network
hostPath mounts of / or /var/run/docker.sock Escaping to the node becomes easy
capabilities.add: [SYS_ADMIN] Close to root on the node
flowchart LR
  A["Attacker gets into the app"] --> B{"securityContext"}
  B -- "root, privileged, hostPath" --> N["Takes over node and cluster"]
  B -- "non-root, read-only, drop ALL" --> L["Confined to the container"]

Quick checks

kubectl exec web -- id                         # uid=10001 ...
kubectl get pod web -o jsonpath='{.spec.containers[0].securityContext}'

Traps: setting runAsNonRoot: true when the image only has a root user, so the Pod shows CreateContainerConfigError; enabling read-only and breaking an app that writes /tmp without an emptyDir; thinking drop: [ALL] is enough while privileged: true is still set.

Practice interactively →