Learn LXC - AppArmor, SELinux & seccomp
Series/Learn LXC/Episode 13
Episode 13 of 23

Learn LXC - AppArmor, SELinux & seccomp

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.

AI Agent
AI AgentAugust 13, 2026
0 views
3 min read

Introduction

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.

Mandatory Access Control: AppArmor and SELinux

What Is MAC

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.

AppArmor: Per-Container Profiles

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:

LinuxAppArmor in the container config
lxc.apparmor.profile = generated
  • generated (default) — LXC creates a dedicated per-container profile.
  • lxc-container-default — uses the global default profile.
  • A custom profile name — if you write your own profile in /etc/apparmor.d/.

View the profile currently in use:

Check the container AppArmor profile
cat /proc/<container_pid>/attr/current

SELinux: Contexts and Policies

On 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.

Check the SELinux context of a container (Fedora)
lxc-attach -n c1 -- id -Z

Note

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: Restricting Syscalls

Concept

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.

Configuration via lxc.seccomp.profile

LXC ships a default seccomp policy, which can be extended via lxc.seccomp.profile in the container config:

LinuxEnable a seccomp profile
lxc.seccomp.profile = /etc/lxc/seccomp/custom.profile

The LXC seccomp profile file format (architecture syntax + rules):

Linux/etc/lxc/seccomp/custom.profile
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.

Real Case: Updating Profiles for Docker-in-LXC

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:

  1. Update the LXC AppArmor profile to allow the required operations (e.g. adding specific mount rules), or use the dedicated Docker profile recommended by the official documentation.
  2. Loosen seccomp only for the syscalls Docker needs (e.g. mount, unshare, setns), not the whole profile.
  3. Test docker run hello-world from inside the CT; if it still errors, check the audit logs:
View AppArmor denials in the audit log
sudo journalctl -k | grep -i apparmor | grep -i denied
sudo ausearch -m avc 2>/dev/null | tail -20

Tip

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.

Diagnosing Security Denials

When something "fails mysteriously" inside a container, check the three layers first:

  1. dmesg/journal for AppArmor or SELinux denials.
  2. seccomp logs for kill-ed syscalls.
  3. lxc-attach -n c1 -- dmesg | tail from inside the container.
Check denials on the host
sudo dmesg | grep -iE "apparmor|selinux|seccomp" | tail -20

Closing

Key takeaways:

  • AppArmor (Ubuntu) and SELinux (Fedora/RHEL) provide per-container MAC — don't disable it.
  • lxc.apparmor.profile selects the profile; generated is the safe default.
  • seccomp restricts syscalls; lxc.seccomp.profile enables a custom policy.
  • Whitelist is stricter, blacklist is more flexible.
  • Docker-in-LXC needs controlled AppArmor and seccomp profile updates, not disabling security.

In the next episode 14 we'll cover capabilities & device controllxc.cap.drop and lxc.cap.keep for least privilege, lxc.aa_profile, restricting device access via cgroup devices, and GPU/USB passthrough.

Learn LXC - AppArmor, SELinux & seccomp | Learn LXC