AppArmor & SELinux
Mandatory access control on files and capabilities.
seccomp filters which syscalls a process may make; it says nothing about which files that process may touch. A Linux Security Module — AppArmor on Debian/Ubuntu, SELinux on RHEL/Fedora — adds mandatory access control: a policy the kernel enforces regardless of file permissions or user, confining a container to a declared set of paths, capabilities, and operations. It is the layer that turns “this process can open any file” into “this process can open these files.”
# Docker applies a default AppArmor profile (docker-default) automatically$ docker run --rm alpine cat /proc/self/attr/currentdocker-default (enforce) # the container is confined by AppArmor# load and apply a tighter custom profile:$ sudo apparmor_parser -r -W /etc/apparmor.d/docker-myapp$ docker run --rm --security-opt apparmor=docker-myapp myapp:1.0
A profile is an allowlist of paths and caps
An AppArmor profile enumerates what the process may do: read these paths, write those, use these capabilities, deny everything else. A tight web-server profile might allow reading its content directory and binding a socket, permit writes only to a cache path, and explicitly deny writing to binary directories or reading /etc/shadow — so even a fully compromised process is boxed into the behavior you declared.
#include <tunables/global>profile docker-myapp flags=(attach_disconnected,mediate_deleted) {#include <abstractions/base>network inet tcp,/app/** r, # read app files/var/cache/app/** rw, # one writable pathdeny /etc/shadow rwkl, # never touch credentialsdeny /bin/** wl, # no writing into binary dirsdeny mount, # no mounting (a common escape primitive)}
SELinux does the same with labels
On RHEL-family hosts the equivalent is SELinux, which labels every process and file and enforces type-based rules — container_t processes may only touch container_file_t content, so a container simply cannot read host files that carry a different label. The concepts map: AppArmor is path-based, SELinux is label-based, both are mandatory access control the kernel enforces beneath the app. Run a new profile in complain/permissive mode first, tune from the denials, then enforce.