Security context and container hardening

Security context and container hardening

A container is a process on the node. The security context decides how much of the node that process can touch.

spec:
  securityContext:                    # Pod level - applies to all containers
    runAsNonRoot: true
    runAsUser: 10001
    fsGroup: 10001
    seccompProfile: { type: RuntimeDefault }
  containers:
    - name: app
      image: nginxinc/nginx-unprivileged:1.27   # listens on 8080
      securityContext:                # container level - wins over Pod level
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: ["ALL"]
      volumeMounts:
        - { name: tmp, mountPath: /tmp }        # writable scratch space
  volumes:
    - { name: tmp, emptyDir: {} }
SettingProtects against
runAsNonRoot / runAsUserroot in the container = root-owned files on the node if it escapes
allowPrivilegeEscalation: falsesetuid binaries, sudo inside
readOnlyRootFilesystem: trueattacker dropping tools or changing the app
capabilities.drop: [ALL]raw sockets, changing ownership, binding ports < 1024
seccompProfile: RuntimeDefault~300 rarely needed syscalls
never privileged: true, hostPID, hostNetwork, hostPath: /these are the node

Container hardening checklist

  • Minimal base image (distroless, alpine, chiselled) - fewer CVEs, no shell.
  • Image built to run as non-root (USER 10001 in the Dockerfile).
  • Pinned tag or digest (@sha256:...), never latest.
  • Scanned in the pipeline (Trivy - Lab 33), signed (cosign).
  • Resource limits set (Day 3) - a runaway container cannot starve the node.
  • Only the ServiceAccount token it needs (usually none).

Many public images (nginx, httpd) expect root. Use their unprivileged variants or build your own.

References