AppArmor is a Linux Security Module (LSM) that implements mandatory access control by confining individual programs to per-application profiles. Each profile lists the files, capabilities, network access and other resources a program may use, and the kernel denies everything the profile does not allow, even when the process runs as root.
In Linux security, AppArmor is known for its path-based model and readable profile language, which make it easier to adopt than label-based SELinux. It has been part of the mainline kernel since version 2.6.36 and is enabled by default on Ubuntu and Debian, where it confines services, snap packages and container runtimes.
How AppArmor enforces mandatory access control
Standard Unix permissions are discretionary access control (DAC): the owner of a file decides who may access it, and root bypasses almost every check. AppArmor adds a second, independent check through LSM hooks in the kernel, so an operation succeeds only if both DAC and the AppArmor profile allow it.
A profile attaches to a program by its executable path, for example /usr/sbin/cupsd. When that binary is executed, the kernel places the new process under the profile, and every later file open, socket creation or capability use is compared with the profile’s rules. Processes without a matching profile run unconfined and are limited only by normal permissions.
Because rules name file paths rather than inode labels, AppArmor needs no filesystem relabeling and works on any filesystem. The same design creates its main weakness: a file reachable through two different paths may receive two different decisions.
Profile structure and rule types
Profiles live in the /etc/apparmor.d directory, are compiled by apparmor_parser and are loaded into the kernel at boot. A profile combines shared abstractions, such as abstractions/base for libraries every program needs, with rules specific to the application.
| Rule type | Example | What it controls |
|---|---|---|
| File rule | /etc/nginx/nginx.conf r, | Read, write, append, lock, link and memory-map access |
| Execute rule | /usr/bin/gzip ix, | How a child program is confined when executed |
| Capability rule | capability net_bind_service, | Use of specific root privileges |
| Network rule | network inet stream, | Socket families and types |
| Signal and ptrace | signal send set=term, | Inter-process control |
| Deny rule | deny /etc/shadow r, | Explicit refusal that overrides allows |
Execute rules deserve special care. The modes ix (inherit the current profile), px (switch to the program’s own profile), cx (switch to a child profile) and ux (run unconfined) decide whether confinement survives an exec. Granting ux to a shell effectively disables the whole profile.
AppArmor profile modes
Every loaded profile runs in a mode that defines what happens when a process attempts a disallowed action. Administrators switch modes per profile without rebooting.
| Mode | Disallowed action | Logged | Typical use |
|---|---|---|---|
| Enforce | Blocked | Yes, as DENIED | Production |
| Complain | Allowed | Yes, as ALLOWED | Developing a new profile |
| Kill | Blocked and process killed | Yes | Strict containment |
| Unconfined flag | Allowed | No | Placeholder profiles that only attach |
| Disabled | Profile not loaded | No | Troubleshooting |
Denials appear in the kernel audit log with the text apparmor="DENIED", the profile name, the operation and the requested path. Reading these records is the fastest way to diagnose an application that fails only under confinement.
AppArmor compared with SELinux and other controls
AppArmor and SELinux are the two dominant MAC systems on Linux, and a kernel normally runs only one of them as the major LSM. Seccomp, Linux capabilities and PAM address different layers and complement either one.
| Mechanism | Model | Scope | Default on |
|---|---|---|---|
| AppArmor | Path-based profiles | Selected programs | Ubuntu, Debian |
| SELinux | Labels and type enforcement | Whole system | RHEL, Fedora, Android |
| Seccomp | System call filter | One process tree | Container runtimes, browsers |
| Linux capabilities | Split root privileges | Per thread | All distributions |
| PAM | Authentication modules | Login and services | All distributions |
The practical trade-off is clear. AppArmor offers faster adoption and readable profiles for confining a few exposed services, while SELinux offers finer, system-wide control and multi-level security at the cost of a steeper learning curve.
Building and maintaining AppArmor profiles
The usual workflow is learn, review, enforce: run the program in complain mode, exercise all its features, convert the logged events into rules and then switch to enforce mode.
| Tool | Purpose |
|---|---|
| aa-status | Lists loaded profiles, their modes and confined processes |
| aa-genprof | Creates a new profile interactively from observed behavior |
| aa-logprof | Turns logged events into proposed rule updates |
| aa-complain | Switches a profile to complain mode |
| aa-enforce | Switches a profile to enforce mode |
| apparmor_parser -r | Reloads a profile after manual editing |
Generated rules must be reviewed by a person. Learning tools tend to suggest broad recursive globs that match whole directory trees, which silently turn a tight profile into a nearly meaningless one.
Limitations and common mistakes
- Only profiled programs are confined. A system with a handful of profiles still runs most processes unconfined.
- Path ambiguity through bind mounts, chroots and namespaces can lead to unexpected denials or gaps.
- Leaving a profile in complain mode indefinitely gives a false sense of protection, because nothing is blocked.
- Disabling AppArmor globally to fix one application removes protection from every confined service; adjusting the single profile is the correct fix.
- Moving a program to a different install path detaches it from its profile without any warning.
Explained without jargon
Normally, a program on Linux can do anything its user account is allowed to do. AppArmor gives selected programs a personal rulebook that is stricter than that.
Picture a hotel where staff cards open certain rooms. AppArmor is like giving the window cleaner a card that opens only the windows, even if the cleaner is also the manager. If an attacker takes over the window cleaner, the card still opens nothing else, so the damage stays small.
A typical use case
A company runs a public web server on Ubuntu with a PHP application that handles file uploads. The security team worries that a vulnerability in the application could let an attacker read configuration files or start a shell.
An administrator writes a profile for the PHP-FPM workers that allows reading the application directory, writing only to the upload folder and connecting only to the local database socket. After two weeks in complain mode, aa-logprof shows three legitimate actions missing, which are added before switching to enforce mode. Months later, an attacker exploits an upload bug and tries to run /bin/sh and read /etc/shadow; both attempts produce DENIED entries, and the compromise stays contained to the upload folder.