AppArmor is the mandatory access control (MAC) of Debian, Ubuntu and
openSUSE: it confines each application in a profile that lists the files,
capabilities and network access it is entitled to, even once compromised.
Where SELinux labels files, AppArmor reasons by paths, which makes it
easier to pick up. This guide is for the Debian or Ubuntu administrator who
wants to confine a service: check the state of the profiles, generate
one for Nginx with aa-genprof, refine it with abstractions, then switch
it to enforce without breaking the service.
What you will learn
- Understand AppArmor's profile-based mandatory access control
- Check the state of profiles with
aa-statusand the enforce/complain modes - Generate a profile for a service and adjust it from the logs
- Switch a profile to blocking mode without breaking the application
- Diagnose an AppArmor denial in the logs
A short history
AppArmor was born in the early 2000s at the company Immunix, with the idea of creating a simpler alternative to SELinux, too complex for many users. Acquired by Novell in 2005, it was integrated into SUSE Linux Enterprise before joining other distributions such as Ubuntu and Debian.
Why use AppArmor?
AppArmor is much more than a simple configuration tool. It plays an essential role in protecting your Linux systems by setting up mandatory access control (MAC) that limits what applications can do, even if they are compromised. Here is why AppArmor is essential to strengthen the security of your servers and applications:
AppArmor acts as an extra layer of defence by limiting the permissions of applications to the resources they need. In case of a security flaw or an exploit in an application (such as a web server), AppArmor prevents the attacker from accessing critical files, executing commands or compromising the whole system. For example:
- If a vulnerability is exploited in Nginx, AppArmor can prevent access to
sensitive directories such as
/etc/passwdor/root/.
Each application is confined within a strict permission profile that determines exactly what it can do:
- Read or write specific files.
- Network access or command execution.
By minimising permissions, AppArmor limits the potential damage of an attack, even if an application is compromised.
Unlike reactive security tools such as antivirus software or firewalls, AppArmor acts upstream. It prevents unauthorised actions before they happen, by applying preventive security policies.
AppArmor provides full isolation of applications, preventing unwanted interactions with the rest of the system or with other applications.
AppArmor also offers full traceability of system access through its detailed logs:
- Enforce mode: unauthorised actions are blocked and recorded.
- Complain mode: violations are recorded so you can adjust profiles without disturbing the application.
These logs let you audit the behaviour of applications and quickly identify problems or suspicious access attempts.
AppArmor is therefore an essential tool for any Linux infrastructure that cares about keeping a high level of security. By limiting application permissions and strengthening their confinement, it protects your systems against a wide range of threats, making your servers more robust and resilient to attacks.
Is it complicated to implement?
Unlike SELinux, which relies on a global security policy that is often hard to configure, AppArmor bets on simplicity and modularity. Rather than examining the system as a whole, it focuses its protection on each application through individual profiles, describing precisely the resources it is entitled to. This more intuitive granular approach has won over a wide audience, from beginners to seasoned administrators, letting them quickly strengthen security without drowning in complex rules.
This simplicity has a trade-off to know in order to choose with your eyes open: AppArmor reasons by file path (pathname-based), not by inode label like SELinux. A hard link, a bind mount or a renamed binary can therefore escape a rule, and AppArmor places fewer kernel hooks than SELinux. It remains sufficient and much simpler in the vast majority of cases; SELinux keeps the edge in environments with maximum requirements.
Features and concepts
Before putting AppArmor into practice, it is essential to understand its mechanisms. Thanks to an approach based on individual profiles and great granularity in defining permissions, AppArmor offers a security solution that is both simple and effective. Here are the main concepts to remember:
- Operating modes:
- Enforce: unauthorised actions are blocked and logged
apparmor="DENIED". - Complain: implicit denials are not blocked but logged
apparmor="ALLOWED", which lets you adjust the profile without interrupting the application. Important pitfall: an explicitdenyrule is still applied even in complain mode. Complain is therefore not "without protection".
- Enforce: unauthorised actions are blocked and logged
- AppArmor profiles: each application is governed by a profile specifying the files, directories, libraries and network resources it can access. This isolates each program within its own security perimeter.
- Permission granularity: rules are defined down to the file path level, allowing very fine control over what the application is allowed to read, write or execute.
- Audit and reporting: when an application tries to access an unplanned resource, AppArmor generates detailed logs. They help understand the application's real needs, fine-tune profiles and follow the evolution of the security policy over time.
- Simplicity and flexibility: command-line tools such as aa-genprof, aa-complain, aa-enforce or apparmor_parser make it easy to create, modify and deploy profiles, without requiring deep security expertise. This flexibility makes it easy to adapt AppArmor to changes in the system and to newly deployed services.
Prerequisites
- A distribution where AppArmor is the native MAC: Debian, Ubuntu or openSUSE. On RHEL/Rocky, it is SELinux: to decide, see the comparison SELinux or AppArmor (in French).
- root access (or
sudo). - The
apparmor-utilspackage for theaa-*tools (andapparmor-notifyforaa-notify). - Preferably a test VM: a profile in enforce mode that is too strict can prevent a service from starting.
Installation and configuration
Before you start creating or modifying profiles, make sure AppArmor is properly installed and operational on your system. On popular Linux distributions, such as Ubuntu or Debian, AppArmor is often already present and enabled by default. If that is not the case, installation remains simple and quick.
To check the current state of AppArmor, you can use the following command:
sudo apparmor_statusapparmor module is loaded.134 profiles are loaded.39 profiles are in enforce mode.
4 profiles are in complain mode. transmission-cli transmission-daemon transmission-gtk transmission-qt
91 profiles are in unconfined mode. wpcom0 processes have profiles defined.0 processes are in enforce mode.0 processes are in complain mode.0 processes are in prompt mode.0 processes are in kill mode.0 processes are unconfined but have a profile defined.0 processes are in mixed mode.This report tells you whether the AppArmor kernel module is loaded, which mode your current profiles run in and how many processes are actually confined. AppArmor has no daemon: the kernel is what applies the profiles.
If you find that AppArmor is not installed, you can install it through your package manager. For example, on Ubuntu or Debian, enter:
sudo apt updatesudo apt install apparmor apparmor-utils apparmor-profiles apparmor-profiles-extraThese tools, including apparmor-utils, give you the features you need to
manage your profiles, switch them to complain or enforce mode, and
create new ones.
Once the installation is done, make sure AppArmor is enabled at boot. On
modern distributions using systemd, you can check the service with the
command below. The apparmor.service unit is of type oneshot with
RemainAfterExit=yes (read on Ubuntu 24.04 and Debian 13): it loads the
profiles then stops, so a healthy machine shows active (exited). The output
below, captured in an LXC container (note the zzz-lxc-service.conf
drop-in), shows inactive (dead) instead: profiles do not load there.
sudo systemctl status apparmor○ apparmor.service - Load AppArmor profiles Loaded: loaded (/usr/lib/systemd/system/apparmor.service; enabled; preset: enabled) Drop-In: /run/systemd/system/service.d └─zzz-lxc-service.conf Active: inactive (dead) Docs: man:apparmor(7) https://gitlab.com/apparmor/apparmor/wikis/home/If the service is not enabled, you can start it and enable it for the next reboots:
sudo systemctl start apparmorsudo systemctl enable apparmorOn Debian, Ubuntu and openSUSE, AppArmor is loaded by default without
any kernel parameter. Check it with
cat /sys/module/apparmor/parameters/enabled (which returns Y) and
cat /sys/kernel/security/lsm (which contains apparmor). The
security=apparmor (or apparmor=1) option is only used to re-enable it
if someone explicitly turned it off with apparmor=0 in GRUB. On a recent
installation, you therefore have nothing to add to the boot loader.
Creating and managing profiles
One of the main strengths of AppArmor lies in its management through individual profiles. Each profile defines precisely the resources a given application is allowed to access, which isolates it and limits the impact of a security flaw.
AppArmor profiles are stored in the /etc/apparmor.d/ directory, with one
file per application, named after the binary's path whose / become
dots: the profile of /usr/sbin/nginx is called usr.sbin.nginx. They are
simple text files, editable with your favourite editor, which makes them
easy to adapt to your needs.
Generate an initial profile
There are two ways to get a first profile: write it by hand then load it
with apparmor_parser, or let aa-genprof learn it by observing the
application. The first hides a pitfall about the loading mode.
A hand-written profile is loaded straight into enforce mode
Worth knowing before writing your first profile, because it goes against
the method taught everywhere, "complain first, enforce afterwards".
apparmor_parser -r loads the profile in enforce mode, immediately.
Measured on Ubuntu 24.04 with apparmor 4.0.1: the aa-status counter goes
from 23 to 24 profiles in enforce mode right after loading, without any
aa-complain being involved. The program is therefore blocked from the
first second, before you could observe what it needs.
The countermeasure fits in one command, to run right after loading:
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginxsudo aa-complain /usr/sbin/nginx # do not forgetaa-genprof, on the other hand, handles this switch for you: one more reason
to go through it when it is usable.
To create a base profile, use the aa-genprof tool:
sudo apt install nginx curlsudo aa-genprof /usr/sbin/nginxDebian 12 and 13 pitfall: aa-genprof cannot find the log
Since Debian 12, rsyslog is no longer installed by default and
/var/log/syslog does not exist (everything goes through journald).
Yet /etc/apparmor/logprof.conf only looks for /var/log/audit/audit.log,
/var/log/syslog and /var/log/messages (read on Debian 13,
apparmor-utils 4.1.0). aa-genprof and aa-logprof then fail with
ERROR: Can't find system log "/var/log/syslog". The workaround, keeping a
stream of the kernel log in a second terminal:
journalctl -b -k -e -f > /var/tmp/aa.log &sudo aa-genprof -f /var/tmp/aa.log /usr/sbin/nginxWhen aa-genprof or aa-logprof suggests abstractions, sort them:
the tool often suggests off-topic includes (for example
dovecot-common, postfix-common or totem for an nginx profile).
Refuse anything that does not match the application.
For example, when trying to start the Nginx service:
sudo systemctl restart nginxJob for nginx.service failed because the control process exited with error code.See "systemctl status nginx.service" and "journalctl -xeu nginx.service" for details.In the terminal window where you started aa-genprof, you will see the
access attempts blocked by AppArmor appear. You can allow or refuse
them, depending on your application's needs.
[(S)can system log for AppArmor events] / (F)inishReading log entries from /var/log/syslog.Complain-mode changes:Enforce-mode changes:
Profile: /usr/sbin/nginxPath: /var/log/nginx/error.logNew Mode: wSeverity: 8
[1 - /var/log/nginx/error.log w,](A)llow / [(D)eny] / (I)gnore / (G)lob / Glob with (E)xtension / (N)ew / Audi(t) / Abo(r)t / (F)inish
Profile: /usr/sbin/nginxPath: /var/log/nginx/error.logNew Mode: wSeverity: 8
[1 - /var/log/nginx/error.log w,](A)llow / [(D)eny] / (I)gnore / (G)lob / Glob with (E)xtension / (N)ew / Audi(t) / Abo(r)t / (F)inishAdding /var/log/nginx/error.log w, to profile.
= Changed Local Profiles =
The following local profiles were changed. Would you like to save them?
[1 - /usr/sbin/nginx](S)ave Changes / Save Selec(t)ed Profile / [(V)iew Changes] / View Changes b/w (C)lean profiles / Abo(r)tWriting updated profile for /usr/sbin/nginx.This tool guides you by observing the application's behaviour. In complain mode, AppArmor does not prevent access, but logs it. By testing Nginx, for example by restarting the service, you will see all the access attempts appear, which you then only have to allow or refuse.
The file built by aa-genprof is a good starting point, but it may need
adjustments to match your needs exactly.
# Last Modified: Fri Dec 13 06:45:45 2024abi <abi/4.0>,
include <tunables/global>
/usr/sbin/nginx { include <abstractions/base> include <abstractions/nameservice>
capability dac_override,
/usr/sbin/nginx mr, /var/log/nginx/access.log w, /var/log/nginx/error.log w, owner /etc/group r, owner /etc/nginx/mime.types r, owner /etc/nginx/nginx.conf r, owner /etc/nginx/sites-available/default r, owner /etc/nsswitch.conf r, owner /etc/passwd r, owner /run/nginx.pid r, owner /run/nginx.pid w,
}This file contains the basic rules for Nginx, allowing read and write access to some configuration and log files. You can refine it by adding or removing rules, depending on your application's needs.
Adjust and customise profiles
If we try to view the Nginx home page, we get:
curl http://localhost<html><head><title>403 Forbidden</title></head><body><center><h1>403 Forbidden</h1></center><hr><center>nginx/1.24.0 (Ubuntu)</center></body></html>The logs show that access was blocked:
sudo journalctl | grep -i apparmor | tail -n 10Dec 13 06:44:54 calm-tadpole kernel: audit: type=1400 audit(1734072294.310:319): apparmor="STATUS" operation="profile_replace" profile="unconfined" name="/usr/sbin/nginx" pid=2276 comm="apparmor_parser"Dec 13 06:45:00 calm-tadpole kernel: audit: type=1400 audit(1734072300.682:320): apparmor="DENIED" operation="capable" class="cap" profile="/usr/sbin/nginx" pid=2286 comm="nginx" capability=7 capname="setuid"Dec 13 06:45:45 calm-tadpole kernel: audit: type=1400 audit(1734072345.725:321): apparmor="STATUS" operation="profile_replace" profile="unconfined" name="/usr/sbin/nginx" pid=2444 comm="apparmor_parser"Dec 13 06:54:22 calm-tadpole kernel: audit: type=1400 audit(1734072862.240:322): apparmor="DENIED" operation="open" class="file" profile="/usr/sbin/nginx" name="/var/www/html/index.nginx-debian.html" pid=2534 comm="nginx" requested_mask="r" denied_mask="r" fsuid=33 ouid=0Once the initial profile is created, you can refine it by editing the file in
/etc/apparmor.d/ directly. The rules state the allowed actions (read,
write, execute) for each file or directory. This journal reports two
distinct gaps, and the generated profile hides a third one:
capname="setuid": the Nginx master runs as root and must change identity to start its workers aswww-data. It needscapability setuidandcapability setgid;- the read of
/var/www/html/index.nginx-debian.html: a rule on the directory alone (/var/www/html/ r,) is not enough, its content must also be covered with the**pattern; - the profile only allows specific files of
/etc/nginx/, whereas Nginx also reads theconf.d/,modules-enabled/andsites-enabled/directories. Without them it starts with no site at all, and nothing listens on port 80. The/etc/nginx/** r,rule covers the whole configuration, read-only.
abi <abi/4.0>,
include <tunables/global>
/usr/sbin/nginx { include <abstractions/base> include <abstractions/nameservice>
capability dac_override, capability setuid, capability setgid,
/usr/sbin/nginx mr, /etc/nginx/** r, /var/log/nginx/*.log w, /run/nginx.pid rw, /var/www/html/ r, /var/www/html/** r,}But we will see that there is a simpler way to add rules to an existing profile, using includes.
Include existing profiles
To avoid repeating common rules across several profiles, AppArmor offers
the include directive. It adds the rules of a shared file to a profile,
simplifying the management of shared permissions.
In our example, Nginx shares common rules with other services, such as lighttpd or Apache. To avoid repeating them, we can include them directly in the Nginx profile, inside its block:
/usr/sbin/nginx { include <abstractions/base> include <abstractions/nameservice> ...}These directives inject common rules, simplifying the management of shared permissions. You can also create your own include files, to group rules specific to your environment.
Include files are stored in the /etc/apparmor.d/abstractions/ directory.
apache2-commonapparmor_apiaspellaudio
...
waylandweb-datawinbindwutmpXxadxdg-desktopxdg-openAmong the last configuration files, we can see that the web-data rules
exist. Let us check what they contain:
cat /etc/apparmor.d/abstractions/web-data# ------------------------------------------------------------------## Copyright (C) 2002-2006 Novell/SUSE# Copyright (C) 2014 Canonical Ltd## This program is free software; you can redistribute it and/or# modify it under the terms of version 2 of the GNU General Public# License published by the Free Software Foundation.## ------------------------------------------------------------------
abi <abi/4.0>,
/srv/www/htdocs/ r, /srv/www/htdocs/** r, # virtual hosting /srv/www/vhosts/ r, /srv/www/vhosts/** r, # mod_userdir @{HOME}/public_html/ r, @{HOME}/public_html/** r,
/srv/www/rails/*/public/ r, /srv/www/rails/*/public/** r,
/var/www/html/ r, /var/www/html/** r,
# Include additions to the abstraction include if exists <abstractions/web-data.d>So we can include them in our Nginx profile, instead of the two
/var/www/html/ rules written by hand. Here is the complete profile:
abi <abi/4.0>,
include <tunables/global>
/usr/sbin/nginx { include <abstractions/base> include <abstractions/nameservice> include <abstractions/web-data>
capability dac_override, capability setuid, capability setgid,
/usr/sbin/nginx mr, /etc/nginx/** r, /var/log/nginx/*.log w, /run/nginx.pid rw,}After each change, reload the profile:
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginxChange the mode of a profile
After adjusting our profile, we can switch it to "enforce" mode to really block unauthorised access, then check the service:
sudo aa-enforce /usr/sbin/nginxsudo systemctl restart nginxps -o user=,comm= -C nginxcurl -s -o /dev/null -w "%{http_code}\n" http://localhost/The test reads on three signs. ps must show the master as root and at
least one worker as www-data, curl must answer 200, and
journalctl -k | grep DENIED must add nothing more. Measured on 1 October 2026
on Ubuntu 24.04 (Nginx 1.24, apparmor 4.0.1): this profile passes all three
checks, systemctl reload nginx included. The generated profile, without the
three additions described above, started with no visible error all the
same: systemctl showed active, but only the master ran and curl got no
answer. An active service is not a service that works: check it.
Conversely, if you want to put a profile back in "complain" mode to observe new actions, use:
sudo aa-complain /usr/sbin/nginxMaintain profiles over time
Applications evolve. New features, updates or modules may require profile adjustments. Temporarily put the profile back in complain mode to observe the new needs, then modify it and apply "enforce" mode again.
This iterative approach lets you keep precise and suitable profiles, strengthening the security of your services without disturbing how they work.
Troubleshooting
The table starts from the symptom you observe, gives the most likely cause, then the solution.
| Symptom | Probable cause | Solution |
|---|---|---|
aa-genprof: Can't find system log "/var/log/syslog" | Debian 12 or 13 without rsyslog (journald only) | journalctl -b -k -e -f > /var/tmp/aa.log then aa-genprof -f /var/tmp/aa.log |
A service no longer starts after aa-enforce | Path missing from the profile | Back to aa-complain, reproduce, aa-logprof, then aa-enforce |
| The profile "does not protect" in complain mode | Complain / enforce confusion | Normal: complain logs ALLOWED. Only explicit deny rules block in complain mode |
| A sandbox (Chrome, AppImage) breaks on Ubuntu 24.04 | apparmor_restrict_unprivileged_userns=1 (default) | A dedicated profile rather than a global deactivation (see the Ubuntu docs) |
| A disabled profile comes back | Link left in /etc/apparmor.d/disable/ | rm /etc/apparmor.d/disable/<profile> then apparmor_parser -r |
Put it into practice
Work with an AppArmor profile on a VM: switch it to complain mode, observe the logs, then switch it back to enforce. A timed drill validates the skill under exam conditions.
Key points
- AppArmor confines each application through a profile based on paths, simpler than SELinux but bypassable through a hard link / bind mount
- Loaded by default on Debian/Ubuntu/openSUSE, without a kernel parameter
- Enforce blocks and logs
DENIED; complain logsALLOWEDbut still applies explicitdenyrules - Work cycle:
aa-complain→ generate denials →aa-logprof→aa-enforce - On Debian 12 and 13, work around the missing
/var/log/syslogforaa-genprof/aa-logprof - Sort the abstractions suggested by
aa-genprof: refuse the off-topic ones
Knowledge check
Check that the essentials of this guide are acquired. The questions only cover what has just been explained here.
Knowledge check
Check what you have learned with this interactive quiz
How it works
- The timer starts when you click Start
- Multiple choice, true/false and short answer questions
- You can move between questions
- Detailed results are shown at the end
Starts the quiz and the timer
Review
(0/0)Score by domain
A domain groups several guides and gets several questions, so its score is more reliable than a per-topic score, which often rests on a single question.
Summary
Strengths
No topic passed at 80% on at least two questions yet.
Weak areas
No topic below the pass mark.
Recommended next
Hints before you try again?
Continue the course: File permissions and ownership: harden a Linux system
A new full quiz with random questions
Work again only on the questions you missed
Conclusion
AppArmor is a powerful and flexible tool to strengthen the security of your
Linux systems, while staying simple to configure thanks to its profile-based
approach. Throughout this guide, we explored its key features, from
installation to creating and managing profiles, along with concrete practical
cases. Thanks to abstractions such as web-data, AppArmor lets you
centralise permissions and simplify the configuration of common applications.
Whether you are a system administrator or an advanced user, AppArmor
offers you an effective way to limit applications' access to the resources
they need, thus reducing your system's attack surface. With interactive
tools such as aa-genprof and aa-logprof, adapting your profiles to the
specific needs of your applications becomes easy while keeping a high level
of security.
To go further, do not hesitate to:
- Test AppArmor on other services and applications of your infrastructure.
- Use the existing abstractions to simplify your profiles.
- Create custom rules suited to your specific environments.
By integrating AppArmor into your security strategy, you take one more step towards a robust, protected and better controlled Linux system. Secure your applications now and take control of your system resources with AppArmor.
FAQ: frequently asked questions
The questions below cover the points that come up most often about AppArmor: how it works, the difference with SELinux, the modes and profile management.
How it works
- Confines programs through security profiles
- Defines precisely the files, network capabilities and resources that can be reached
- Limits the damage even if an attacker compromises a privileged process
Mechanism
- Each confined program is associated with a text profile
- The profile defines the permissions:
- File access (read/write/execute)
- POSIX capabilities (
CAP_NET_BIND_SERVICE,CAP_SYS_ADMIN...) - Network and IPC rules
- At each syscall, AppArmor checks the authorisation
Profile syntax
profile nginx {
#include <abstractions/base>
/var/www/** r,
/var/log/nginx/* w,
}
Comparison
| Criterion | AppArmor | SELinux |
|---|---|---|
| Model | Path-based (paths) | Label-based (labels) |
| Complexity | Simpler | More complex |
| Granularity | Good | Very fine |
| Learning curve | Gentle | Steep |
AppArmor advantages
- Readable syntax
- Progressive deployment, profile by profile
- Ideal to start with
SELinux advantages
- No bypass through a hard link or bind mount (inode labels)
- Robust isolation for containers
- Certifications (PCI-DSS)
Ways to check
1. Full state:sudo aa-status
2. Quick check:cat /sys/module/apparmor/parameters/enabled
# Returns 'Y' if active
3. systemd service:systemctl status apparmor
4. Boot messages:dmesg | grep -i apparmor
5. Active LSMs:cat /sys/kernel/security/lsm
3 approaches
1. Automatic generation (recommended)sudo aa-genprof /usr/bin/my-program
Starts the program, observes its access and builds a profile interactively.2. Learning modesudo aa-complain /etc/apparmor.d/my-profile
# Use the application normally
sudo aa-logprof # Analyse and adjust
3. Writing by handprofile my-app {
#include <abstractions/base>
/etc/my-app/** r,
/var/log/my-app/* w,
}
Best practices
- Start restrictive
- Test in complain before enforce
Disable temporarily
sudo systemctl stop apparmor
# or
sudo aa-teardown
Disable at boot
- Edit GRUB:
sudo nano /etc/default/grub
# Add apparmor=0 to GRUB_CMDLINE_LINUX_DEFAULT
- Apply:
sudo update-grub
Re-enable
# Remove apparmor=0 or use:
apparmor=1 security=apparmor
Caution: prefer putting profiles in complain mode rather than disabling globally.Main modes
| Mode | Description | Use |
|---|---|---|
| enforce | Blocks AND logs | Production |
| complain | Logs only | Debug/Test |
| unconfined | No restriction | Disabled |
| kill | Kills the process | High security |
Commands
# Switch to enforce
sudo aa-enforce /etc/apparmor.d/profile
# Switch to complain
sudo aa-complain /etc/apparmor.d/profile
Profile types
- Local: specific to the machine
- System: shipped by packages (nginx, mysql...)
Interactive method (recommended)
sudo aa-logprof
Analyses the logs and suggests changes.Manual method
- Edit the profile:
sudo nano /etc/apparmor.d/usr.sbin.nginx
- Reload:
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
System updates
sudo apt update && sudo apt upgrade apparmor apparmor-profiles-extra
Local overrides
Use/etc/apparmor.d/local/ to customise without modifying system profiles.Tree
/etc/apparmor.d/
├── abstractions/ # Reusable rules (#include)
├── tunables/ # Variables (@{HOME}, @{PROC})
├── local/ # Local overrides
├── disable/ # Links to disable
├── cache/ # Compiled profiles
└── usr.sbin.nginx # Active profiles
Other locations
- Extra profiles:
/usr/share/apparmor/extra-profiles/ - Cache:
/var/cache/apparmor/
Naming convention
The profile for/usr/sbin/nginx is called usr.sbin.nginxEssential commands
# Reload a profile (replace)
sudo apparmor_parser -r /etc/apparmor.d/profile
# Add a new profile
sudo apparmor_parser -a /etc/apparmor.d/profile
# Reload all profiles
sudo systemctl reload apparmor
Useful options
| Option | Description |
|---|---|
-W |
Write to the cache |
-T |
Skip the cache |
-Q |
Do everything except loading into the kernel (syntax test) |
Check
aa-status | grep my-profile
Definition
The enforce mode is the production mode, where rules are strictly applied.Behaviour
- Any unauthorised access is blocked
- Violations are logged with
DENIED - Example: nginx accessing
/etc/shadow→EACCESerror
Enabling
sudo aa-enforce /etc/apparmor.d/usr.sbin.nginx
Or in the profile:profile nginx flags=(enforce) {
...
}
Check
aa-status # Lists the profiles in enforce mode
It is the default mode for loaded profiles.Definition
The complain (audit) mode is a permissive mode that logs without blocking.Use cases
- Create new profiles
- Debug access problems
- Prepare a move to enforce
Enabling
sudo aa-complain /etc/apparmor.d/profile
Typical workflow
complain → observe → aa-logprof → test → enforce
Logs
ALLOWED messages show what would be blocked in enforce mode.Caution: in complain mode, only the explicit deny rules of the profile are still applied; everything else goes through.Log sources
1. Audit log:sudo ausearch -m AVC -ts today
grep apparmor /var/log/audit/audit.log
2. Syslog:grep -i apparmor /var/log/syslog
3. Journald:sudo journalctl -k | grep apparmor
journalctl _AUDIT_TYPE_NAME=APPARMOR
Message format
apparmor="DENIED" operation="open"
profile="/usr/bin/app" name="/etc/secret"
Tools
aa-notify -s 1 -v # Real-time notifications
aa-logprof # Interactive analysis
Security
- Confinement of applications
- Limits the impact of flaws (even root)
- Defence in depth
Ease
- Readable path-based syntax
- Progressive deployment per application
- Interactive tools (
aa-genprof,aa-logprof)
Performance
- Profiles compiled and cached
Integration
- Profiles for common services (Apache, MySQL, Docker)
- Native support in LXC/LXD and Kubernetes
Maintenance
- Updates preserve customisations
- Complain mode for risk-free tests
Enabled by default
- Ubuntu (since 7.10)
- Debian (since 10 Buster)
- openSUSE / SLES
- Linux Mint
- Pop!_OS
Available (not default)
- Arch Linux (
pacman -S apparmor) - Fedora (prefers SELinux)
- Gentoo
Containers
- Docker: auto-generated profiles
- LXC/LXD: native support
- Kubernetes: through
securityContext
Check
cat /sys/kernel/security/lsm
# Must include 'apparmor'
AppArmor has been upstream in the Linux kernel since 2.6.36.