Pick the mandatory access control system your distribution ships and supports: for example SELinux on Red Hat family systems and AppArmor on Ubuntu. Both confine programs beyond normal file permissions. The best one is the one you leave in enforcing mode and keep maintained, so choose by support and team skill first.
What each one does
Both add a second layer of rules on top of user and group permissions, so a compromised process is limited to what policy allows even if it runs as root.
AppArmor. The AppArmor manual describes it as a kernel enhancement to confine programs to a limited set of resources, with a security model that binds access control attributes to programs rather than to users. Profiles are loaded into the kernel, usually through the apparmor.service unit, and are traditionally stored under /etc/apparmor.d/, named after the program path. A profile applies at exec time. A process already running unconfined cannot be confined later. The manual adds that AppArmor restricts privileged operations even for root, for example a confined process cannot call ptrace, reboot or init_module.
SELinux. The Red Hat documentation describes the policy as applied by labels on files and processes, with enforcing and permissive modes plus a disabled state. SELinux depends on correct labels on every file system object, which is why it relabels after being re-enabled.
Modes, side by side
| AppArmor | SELinux | |
|---|---|---|
| Enforce | Profile enforced and violations reported | Policy enforced, access denied |
| Learn | Complain mode: allows and logs | Permissive mode: logs only |
| Off | Per profile or unloaded | Disabled: no policy loaded |
The AppArmor manual warns that complain mode provides no security, only auditing, and should not be used in a hostile environment. Permissive mode on SELinux has the same property. Details are in AppArmor complain or enforce and SELinux permissive vs disabled.
A decision rule
- Use what your distribution supports. Vendor policy, documentation and bug reports assume it. Switching systems on a distribution that does not ship the other is a lot of work.
- Count your custom software. Packaged software usually comes with policy. Internal apps need a profile or module that someone writes and maintains.
- Check team skill. A team that knows how to read denials and fix labels or profiles gets value. A team that disables it at the first outage gets none.
- Plan for containers. If you run containers, see which of seccomp, AppArmor or SELinux fits Kubernetes.
- Decide the exception process. When a service breaks, the path should be permissive for that one domain or profile, fix, enforce again, not disable everything.
Common mistakes
- Leaving the system in permissive or complain mode permanently.
- Writing a wide allow rule to silence a denial without reading it.
- Skipping the check after upgrades, since policy and profiles change with packages.
- Treating it as a substitute for patching. It limits damage, it does not remove the bug.
Safeguard offers asset discovery, compliance evidence automation and policies, gates and guardrails, and there is a free scan at gold.safeguard.sh/scan.
Quick check
Can I run SELinux and AppArmor together on one host?
Not in the usual setup. Distributions normally enable one primary system, so use the one your distribution supports.
Is complain or permissive mode a safe default?
No. Both log without blocking. Use them briefly for debugging, then return to enforcing.
Sources
- apparmor(7) manual page, Ubuntu manpages
- Changing SELinux states and modes, Red Hat Enterprise Linux 9 documentation
Last reviewed: 19 October 2026.