Verify AppArmor coverage and audit profiles
As a Security Engineer, your priority is to verify that the defense-in-depth layer provided by AppArmor is active and effective across your fleet. You don't always need to write profiles yourself; often, your job is to ensure the existing profiles provide meaningful confinement while not breaking the production.
In this guide, you will learn: - How to verify AppArmor coverage - How to audit profile quality - How to use AppArmor for incident response
Prerequisites
- Install
apparmor-utilsandapparmor-notify
Verify the coverage
Check profiles status
AppArmor restrictive profiles can be in two modes: * Enforce: Blocks and logs any behavior not explicitly allowed by the profile. * Complain: Logs allowed and disallowed behavior but does not block anything. Used for debugging.
Check the status of all profiles on your system:
sudo aa-status
You can also filter by mode to see which applications are in complain or enforce mode:
sudo aa-status --filter.mode=enforce # or --filter.mode=complain
When possible, profiles should be in enforce mode.
Check for unconfined processes
Use aa-unconfined to identify processes with open network sockets that have no AppArmor profile loaded.
sudo aa-unconfined
These processes should be confined, especially if they provide a security-sensitive service.
Enable a disabled profile
If a profile is disabled or not in enforce mode, you can enable it with:
sudo aa-enforce /etc/apparmor.d/profile.name
If the profile doesn't exist, learn how to confine your application by following the tutorial Confine Your First Application.
Harden profiles
Distributions ship profiles, but they are generic to ensure compatibility across different environments. You can improve security by hardening these profiles to only allow the behaviors strictly needed for your specific use case.
Let's take the example of the curl profile:
profile curl /usr/bin/curl {
include <abstractions/base>
# ...
# allow writing output to $HOME, /tmp (see -o option)
file w @{HOME}/**,
file w /tmp/**,
# ...
# Site-specific additions and overrides. See local/README for details.
include if exists <local/curl>
}
Suppose that in your environment, curl should only write data to @{HOME}/curl/, and not to any other directory in home. You can enforce this restriction using a local override.
- Create or open the file
/etc/apparmor.d/local/curl. -
Add the following content:
audit deny file w @{HOME}/**, priority=1 file w @{HOME}/curl/**,Here, we block (
deny) writes (w) to@{HOME}/**, while explicitly allowing writes to@{HOME}/curl/**. Theauditflag ensures every blocked attempt is logged.The
priority=1flag ensures this rule takes precedence over the deny rule. -
Reload the profile with:
sudo apparmor_parser -r /etc/apparmor.d/curl
Congratulations! You have successfully hardened the curl profile.
As you made your modifications to a local include file, your changes are easy to review and will persist when the main profile is updated.
Forensics and incident response
As a Linux Security Module, AppArmor provides high-quality system-level behavioral logs which can be helpful to detect attack attempts and compromised services. These logs can usually be found in the kernel logs (e.g. /var/log/syslog).
Check and follow AppArmor logs with:
sudo dmesg -w | grep 'apparmor'
You can also use aa-notify -pm to receive real-time desktop notifications whenever denials occur.
If you see a denial for /bin/bash from your web server profile:
type=1400 audit(1612300000.000:99): apparmor="DENIED" operation="exec" profile="nginx" name="/bin/bash" ...
This tells you:
- The Attack: Someone exploited a vulnerability (RCE) in Nginx.
- The Payload: They attempted to spawn a shell (
/bin/bash). - The Result: AppArmor blocked the payload execution.
- Action: Isolate the node immediately. The service is compromised, even if the shell failed.