Modes and commands
Profiles live in /etc/apparmor.d/. Two modes:
- enforce — violations are blocked;
- complain — only logged (for debugging/rollout).
The
apparmor-utilspackage provides theaa-*helpers; without it, onlyapparmor_parseris available.
Mandatory access control on Ubuntu — service profiles, enforce/complain modes.
sudo aa-status
profiles and modes
journalctl -k | grep -i 'apparmor.*denied'
what is being blocked
sudo aa-complain /etc/apparmor.d/usr.sbin.nginx
log only
sudo aa-logprof
extend a profile from the logs
sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx
block again
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
reload
AppArmor is Ubuntu's mandatory access control (MAC) system. On top of regular permissions (permissions) it restricts which files/capabilities a specific program may touch, per profile. If a service behaves strangely with "Permission denied" even though the permissions look right, suspect AppArmor.
On the lab machine AppArmor is on in the kernel
(/sys/module/apparmor/parameters/enabled is Y), 116 profiles are
loaded, 22 of them in enforce mode.
A profile is a file in /etc/apparmor.d/: which program it belongs to and
what the program may do. A lab profile for a copy of ls:
abstractions/base is the common set a program cannot start without:
libraries, locales, /dev/null. The line include if exists <local/…>
pulls in /etc/apparmor.d/local/<name> if it exists. Package profiles
include such a file — put your rules there and leave the package's file
alone, so an upgrade does not wipe them.
After an edit, load the profile into the kernel again; it applies at once to processes already running:
| Rule | What it allows |
|---|---|
/srv/data/public/ r, |
reading the directory itself — the file list |
/srv/data/public/* r, |
files directly in the directory |
/srv/data/public/** r, |
everything inside the directory at any depth, but not the directory itself |
With only the ** rule a file can be read, but the directory cannot be
listed:
After the line /srv/data/public/ r, in the local file, demols
/srv/data/public prints a.txt. The file permissions are a+rX all
along — plain ls lists the directory with no error.
| Field | Meaning |
|---|---|
profile |
whose profile refused |
operation |
the action: open, exec, mknod, connect |
name |
the path the program asked for |
requested_mask, denied_mask |
what was asked and what was refused: r, w, x, a |
comm, pid |
the process |
fsuid, ouid |
the process's user and the file's owner |
A name ending in / is a directory: the /dir/ r, rule is missing.
The LABEL column of ps -eZ shows the profile and mode; unconfined
means no profile. In complain mode (aa-complain) nothing is refused,
and the log gets apparmor="ALLOWED" instead of DENIED.
While the auditd service is off (as on the lab machine), denials go to
the kernel log: sudo journalctl -k | grep DENIED. A running auditd
takes audit messages for itself — new denials go to
/var/log/audit/audit.log, and journalctl -k does not have them:
ausearch -i adds a SYSCALL record with uid and exe and a
PROCTITLE record with the whole command (see auditd).
Profiles live in /etc/apparmor.d/. Two modes:
The
apparmor-utilspackage provides theaa-*helpers; without it, onlyapparmor_parseris available.
A profile's structure (simplified): the path to the binary plus access rules
(capabilities, paths with r/w/ix), plus #include <abstractions/...> and
#include <tunables/...>.
Semi-automatic tuning of a profile against real behavior:
A typical cycle: aa-complain → run the workload → aa-logprof (accept the
generated permissions) → aa-enforce.
AppArmor is blocking it. Check the denials:
A line like DENIED ... operation="open" ... name="/path" shows what the
profile did not allow.
The profile does not allow that path. For debugging, use aa-complain, run
the workload, then aa-logprof will extend the profile, followed by
aa-enforce.
sudo apparmor_parser -r <profile> (or reload apparmor). A syntax error will
be shown by the parser.
sudo aa-complain <profile> (soft) or aa-disable. Turning AppArmor off
entirely in production is not a good idea — it is a real loss of protection.
Snap uses its own profiles (confinement); denials are also visible in
journalctl -k (see snap snap connections).
Found an inaccuracy? Report an error in the article