Andrew Mercer
on this page

AppArmor is a Linux Security Module (LSM) that restricts what a program can do — which files it can read/write/execute, which network access it has, which capabilities it can use — by attaching a per-program profile to its binary path, rather than to a user or file label as SELinux does. It ships enabled by default on Ubuntu/Debian-family distributions; RHEL-family distributions use SELinux instead (the two are mutually exclusive on one kernel).

Check status

sudo aa-status                      # loaded profiles and their mode; overall summary
sudo apparmor_status                # same, older name
cat /sys/module/apparmor/parameters/enabled     # Y if the LSM itself is active

Profile modes

Mode Behaviour
enforce violations are blocked and logged
complain (a.k.a. audit) violations are logged only, not blocked — used while developing/tuning a profile
unconfined no profile loaded; program runs unrestricted
sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx      # switch a profile to enforce
sudo aa-complain /etc/apparmor.d/usr.sbin.nginx     # switch to complain (learning) mode
sudo aa-disable /etc/apparmor.d/usr.sbin.nginx      # unload it entirely

Profiles live in /etc/apparmor.d/

Profile files are named after the binary's path with slashes turned into dots, e.g. /usr/sbin/nginx → /etc/apparmor.d/usr.sbin.nginx.

#include <tunables/global>

/usr/sbin/nginx {
  #include <abstractions/base>
  #include <abstractions/nameservice>

  capability net_bind_service,
  capability setuid,
  capability setgid,

  /etc/nginx/**       r,
  /var/log/nginx/*    w,
  /var/www/**         r,
  /run/nginx.pid      rw,

  network inet stream,
  network inet6 stream,

  deny /home/**       rwx,
  deny /root/**       rwx,
}

r/w/x are read/write/execute; ix inherits the current profile for an exec'd child; Px/Cx transition the child to another/child profile. deny rules are evaluated after allow rules and always win.

Reload after editing:

sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
sudo systemctl reload apparmor        # reload everything

Generating a profile from observed behaviour

sudo aa-genprof /usr/local/bin/myapp

Run the program through its normal workflows in another terminal while aa-genprof watches; it prompts you to allow, deny, or glob each access it sees, then writes the profile.

sudo aa-logprof            # after a profile has been running in complain mode for a while,
                            # walks you through the logged violations to turn them into rules

Investigating denials

sudo journalctl -k | grep -i apparmor       # kernel log, most systems
sudo dmesg | grep -i apparmor
sudo aa-notify -p                            # desktop notifications for denials (if installed)

A denial log line names the profile, the operation, and the path — usually enough to add one more rule and reload.

Everyday commands

sudo aa-unconfined                  # list processes with open network sockets that have no profile
sudo aa-autodep /usr/local/bin/myapp        # generate a minimal starter profile from static analysis

See also SELinux basics referenced from firewalld/RHEL contexts for the RHEL-family equivalent approach, and sudo and fail2ban for complementary access-control layers.