AppArmor and seccomp on Kubernetes
AppArmor and seccomp shrink what a container can do after it starts. Admission policies decide whether the pod is allowed. These two decide what syscalls and file paths it gets once it is running.
AppArmor is about files, network, and capabilities. Seccomp is about syscalls. Both are kernel features. Kubernetes only attaches a profile that already exists on the node.
AppArmor
Modes:
enforce— block violationscomplain— log and allow (use this to learn the profile)unconfined— no profile
sudo systemctl status apparmor
sudo aa-status
Profiles live in /etc/apparmor.d/. Load one, then enforce it:
sudo apparmor_parser -r /etc/apparmor.d/nginx-profile
sudo aa-enforce nginx-profile
sudo aa-complain nginx-profile # while you are writing it
A tight nginx-shaped profile denies /etc/shadow, /root/, and sys_admin / net_admin, and allows the paths nginx actually needs.
Attach it with a per-container annotation. The name after localhost/ must match the profile name on every node that might run the pod:
apiVersion: v1
kind: Pod
metadata:
name: nginx-apparmor
annotations:
container.apparmor.security.beta.kubernetes.io/nginx: localhost/nginx-profile
spec:
containers:
- name: nginx
image: nginx:1.20
If the profile is missing on a worker, the pod will not start. That is the usual failure.
Violations show up in the kernel log:
sudo dmesg | grep -i apparmor
kubectl describe pod nginx-apparmor
Seccomp
Modes you will actually use:
RuntimeDefault— the runtime’s default filter. Start here.Localhost— a JSON file under the kubelet seccomp directoryUnconfined— no filter. Avoid it.
apiVersion: v1
kind: Pod
metadata:
name: secure-pod
spec:
securityContext:
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: nginx:1.20
Custom profile:
sudo mkdir -p /var/lib/kubelet/seccomp/profiles
spec:
securityContext:
seccompProfile:
type: Localhost
localhostProfile: profiles/nginx-seccomp.json
The JSON defaultAction is usually SCMP_ACT_ERRNO. Then you allow the syscalls the process needs. Deny mount, ptrace, chmod if the workload does not need them. Validate the file with jq before you point a pod at it.
Container-level seccompProfile overrides the pod-level setting.
grep CONFIG_SECCOMP /boot/config-$(uname -r)
sudo dmesg | grep -i seccomp
kubectl get pod nginx-pod -o yaml | grep -A 10 securityContext
Write the AppArmor profile in complain mode first. Use RuntimeDefault seccomp unless you have a reason not to. Put the files on every node, not just the control plane.