AppArmor: confine behavior
Load a profile, reference it, enforce vs complain.
seccomp controls which syscalls a container may make, but it says nothing about which files that container may read or write — a process can be allowed the open syscall and still open anything on the filesystem. AppArmor fills that gap. It is a Linux Security Module that confines a program to a declared set of file paths, Linux capabilities, and network operations, so a compromised app that could previously write anywhere is reduced to writing the three paths its profile permits.
AppArmor is path-based and loaded per node. Confirm the kernel module is enabled, then load profiles with apparmor_parser and inspect state with aa-status, which lists every loaded profile and whether it is in enforce or complain mode. Because it is a host feature, the profile must exist on whatever node the pod lands on.
$ cat /sys/module/apparmor/parameters/enabled # -> Y (module active)$ sudo apparmor_parser -q /etc/apparmor.d/k8s-frontend # load a profile$ sudo aa-statusapparmor module is loaded.12 profiles are loaded. 12 in enforce mode, 0 in complain mode.k8s-frontenddocker-default ...
What a profile looks like
A profile is a set of allow rules over paths and capabilities; anything not permitted is denied. A tight web-server profile might allow reading its static content and binding a socket, deny writes to the binary directories, and drop capabilities it never uses. The shape below confines a frontend to read-only content and an explicit writable temp path.
#include <tunables/global>profile k8s-frontend flags=(attach_disconnected) {#include <abstractions/base>network inet tcp, # allow TCP/usr/share/nginx/** r, # read static content/var/cache/nginx/** rw, # explicit writable pathdeny /bin/** wl, # no writing into binary dirsdeny /etc/shadow rwkl, # never touch the shadow file}
Reference it from the pod
Recent Kubernetes sets the profile as a field on the security context (type Localhost, plus the profile name); older clusters used a container.apparmor.security.beta.kubernetes.io/<container> annotation — you will still see the annotation in the wild and in some exam environments. Either way the named profile must already be loaded on the node, so load it fleet-wide with a DaemonSet or your node bootstrap.
spec:containers:- name: appimage: registry.internal/frontend:1.4.2securityContext:appArmorProfile:type: LocalhostlocalhostProfile: k8s-frontend# legacy form (still common):# metadata.annotations:# container.apparmor.security.beta.kubernetes.io/app: localhost/k8s-frontend