Skip to main content
RunBook Academy

LinuxXXVIII · SELinux and AppArmorAppArmor

AppArmor concepts - path-based MAC for Debian-family systems

Intermediate⏱ ~10 minaa-statusaa-genprofaa-complain

What you'll learn

  • Describe AppArmor profiles and modes
  • Read and write simple profiles
  • Use aa-status and aa-complain for diagnosis
  • Recognise when AppArmor is the right choice

Prerequisites

Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09

Not yet marked complete on this device.

AppArmor is the default MAC on Ubuntu and Debian. It is path-based (uses file paths) rather than label-based (like SELinux). This lesson covers profiles, modes, and daily use.

Profiles

An AppArmor profile is a text file in /etc/apparmor.d/ that defines what a binary can do:

#include <tunables/global>

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

  capability dac_override,
  capability net_bind_service,

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

  network inet stream,
  network inet6 stream,

  deny /etc/shadow r,
  deny /root/** rwx,
}

The profile is enforced for the specified binary path.

Modes

ModeBehaviour
enforceProfile is enforced. Violations are denied and logged.
complainProfile is not enforced. Violations are logged only.
unconfinedNo profile. Process runs with DAC only.
sudo aa-status                            # all profiles
sudo aa-status --json                     # machine-readable
cat /sys/kernel/security/apparmor/profiles    # kernel view

A profile in complain mode lets you capture the accesses the application wants without denying them. Useful for developing a profile.

Switch mode

sudo aa-complain /usr/sbin/nginx      # complain mode
sudo aa-enforce /usr/sbin/nginx       # enforce mode
sudo aa-disable /usr/sbin/nginx       # remove profile (unconfined)

Generate a profile

sudo aa-genprof /usr/sbin/nginx

Runs the program (when you start it manually) and observes its accesses. You choose which accesses to allow or deny interactively. Saves the profile to /etc/apparmor.d/usr.sbin.nginx.

For a service that runs continuously, set it to complain mode, restart the service, observe, then enforce:

sudo aa-complain /usr/sbin/nginx
sudo systemctl restart nginx
# Wait for the service to do all its normal operations
sudo aa-enforce /usr/sbin/nginx

Common profile building blocks

#include <abstractions/base>        # base permissions
#include <abstractions/nameservice> # DNS, /etc/nsswitch
#include <abstractions/openssl>     # OpenSSL library and config
#include <abstractions/ssl_certs>   # the CA bundle under /etc/ssl/certs
#include <abstractions/web-data>    # common document-root paths

Abstractions are reusable rule sets. Use them instead of listing every file the application needs.

There is no abstractions/nginx. Abstractions are the set of files shipped in /etc/apparmor.d/abstractions/, not a naming convention you can extend by guessing — and an #include of one that does not exist is a hard parse failure, not a warning:

$ apparmor_parser -Q ./myprofile
AppArmor parser error for ./myprofile in profile ./myprofile at
line 5: Could not open 'abstractions/nginx'

Check what your system actually provides, and validate every profile before loading it:

ls /etc/apparmor.d/abstractions/
sudo apparmor_parser -Q /etc/apparmor.d/usr.sbin.nginx   # parse only, loads nothing

Capability rules

capability dac_override,         # bypass read/write permissions
capability net_bind_service,     # bind to ports < 1024
capability sys_admin,            # mount, etc.
capability sys_chroot,           # chroot

Capabilities are granular Linux privileges. Each one allows a specific operation.

File rules

/etc/nginx/** r,             # recursive read
/var/log/nginx/** w,         # recursive write
/run/nginx.pid w,            # single file write
deny /etc/shadow r,          # explicit deny

Modes: r (read), w (write), a (append), m (map executable), k (lock), l (link).

Execute is different: bare x is a syntax error. Every exec rule must carry a qualifier saying which profile the new process runs under, because that decision is the whole security question.

/usr/bin/helper ix,    # inherit: child runs under THIS profile - safest default
/usr/bin/helper Px,    # child runs under its own profile; fails if none exists
/usr/bin/helper Cx,    # child runs under a child profile defined inline
/usr/bin/helper Ux,    # UNCONFINED - drops confinement entirely. Avoid.

Omit the qualifier and the parser refuses the profile:

$ apparmor_parser -Q ./myprofile
AppArmor parser error ... at line 5: Invalid perms, 'x' must be
preceded by exec qualifier 'i', 'p', or 'u'

Network rules

network inet stream,         # TCP IPv4
network inet6 stream,        # TCP IPv6
network inet dgram,          # UDP IPv4

Network rules restrict what protocols and families a process can use.

Audit

sudo journalctl -k | grep audit
sudo ausearch -m appArmor --interpret    # human-readable

AppArmor denials appear in the kernel log with audit: and apparmor="DENIED".

AppArmor vs SELinux

FeatureAppArmorSELinux
Policy byPathLabel
Default onDebian, UbuntuRHEL, Fedora
Profile syntaxEasierHarder
GranularityPath-basedLabel-based (more granular)
Default policyProfiles shipped for common servicesTargeted policy
Toolingaa-genprof, aa-logprofaudit2allow, semodule

Both achieve the same goal. The choice is usually the distribution default: AppArmor on Debian-family, SELinux on RHEL-family.

Knowledge check

Knowledge check · 3 questions

  1. Q1. What command switches an AppArmor profile to enforce mode?

  2. Q2. AppArmor profiles are label-based like SELinux.

  3. Q3. Which of the following are AppArmor rule types? Select all that apply.

Passing score: 75%. Answers are checked in this browser.