CoursesLinux hardeningMandatory access control

AppArmor profiles

Path-based confinement, complain to enforce.

Advanced12 min · lesson 13 of 16

AppArmor is the MAC system on Debian/Ubuntu-family distributions, and it reaches the same goal as SELinux by a different, often simpler route: instead of labeling everything, it confines individual programs with per-application profiles that list the exact files, capabilities, and network operations each program may use. A profile for nginx says "may read the web root, bind these ports, and nothing else," so a compromised nginx is confined to that declared behavior — path-based rules that are easy to read and write.

COMPLAIN FIRST, THEN ENFORCE
1Load in complain mode
aa-complain: log accesses, block nothing
2Exercise the app
run the real workload so AppArmor logs it
3Update from logs
aa-logprof adds the paths/capabilities needed
4Switch to enforce
aa-enforce: profile now blocks the rest
Never flip an unproven profile straight to blocking on a production service.
terminal
$ sudo aa-status # which profiles are loaded, and in which mode
apparmor module is loaded.
25 profiles are loaded. 23 in enforce mode, 2 in complain mode.
$ ls /etc/apparmor.d/ # profiles live here, one per program

A profile is an allowlist of paths and capabilities

An AppArmor profile enumerates what a program may do and denies the rest: read these paths, write those, use these capabilities, make these network calls. Because the rules are literal filesystem paths, a profile is legible at a glance — you can see exactly what a confined program is allowed to touch. A tight profile turns "this process can open any file its user can" into "this process can open these specific files," which is the whole value: even fully attacker-controlled code stays inside the box.

/etc/apparmor.d/usr.local.bin.myapp
profile myapp /usr/local/bin/myapp {
#include <abstractions/base>
capability net_bind_service, # may bind a low port
/etc/myapp/** r, # read its config
/var/lib/myapp/** rw, # read/write its data dir
/var/log/myapp/*.log w, # write its logs
deny /etc/shadow r, # never read credentials
deny /home/** r, # never read user data
}

Complain first, then enforce

The safe way to build or apply a profile is to run it in complain mode first: AppArmor logs what the program does without blocking anything, so you can see every access it makes, confirm the profile covers the legitimate ones, and then switch to enforce. aa-complain and aa-enforce toggle the mode, and aa-logprof helps update a profile from the logged activity. This is the same tune-then-enforce discipline as SELinux and seccomp — you never flip an unproven profile straight to blocking on a production service.

terminal
$ sudo aa-complain /usr/local/bin/myapp # log violations, block nothing (tune here)
# exercise the app, then review and update the profile from the logs:
$ sudo aa-logprof
$ sudo aa-enforce /usr/local/bin/myapp # switch to blocking once the profile is right
Do not disable the profile to make an app work
As with SELinux, the wrong fix for an AppArmor denial is aa-disable or removing the profile — that drops the confinement entirely. The right move is complain mode plus aa-logprof to add the specific path or capability the app legitimately needs, then re-enforce. Keep profiles loaded and enforcing, especially for anything internet-facing (browsers, web servers, mail), because those are exactly the programs whose compromise the profile is designed to contain.