Read AppArmor Policy State with aa-status
aa-status tells you in one command whether AppArmor is enabled, what policy is loaded, and what mode it runs in. It also tells you the difference between "nothing to report" and "you don't have permission to look", which matters more than it sounds. Allow about ten minutes. The examples target AppArmor 4.0.1, as supplied by the Ubuntu package installed on this machine.
The route
Jump straight to the step you need, or tick off Done means at the end.
1. Check the installed command
Confirm which executable runs and which package version provides it. Both of these are read-only, so no privilege needed yet:
$ command -v aa-status
/usr/sbin/aa-status
$ dpkg-query -W -f='${Package} ${Version}\n' apparmor
apparmor 4.0.1really4.0.1-0ubuntu0.24.04.7
The installed manual describes aa-status as a report of the current confinement state. Option names and output details can vary on older distributions, so treat your local manual as the final word when your package version differs.
Checkpoint
The binary and version are confirmed. If command -v finds nothing, install or repair the AppArmor utilities before continuing.
2. Get a yes-or-no answer with --enabled
When a script only needs a health check, --enabled gives you a status code instead of a full report:
$ aa-status --enabled
$ printf 'aa-status exit status: %s\n' "$?"
aa-status exit status: 0
- 0 AppArmor is enabled and policy is loaded.
- 1 not enabled, or not loaded.
- 2 enabled, but no policy loaded.
- 3 AppArmor control files are missing under
/sys/kernel/security/. - 4 insufficient privilege to check.
- 42 internal error.
Those are distinct diagnostics, not one generic failure, so a health check should branch on the status, not scrape stdout for text:
if aa-status --enabled; then
echo 'AppArmor is enabled with policy loaded'
else
status=$?
printf 'AppArmor check failed with status %s\n' "$status" >&2
fi
3. Run the full report with sudo
For profile and process detail, run it with elevated privilege. The default report is the same as --verbose:
$ sudo aa-status
apparmor module is loaded.
110 profiles are loaded.
102 profiles are in enforce mode.
8 profiles are in complain mode.
Out of 129 processes running:
13 processes have profiles defined.
8 processes have profiles in enforce mode.
5 processes have profiles in complain mode.
Those numbers are the manual's sample, not a prediction for your host: counts change as services start, stop and reload, so look for the shape of the report rather than an exact match.
A non-root run on this machine prints You do not have enough privilege to read the profile set. and exits with status 4. That's a permissions result, not evidence that AppArmor is disabled.
Checkpoint
Run it once with sudo and note whether the module is loaded, how many profiles are loaded, and whether any are in complain mode. Nothing here changes policy.
4. Pull one number when that's all a script needs
- --profiled counts loaded policies.
- --enforced counts enforcing policies.
- --complaining counts non-enforcing policies.
- --kill, --prompt, --special-unconfined, --process-mixed report narrower policy or process categories.
$ sudo aa-status --enforced
# one host-specific integer is printed here
That number is host-specific: don't treat the comment as literal output. Keep the exit status too, since a failed read can leave you with no number and status 4. The newer, more explicit form does the same job for a chosen data set:
$ sudo aa-status --show=profiles --count
102
--show takes profiles, processes or all (the default). --count prints only the count and implies quiet output. Don't stack several report options expecting them to merge; the manual is explicit that it's one argument at a time.
5. Use JSON for monitoring
For a program rather than a person, ask for JSON and parse it with a real parser rather than grep: profile names and process entries can contain characters that break line-oriented matching. --json targets machine consumption; --pretty-json is the same data formatted for people.
$ sudo aa-status --json > /tmp/aa-status.json
$ jq . /tmp/aa-status.json
$ printf 'aa-status exit status: %s\n' "$?"
aa-status exit status: 0
That temporary file is just an example output location, not policy configuration, and it's safe to remove after inspection.
Tip
AppArmor's upstream project has fixed malformed JSON in the 4.x line, including output selected with --show. Parse the output and check the exit status rather than trusting a hand-written text pattern. If an older distribution emits invalid JSON, check its package version and its own release notes before building automation on top of it.
6. Narrow a report with filters
Filters reduce what's displayed without touching policy, and they take POSIX regular expressions. This lists enforcing profiles only:
$ sudo aa-status --show=profiles --filter.mode='^enforce$'
# profile entries for this host are printed here
- --filter.profiles matches the confining profile name.
- --filter.pid matches process IDs.
- --filter.exe matches executable names.
Quote the expression so the shell doesn't eat special characters. A filter matching nothing doesn't prove policy is absent, it may just be too narrow: drop filters first, then try sudo aa-status --show=all to separate a matching mistake from a real policy or permissions problem.
Common traps
aa-status only reads the kernel's AppArmor control files and process information. It never loads, unloads, enforces or complains a profile, so a surprising count is not a reason to edit a profile or restart a service until you know which of these it is:
- Status 1: AppArmor is not enabled or loaded.
- Status 2: enabled, but no policy loaded.
- Status 3: control files unavailable under
/sys/kernel/security/. - Status 4: not enough privilege to read the control files.
- Status 42: internal error.
Rerun with sudo first. If it still fails, inspect the security filesystem and service state with your distribution's normal diagnostic tools.
Warning
Don't mount filesystems, reload profiles or restart production services just to make a status command return zero. Those are operational changes with their own outage and policy risk, way out of proportion to a status check.
There's no undo needed here: the examples only read state and, at most, write a temporary report under /tmp. If you created that file and no longer need it, remove that exact path:
$ rm -- /tmp/aa-status.json
Done means
- Version confirmed: you identified the installed
aa-statusand AppArmor package version. - Health check separated from report: you used
--enabledfor a yes-or-no exit-status check. - Privilege understood: you used
sudofor profile and process detail and recognised status 4 as a privilege failure, not a disabled system. - Right output picked: you chose counts, JSON or filters for the job instead of scraping a verbose report.
- Nothing changed: you treated the output as a read-only snapshot throughout.