Clean Up Stale AppArmor Profiles with aa-remove-unknown
aa-remove-unknown clears AppArmor profiles that stay loaded in the kernel after their file vanishes from /etc/apparmor.d/. Delete a profile by hand, remove a package, or roll back a deployment, and you can end up with exactly that: a ghost policy with nothing on disk behind it. Budget about ten minutes for a careful review, not a quick run. You need the AppArmor utilities package, a shell, and root for the inventory and removal steps.
The route
Jump straight to the step you need, or tick off Done means at the end.
This guide covers the installed AppArmor package version 4.0.1really4.0.1-0ubuntu0.24.04.7; the local manual identifies the utility itself as AppArmor 4.0.1. The command changes loaded kernel policy. It does not touch anything under /etc/apparmor.d/.
1. See what the tool actually compares
- It reads two lists. Profile names on disk in
/etc/apparmor.d/, and profile names currently loaded into the kernel. - It flags the mismatch. Any loaded name that is missing from the file inventory is a candidate for removal.
- An entry is a name, not a path. What it prints is a loaded profile name, never a file it is about to delete.
$ command -v aa-remove-unknown
/usr/sbin/aa-remove-unknown
$ aa-remove-unknown --help
usage: /usr/sbin/aa-remove-unknown [options]
Remove profiles unknown to the system
Options:
-h, --help Show this help message and exit
-n Dry run; don't remove profiles
Checkpoint
Stop here if you expected this to delete obsolete files, disable AppArmor altogether, or edit policy text. It does none of those things.
2. Run the dry run first
The -n option only prints what would be removed, so start there every time:
$ sudo aa-remove-unknown -n
Would remove 'example//null-/usr/bin/whoami'
Would remove 'example'
That output is the manual's sample, not a forecast of yours: what you see depends entirely on the profiles loaded on your host. Empty output is a good result too, it just means nothing loaded is missing from disk.
An unprivileged run cannot read /sys/kernel/security/apparmor/profiles and exits with an error. That is a permissions failure, not proof of a clean system. Use sudo, then check the exit status rather than trusting the absence of text on screen:
$ sudo aa-remove-unknown -n
$ status=$?
$ printf 'dry-run status: %s\n' "$status"
dry-run status: 0
A successful dry run finishes without an AppArmor filesystem error. If it complains that it cannot read the loaded-profile list, stop and fix that before you go anywhere near removal.
3. Investigate every name it flags
Before touching kernel state, chase down each candidate. Start by listing what actually exists on disk:
$ sudo find /etc/apparmor.d -maxdepth 1 -type f -printf '%f\n' | sort
$ sudo aa-remove-unknown -n
- Watch for generated profiles. Names with spaces or AppArmor namespace qualifiers can look unfamiliar even when they are perfectly legitimate.
- Check who owns the file. A name that looks stale is not automatically safe to drop.
$ dpkg-query -S /etc/apparmor.d/PROFILE_FILE
Swap PROFILE_FILE for a real filename from the directory. If a package owns it, this becomes a packaging or service-management decision, not a job for this tool: the comparison is mechanical and has no idea whether a profile is still needed.
Safety warning
Removal can weaken confinement for a process that is still running or due to start again. Do not wave a name through just because its file is hard to find. Resolve ownership and lifecycle first.
4. Remove only what you've cleared
Once every flagged name is understood and the maintenance window allows a policy change, drop -n:
$ sudo aa-remove-unknown
Removing 'example//null-/usr/bin/whoami'
Removing 'example'
It reports each profile it removes. There is no flag to narrow this to one profile: it always acts on the full comparison. That is exactly why steps 2 and 3 matter so much.
Checkpoint
Save this output with your change record. If it lists a profile you never approved, stop making further changes and preserve the evidence.
5. Undo a removal that turns out to be wrong
There is no undo flag. The command never touches /etc/apparmor.d/, so recovery means restoring or locating the intended profile file, not recreating one from memory:
$ sudo apparmor_parser --replace /etc/apparmor.d/PROFILE_FILE
- Restore the real file first. Pull it from the package or your deployment copy; never improvise policy text.
- Reload it with the parser. The installed parser documents
--replaceas replacing an AppArmor definition. - Confirm, don't assume. A successful parser run is not proof the service is protected again: check with your normal AppArmor monitoring.
- Use the right recovery path. Reinstall or repair the owning package through your normal process, or trigger the service's own regeneration procedure. Keep the dry-run and removal output until recovery is verified.
Common traps
Permission denied: the utility could not read the AppArmor profiles interface. Rerun with the privilege your host requires and confirm AppArmor's securityfs is mounted. Do not read this error as a clean inventory.
Unexpected names: stop before removing anything. Match the name against a profile file and its owning service or package; namespace-qualified names rarely look like the filename you expect.
Parser or profile errors: fix the profile source first. A broken policy inventory is no basis for unloading anything. Rerun the dry run once the source is fixed.
No output: normal, and it means nothing loaded is unknown, but only when the command also exits successfully. Capture the exit status immediately if you're scripting this.
Done means
- Version confirmed: you identified the installed
aa-remove-unknownand AppArmor version. - Dry run clean: it completed successfully with enough privilege to read AppArmor state.
- Every candidate checked: each proposed removal was matched to an ownership or lifecycle decision.
- Only the reviewed run applied: you ran the full comparison without
-nonly once you'd cleared it. - Recovery ready: the output was kept, and any wrong removal was fixed by restoring and reloading the correct profile.