This episode covers LXC's MAC security layers: per-container AppArmor profiles, SELinux contexts, and seccomp policies via lxc.seccomp.profile to restrict syscalls, including updating profiles for workloads like Docker-in-LXC.

In episode 5 we saw unprivileged containers limit user privileges. But that's only one layer. In episode 13 we add the next two: Mandatory Access Control (MAC) with AppArmor/SELinux, and seccomp to restrict the syscalls that processes inside the container can use. These are the "safety fences" that contain the damage even when something inside the container has already leaked.
The concept: even a process running as root can't do anything not allowed by the policy. Unlike DAC (the classic rwx permissions), MAC binds access to labels and profiles — not to users. You already learned the privilege concept in episode 5; MAC is a stricter guard on top of it.
On Ubuntu and its derivatives, LXC uses AppArmor. Each container gets a profile restricting file access, capabilities, and mounts. The profile LXC generates contains safe default rules, then overlays the generic lxc-container-default profile.
The container config references the profile:
lxc.apparmor.profile = generatedgenerated (default) — LXC creates a dedicated per-container profile.lxc-container-default — uses the global default profile./etc/apparmor.d/.View the profile currently in use:
cat /proc/<container_pid>/attr/currentOn SELinux distros (Fedora, RHEL/CentOS), MAC isolation is enforced with SELinux, where each container gets a dedicated context (label). LXC integrates with the container_t policy and its derivatives. The concept is the same — access is bound to labels — only the implementation differs from AppArmor.
lxc-attach -n c1 -- id -ZNote
Choose one: Ubuntu/Debian distros use AppArmor, Fedora/RHEL distros use SELinux. Both are equivalent MAC — what matters is don't disable them. Turning off MAC to "save time" is like opening every door in the house to avoid the hassle of carrying keys.
seccomp restricts the syscalls that processes inside the container may invoke. The kernel rejects syscalls not allowed by the policy before they can execute. This is a defense layer against kernel exploits: an attacker who's already in is still limited in the tools they can use.
LXC ships a default seccomp policy, which can be extended via lxc.seccomp.profile in the container config:
lxc.seccomp.profile = /etc/lxc/seccomp/custom.profileThe LXC seccomp profile file format (architecture syntax + rules):
1
whitelist
vuname x86_64
reject_force kill
open
close
read
write
execve
...A whitelist block means: only the listed syscalls are allowed; everything else gets killed. This is the strictest approach — suitable for workloads whose syscalls are already known. For more flexibility, use blacklist mode with a list of dangerous syscalls to block.
Warning
Don't enable an overly strict seccomp profile for general workloads — the container can die with an unclear syscall error, or a service can "suddenly die". Start from LXC's default policy, add only proven-necessary exceptions, and test thoroughly before production.
When running Docker-in-LXC (episode 12), the LXC container must allow the syscalls and operations the Docker runtime needs (like clone with namespace flags, mount, and overlayfs features). LXC's default AppArmor policy often blocks these operations, and the default seccomp profile can be too strict.
The correct fix — not disabling security:
mount, unshare, setns), not the whole profile.docker run hello-world from inside the CT; if it still errors, check the audit logs:sudo journalctl -k | grep -i apparmor | grep -i denied
sudo ausearch -m avc 2>/dev/null | tail -20Tip
The principle is always the same: loosen just enough, don't turn it off. Docker-in-LXC needs a bounded loosening for container runtime operations — that's normal and documented. What's never recommended is casually setting lxc.apparmor.profile = unconfined, except in a private lab where the risk is consciously accepted.
When something "fails mysteriously" inside a container, check the three layers first:
dmesg/journal for AppArmor or SELinux denials.lxc-attach -n c1 -- dmesg | tail from inside the container.sudo dmesg | grep -iE "apparmor|selinux|seccomp" | tail -20Key takeaways:
lxc.apparmor.profile selects the profile; generated is the safe default.lxc.seccomp.profile enables a custom policy.In the next episode 14 we'll cover capabilities & device control — lxc.cap.drop and lxc.cap.keep for least privilege, lxc.aa_profile, restricting device access via cgroup devices, and GPU/USB passthrough.