Black Friday · 27 Nov 2026

Up to 90% off selected certifications.

One time only offer for a very precious career goal. Limited coupons, waitlist first.

Free lesson · CKAD Prep Program · Module 5: Environment, configuration and security

Drop Privileges with Security Contexts

Default containers run as root with a full capability set, which turns any escape into a host compromise. Security contexts strip that down to exactly what the app needs.

12 min reading

Objectives

  • Run containers as non-root users with explicit IDs
  • Drop capabilities and forbid privilege escalation per container
  • Set pod-level fsGroup plus sysctls where the app needs them
  • Read a denied syscall back to the missing context field

Why this matters

Container breakout severity equals container privilege, so least privilege is the cheapest security control in the cluster. The CKAD tests the fields directly because they are pure YAML with immediate enforceable effects: runAsUser, capabilities, readOnlyRootFilesystem, allowPrivilegeEscalation. Exams love them for the same reason attackers hate them.

Concepts

Pod-level context sets shared defaults: runAsUser plus runAsGroup for all containers, fsGroup so mounted volumes arrive group-writable, and selected sysctls. Container-level context overrides per container and carries the sharp tools: capabilities drop (ALL, then add back only NET_BIND_SERVICE or whatever the app proves it needs), allowPrivilegeEscalation false (blocks setuid paths to root), readOnlyRootFilesystem true (with emptyDir mounts for the scratch paths the app genuinely writes), and runAsNonRoot true (refuses images that only run as uid 0).

Denials read back cleanly: permission errors on writes mean the filesystem or user is wrong, capability errors name the missing capability in audit or dmesg, and CrashLoops at start with runAsNonRoot set mean the image demands root and must be fixed or replaced.

Worked example

An nginx-shaped app, stripped to its needs:

securityContext:
  runAsUser: 10001
  runAsGroup: 30001
  fsGroup: 30001
containers:
- name: web
  securityContext:
    allowPrivilegeEscalation: false
    readOnlyRootFilesystem: true
    runAsNonRoot: true
    capabilities:
      drop: [ALL]
      add: [NET_BIND_SERVICE]
  volumeMounts:
  - { name: tmp, mountPath: /tmp }
  - { name: cache, mountPath: /var/cache/nginx }
volumes:
- { name: tmp, emptyDir: {} }
- { name: cache, emptyDir: {} }

kubectl exec as uid 10001 proves the identity; writing outside the mounts fails by design; binding port 80 works through the single kept capability. Every allowance traces to a need, nothing to habit.

Exam notes

readOnlyRootFilesystem breaks apps that write config at boot: add the exact scratch mounts instead of dropping the flag. Dropping ALL capabilities kills features silently (binding low ports, raw sockets, chown): add back one at a time from the error. And runAsUser without fsGroup leaves volume writes owned by root: pair them whenever the app writes to mounts.

Quick check

A 4-question self-check. Your answers stay on your device, are never stored or sent. Only a passing score (for example 3 of 4) is saved, and only to unlock lesson progress: at least 3 correct is required before a lesson counts as complete. It never counts toward any certification assessment.

Lesson feedback

No published feedback yet.

Log in and complete the lesson to leave feedback.

Exercise

Harden a fictional root-running app manifest with pod plus container contexts (non-root user, dropped caps with one justified add-back, read-only root with scratch mounts, no escalation). Show the exec proof of identity plus one designed denial with its meaning.

Pass criteria

Contexts complete at both levels with every allowance justified; exec proves non-root; the denial output is quoted with the correct field-level reading.

Sources

Log in to track progressFree account: stores only your lesson progress and quiz results.