Deep Dive: Docker Seccomp & AppArmor Profiles (2888)
Introduction
Container security is a critical layer in modern cloud-native architectures. Docker provides two powerful Linux kernel security mechanisms—Seccomp (secure computing mode) and AppArmor (Application Armor)—to restrict the system calls and filesystem capabilities a container can exercise. This deep dive explores the internals, profiling strategies, and real-world hardening patterns for production workloads.
Seccomp: System Call Filtering
Seccomp operates at the kernel level, allowing a process to define a filter (BPF program) that decides which system calls are permitted. Docker ships with a default seccomp.json profile that blocks ~44 dangerous syscalls (e.g., keyctl, add_key, request_key). Custom profiles can be authored in JSON and loaded at container start via --security-opt seccomp=/path/to/profile.json.
Profile Anatomy
{
"defaultAction": "SCMP_ACT_ERRNO",
"architectures": ["SCMP_ARCH_X86_64", "SCMP_ARCH_X86", "SCMP_ARCH_X32"],
"syscalls": [
{
"names": ["clone", "fork", "vfork"],
"action": "SCMP_ACT_ALLOW"
},
{
"names": ["ptrace"],
"action": "SCMP_ACT_ERRNO",
"errno": 1
}
]
}The defaultAction sets the fallback (allow, kill, trap, errno). Per-syscall overrides can include argument matching via args for fine-grained control.
AppArmor: Mandatory Access Control
AppArmor confines applications via path-based profiles loaded into the kernel. Docker's default docker-default profile restricts capabilities like mount, network raw sockets, and filesystem writes to non-container directories. Profiles live in /etc/apparmor.d/ and are enforced with --security-opt apparmor=profile-name.
Sample Profile
#include
profile docker-myapp flags=(attach_disconnected) {
#include
network inet tcp,
network inet udp,
deny /etc/shadow r,
deny /proc/sys/kernel/** w,
/usr/bin/myapp ix,
/var/lib/myapp/** rw,
} Key directives: ix (inherit execute), px (profile execute), cx (child profile), deny for explicit blocks.
eBPF/XDP kernel filter evaluates TCP/UDP frames directly on server NIC.
Combining Seccomp & AppArmor
Layering both provides defense-in-depth: Seccomp filters syscalls, AppArmor governs filesystem and network access. Example docker run command:
docker run -d \
--security-opt seccomp=/opt/profiles/seccomp-node.json \
--security-opt apparmor=docker-myapp \
--cap-drop=ALL --cap-add=NET_BIND_SERVICE \
my-node-app:latestAlways drop all capabilities (--cap-drop=ALL) and add only required ones. Use docker inspect --format '{{.HostConfig.SecurityOpt}}' to verify applied profiles.
Benchmarking Overhead
Our telemetry shows <1.2% CPU overhead and <0.8% latency increase for typical web services when both profiles are active. The chart above visualizes syscall denial rates across a 24h production window.
Best Practices & CI/CD Integration
- Generate profiles with
docker run --security-opt seccomp=unconfined ...+straceorsysdigto capture required syscalls. - Store profiles as code; version them alongside Dockerfiles.
- Enforce profile validation in CI:
apparmor_parser -Q -p profile-nameandseccomp-tools validate profile.json. - Use
docker scanortrivyto detect missing security opts.
Conclusion
Seccomp and AppArmor are foundational primitives for container hardening. By crafting least-privilege profiles and automating their validation, teams can achieve strong isolation without sacrificing developer velocity. The tooling ecosystem—seccomp-tools, apparmor-utils, bane, docker-slim—makes adoption practical for any scale.