Stage 5 · Platform
Security, Identity & Admission
Runtime Hardening
seccomp, AppArmor, read-only filesystems, Linux capabilities, and non-root containers.
Runtime Security Layers
Runtime security prevents container escape and privilege escalation. Layers: seccomp (syscall filtering), AppArmor (mandatory access control), capabilities (privilege reduction), read-only filesystem (write prevention), and non-root execution (privilege restriction).
- seccomp — Filter allowed system calls (Linux kernel feature)
- AppArmor — Restrict file access, network, and capabilities
- Capabilities — Drop unnecessary Linux privileges
- Read-only root filesystem — Prevent writes to container filesystem
- Non-root — Run containers as unprivileged user
seccomp Profiles
seccomp (Secure Computing Mode) filters system calls a container can make. RuntimeDefault uses the container runtime's default profile. Localhost loads a custom profile from the node. Unconfined disables filtering.
apiVersion: v1
kind: Pod
metadata:
name: seccomp-pod
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: my-app:1.0
---
# Custom seccomp profile
apiVersion: v1
kind: Pod
metadata:
name: custom-seccomp
spec:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: profiles/app-profile.json
containers:
- name: app
image: my-app:1.0
volumeMounts:
- name: seccomp-profiles
mountPath: /var/lib/kubelet/seccomp/profiles
volumes:
- name: seccomp-profiles
hostPath:
path: /var/lib/kubelet/seccomp/profilesRuntimeDefault blocks dangerous syscalls (mount, ptrace, reboot) while allowing common operations. Localhost loads a custom profile from the node. Create custom profiles with strace to record actual syscall usage.
AppArmor Profiles
AppArmor restricts what files, capabilities, and network access a container has. Profiles can be enforce (block violations) or complain (log only). AppArmor is available on Ubuntu, Debian, and SUSE — not available on all distributions.
apiVersion: v1
kind: Pod
metadata:
name: apparmor-pod
annotations:
container.apparmor.security.beta.kubernetes.io/app: localhost/app-profile
spec:
containers:
- name: app
image: my-app:1.0AppArmor profiles are applied via annotations. The profile must be loaded on the node before the pod can use it. Use kubectl get nodes -o jsonpath='{.items[*].status.nodeInfo.appArmorProfile}' to check AppArmor support.
Linux Capabilities
Linux capabilities split root privileges into distinct units. Drop all capabilities and add only what's needed. This follows the principle of least privilege — most applications need no capabilities at all.
apiVersion: v1
kind: Pod
metadata:
name: capabilities-pod
spec:
containers:
- name: app
securityContext:
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE # Bind to ports <1024
- CHOWN # Change file ownership
# For privileged system pods (CNI, storage)
- name: system-app
securityContext:
capabilities:
drop:
- ALL
add:
- NET_ADMIN # Network configuration
- SYS_ADMIN # System administration (use carefully)
- NET_RAW # Raw network access (ping)drop: ALL removes all capabilities. add: selectively adds specific ones. NET_BIND_SERVICE allows binding to low ports. NET_ADMIN allows network configuration. SYS_ADMIN is very powerful — avoid it unless absolutely necessary.
Read-Only Filesystem
readOnlyRootFilesystem prevents writes to the container filesystem. This stops attackers from modifying binaries, dropping malware, or changing configuration. Use emptyDir volumes for writable paths (logs, temp, cache).
apiVersion: v1
kind: Pod
metadata:
name: readonly-fs-pod
spec:
containers:
- name: app
securityContext:
readOnlyRootFilesystem: true
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /app/cache
- name: logs
mountPath: /app/logs
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir:
sizeLimit: 100Mi
- name: logs
emptyDir:
sizeLimit: 1GireadOnlyRootFilesystem: true makes the container filesystem read-only. emptyDir volumes provide writable paths for /tmp, cache, and logs. This prevents attackers from writing to the container filesystem.
Non-Root Containers
Running as root gives the container full privileges inside the container. Running as non-root limits the damage if the container is compromised. Use runAsNonRoot, runAsUser, and fsGroup to enforce non-root execution.
apiVersion: v1
kind: Pod
metadata:
name: non-root-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
runAsGroup: 3000
fsGroup: 2000
supplementalGroups: [4000]
containers:
- name: app
image: my-app:1.0
securityContext:
allowPrivilegeEscalation: falserunAsNonRoot: true prevents running as UID 0. runAsUser: 1000 runs as a specific user. fsGroup: 2000 sets the group for volume mounts. supplementalGroups: 4000 adds additional groups. allowPrivilegeEscalation: false prevents setuid/setgid.
Add USER 1000:1000 to your Dockerfile. This ensures the container runs as non-root even without Kubernetes security context. The USER directive sets the default user for the container.
Mark this lesson complete to store local progress and unlock a cleaner resume path the next time you visit.