How do rootless containers, Seccomp, and AppArmor harden Docker, and what breaks when you use all three?
Quick Answer
Rootless containers run Docker without root privileges. Seccomp restricts which system calls a container can make. AppArmor limits file and network access per container. Together they provide layered security, but conflicts happen when rootless mode needs system calls that Seccomp blocks, or when AppArmor denies paths that rootless UID mapping requires.
Detailed Answer
Think of a building with three independent security systems: a keycard system that controls who can enter which floor, a phone system that blocks certain outgoing call types, and a camera system that restricts which rooms each employee can access. Each system works on its own, but sometimes a new employee's keycard triggers the camera alert because their access pattern looks unusual. Container security layers interact the same way.
Rootless containers tackle the fundamental risk that the Docker daemon traditionally runs as root on the host. In rootless mode, dockerd runs under a regular user's UID, and user namespaces remap container UIDs so that root inside the container maps to an unprivileged UID on the host. This means even if an attacker escapes the container, they land as a non-root user on the host. Seccomp (Secure Computing Mode) is a Linux kernel feature that filters system calls. Docker applies a default Seccomp profile that blocks about 44 dangerous syscalls including mount, reboot, and kexec_load. AppArmor provides Mandatory Access Control that restricts which files, network sockets, and capabilities a container process can access, regardless of its UID.
Internally, these three mechanisms operate at different kernel layers. User namespaces (rootless) work at the namespace level, remapping UIDs and GIDs through /etc/subuid and /etc/subgid. Seccomp operates at the system call interface, using BPF filters loaded before the container process starts. AppArmor operates at the LSM (Linux Security Module) layer, enforcing path-based access control policies loaded into the kernel. Docker applies all three during container creation: it sets up user namespace mapping, loads the Seccomp BPF filter via the OCI runtime spec, and assigns the AppArmor profile through the container's security options.
At production scale, the combination provides real defense in depth. A container running the checkout-api might have rootless mode preventing host root access, a custom Seccomp profile allowing only the 150 system calls the Go binary actually uses, and an AppArmor profile restricting file writes to /tmp and /var/log/checkout only. Security teams should test Seccomp in audit mode before enforcing, generate AppArmor profiles using tools like bane or aa-logprof, and test rootless networking thoroughly because rootless mode uses slirp4netns or pasta for network isolation, which adds latency compared to native bridge networking.
The tricky part is that rootless containers need the newuidmap and newgidmap system calls for user namespace setup, and an overly strict Seccomp profile can block these. Similarly, AppArmor may deny access to /proc/self/uid_map or /etc/subuid paths that rootless mode needs. When turning on all three, architects must test the specific combination: create a Seccomp profile that includes the system calls rootless mode requires, make sure the AppArmor profile allows the filesystem paths rootless networking and UID mapping use, and verify that slirp4netns or pasta networking works under both Seccomp and AppArmor constraints. Roll these out one at a time (rootless first, then Seccomp in audit mode, then AppArmor in complain mode) to avoid silent breakage in production.