⌂ Home

Security Context Hierarchy

Understanding Pod-Level vs Container-Level Security Settings

Repository YAML Files:

Security Context Hierarchy Flow

🛡️ Pod-Level Security Context

Applied to all containers in the Pod (spec.securityContext)

runAsUser
User ID for all containers
runAsGroup
Group ID for all containers
fsGroup
Volume ownership group
fsGroupChangePolicy
How to apply fsGroup
supplementalGroups
Additional group IDs
sysctls
Kernel parameters
Container overrides apply
📦 Container-Level Security Context

Applied to specific containers (containers[].securityContext)

runAsUser
Overrides Pod-level user
runAsGroup
Overrides Pod-level group
capabilities
Add/drop Linux capabilities
privileged
Run in privileged mode
allowPrivilegeEscalation
Allow privilege escalation
readOnlyRootFilesystem
Make root filesystem read-only
runAsNonRoot
Enforce non-root execution
seccompProfile
Seccomp profile settings
seLinuxOptions
SELinux context
Key Rule: When both Pod and Container security contexts define the same setting (e.g., runAsUser), the Container-level setting always wins. This allows per-container customization while maintaining Pod-wide defaults.

Security Context Settings Comparison

Setting Pod-Level Container-Level Override Behavior
runAsUser Applies to all Per-container Container Overrides
runAsGroup Applies to all Per-container Container Overrides
runAsNonRoot Enforced on all Per-container Container Overrides
fsGroup Volume ownership Not available Pod-level only
supplementalGroups Additional GIDs Not available Pod-level only
sysctls Kernel parameters Not available Pod-level only
capabilities Not available Add/drop caps Container-level only
privileged Not available Privileged mode Container-level only
allowPrivilegeEscalation Not available Control escalation Container-level only
readOnlyRootFilesystem Not available Read-only root Container-level only
seccompProfile Pod-wide profile Per-container Container Overrides
seLinuxOptions Pod-wide SELinux Per-container Container Overrides
Important: Some settings like fsGroup and capabilities are exclusive to their respective levels. You cannot set fsGroup at the container level or capabilities at the pod level.

Practical YAML Examples

Example 1: Pod-Level Security Context

Pod-level settings apply to all containers
apiVersion: v1 kind: Pod metadata: name: security-context-demo spec: securityContext: runAsUser: 1000 # All containers run as UID 1000 runAsGroup: 3000 # All containers run as GID 3000 fsGroup: 2000 # Volumes owned by GID 2000 supplementalGroups: - 4000 # Additional group membership volumes: - name: data-volume emptyDir: {} containers: - name: app image: busybox:1.28 command: ["sh", "-c", "sleep 1h"] volumeMounts: - name: data-volume mountPath: /data securityContext: allowPrivilegeEscalation: false # Container-specific

Result: The container runs as UID 1000, GID 3000, with supplemental group 4000. Files in /data are owned by GID 2000. Privilege escalation is disabled.

Example 2: Container-Level Override

Container settings override Pod-level defaults
apiVersion: v1 kind: Pod metadata: name: multi-container-security spec: securityContext: runAsUser: 1000 # Default for all containers fsGroup: 2000 # Volume ownership containers: - name: app image: nginx:1.21 # Uses Pod-level runAsUser: 1000 securityContext: readOnlyRootFilesystem: true capabilities: drop: - ALL # Drop all capabilities add: - NET_BIND_SERVICE # Allow binding to privileged ports - name: sidecar image: busybox:1.28 command: ["sh", "-c", "sleep 1h"] securityContext: runAsUser: 2000 # OVERRIDES Pod-level 1000 runAsNonRoot: true allowPrivilegeEscalation: false

Result: The "app" container runs as UID 1000 (from Pod), while "sidecar" runs as UID 2000 (override). Both share fsGroup 2000 for volumes. Each has distinct capability and privilege settings.

Interactive Security Context Views

Pod securityContext spec.securityContext runAsUser: 1000 runAsGroup: 3000 fsGroup: 2000 Container Overrides Apply Container: app containers[0].securityContext ✓ Inherited from Pod runAsUser: 1000 runAsGroup: 3000 fsGroup: 2000 Container-Specific Settings allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop [ALL] add [NET_BIND_SERVICE] Container: sidecar containers[1].securityContext ⚠ OVERRIDES POD DEFAULTS ✗ Overrides Pod Settings runAsUser: 2000 (was 1000) runAsGroup: 4000 (was 3000) fsGroup still 2000 (Pod-only) Container-Specific Settings runAsNonRoot: true allowPrivilegeEscalation: false

Key Insight: Container settings always override Pod settings for the same field. Some settings (fsGroup, sysctls) are Pod-only; others (capabilities, privileged) are Container-only.

User/Group ID Enforcement Flow Pod Specification spec.securityContext: runAsUser: 1000 runAsGroup: 3000 fsGroup: 2000 supplementalGroups: [4000] Apply to Process Running Process $ id uid=1000 gid=3000 groups=2000,3000,4000 Process runs as UID 1000 Member of GIDs 2000, 3000, 4000 Access Files Volume Filesystem $ ls -l /data drwxrwsr-x 2 root 2000 4096 file1.txt -rw-rw-r-- 1 1000 2000 512 file2.txt -rw-rw-r-- 1 1000 3000 1024 file3.txt fsGroup: 2000 owns volumes How fsGroup Works 1. Kubernetes sets volume ownership to fsGroup (2000) 2. Process (UID 1000) is member of GID 2000, so it can read/write 3. All containers in the Pod share the same fsGroup for volume access

Validation: Use kubectl exec pod-name -- id to verify user/group IDs are applied correctly.

Linux Capabilities: Add vs Drop Default Container Capabilities (No securityContext specified) CAP_CHOWN CAP_DAC_OVERRIDE CAP_FOWNER CAP_SETGID CAP_SETUID CAP_NET_BIND_SERVICE + 8 more... Drop ALL Capabilities capabilities.drop: [ALL] No Capabilities Minimal attack surface Security Hardening Best Practice: Drop ALL + Add Selective securityContext: capabilities: drop: - ALL add: - NET_BIND_SERVICE # Only allows binding to ports < 1024 # All other capabilities dropped

Security Tip: Always start with drop: [ALL] and add only required capabilities. Never use privileged: true unless absolutely necessary.

Best Practices & Security Guidance

1. Start with Pod-Level Defaults
Set secure defaults at the Pod level (e.g., runAsUser: 1000, fsGroup: 2000) that apply to all containers. Override only when specific containers need different settings.
2. Use Container-Level for Specific Requirements
Apply container-level settings for capabilities, privileged mode, or when a specific container needs to run as a different user. This maintains least-privilege while allowing necessary flexibility.
3. Always Disable Privilege Escalation
Set allowPrivilegeEscalation: false at the container level unless absolutely required. This prevents processes from gaining more privileges than their parent.
4. Avoid Running as Root
Use runAsNonRoot: true to enforce non-root execution. Combine with runAsUser to specify a non-root UID (e.g., 1000). Kubernetes will refuse to start containers that violate this policy.
5. Drop Unnecessary Capabilities
Use capabilities.drop: [ALL] and selectively add only required capabilities (e.g., NET_BIND_SERVICE). This reduces the attack surface significantly.
6. Validation Commands
After deploying, verify security context settings:
kubectl exec <pod> -- id - Check user/group IDs
kubectl exec <pod> -- ls -l /path - Verify file ownership
kubectl describe pod <pod> - Review applied security context