41.6 AppArmor: Profile-Based MAC for Ubuntu and SUSE
Alright, let’s talk AppArmor. If SELinux is the over-engineered, paranoid security guard who demands to see your papers and calls his supervisor twice before letting you into your own house, AppArmor is the pragmatic bouncer who just checks the list. It’s path-based, not context-based, which is a fancy way of saying it cares about where you are, not who you are or what label you’re wearing. This makes it conceptually simpler and, frankly, a lot less of a headache to wrap your brain around on a Tuesday afternoon.
Its home turf is the Debian/Ubuntu and SUSE worlds, where it’s the default Mandatory Access Control (MAC) system. The core idea is beautiful in its simplicity: you create a profile for an application. This profile is just a list of rules that explicitly says what files that application can read, what directories it can write to, what network ports it can bind to, and so on. Anything not explicitly allowed is denied. This is the “whitelist” model, and it’s the only sane way to do security. You’re not trying to plug a million holes; you’re building a very small, very fortified room and only letting the application do the one job it’s supposed to do.
How Profiles Work: The Two Modes
AppArmor profiles can run in two modes: complain and enforce. This is its killer feature for learning and debugging.
- Complain Mode: The profile is active but not enforcing denials. It just logs them. The application runs as if it’s free, but AppArmor is quietly taking notes in
/var/log/syslogor/var/log/audit/audit.logabout everything it would have blocked. This is how you build a profile from scratch. You turn this on, run the application through all its paces, and then use the logs to generate the rules you actually need. - Enforce Mode: This is business time. The profile is active and will block any action that violates its rules. This is where your security posture actually gets teeth.
You can check the status of all profiles with sudo apparmor_status.
$ sudo apparmor_status
apparmor module is loaded.
22 profiles are loaded.
22 profiles are in enforce mode.
/sbin/dhclient
/usr/bin/evince
/usr/sbin/sssd
...
0 profiles are in complain mode.
0 processes are unconfined but have a profile defined.
Anatomy of a Profile
Profiles live in /etc/apparmor.d/. Let’s look at a real-world snippet, say for evince (the document viewer). The syntax is refreshingly human-readable.
# /etc/apparmor.d/usr.bin.evince
#include <tunables/global> # Include common variable definitions
/usr/bin/evince { # This is the profile for the binary at this path
#include <abstractions/base>
#include <abstractions/fonts>
#include <abstractions/gnome>
#include <abstractions/ibus>
# Allow reading the binary itself and all shared libraries
/usr/bin/evince mr,
/usr/lib/**/**.so mr,
# Allow reading user's documents and downloads
owner @{HOME}/Documents/ r,
owner @{HOME}/Documents/** r,
owner @{HOME}/Downloads/ r,
owner @{HOME}/Downloads/** r,
# Allow writing to the temporary directory
/tmp/** rw,
# Deny everything else. This is implied, but sometimes explicitly written.
deny /etc/shadow r, # Example of an explicit, overly cautious deny
}
Key things to notice: #include statements pull in common sets of rules (abstractions), which is brilliant for DRYness. The mr permission means mmap readable (which implies read) and r is just read. rw is read-write. The owner keyword ensures the rule only applies when the process runs as the file’s owner. The @{HOME} is a variable, defined in the tunables/global file. Simple, right?
Building Your Own Profile: The Right Way
The absolute worst thing you can do is try to write a profile from a blank text file. You’ll miss fifty things and drive yourself mad. The correct, sanity-preserving process is:
- Put the application into complain mode:
sudo aa-complain /path/to/binary - Use the application normally. Do everything it’s supposed to do. Run your tests, click all the buttons.
- Gather the logs: Use
sudo aa-logprofto parse the logs and interactively ask you about each denial. This tool is the real workhorse. For each event, it will suggest rules. You can choose to allow, deny, or ignore. This iterative process builds a 95% complete profile with minimal effort. - Review the generated profile in
/etc/apparmor.d/. Understand what it’s doing. Tweak it if needed. - Put it into enforce mode:
sudo aa-enforce /path/to/binary - Test again. Make sure you didn’t break anything. Check
dmesgor the logs for any new denials.
Common Pitfalls and The ‘Why’
- The Cache Problem: Applications love to cache things in
~/.cacheor~/.config. Your profile must allow writes to these specific subdirectories, not just the home folder.aa-logprofusually catches this. - The Abstraction Blind Spot: Abstractions are great, but they can be overly broad. An abstraction like
abstractions/sslgrants read access to a wide range of SSL certificate paths. This is convenient but slightly reduces the security principle of least privilege. Know what your includes are doing. - The /proc and /sys Conundrum: Modern applications need to read from various
/procand/syspseudo-filesystems to get information about the system. Theaa-logproftool is usually good at prompting for the specific files needed, but sometimes you get a rule that’s too broad, like/proc/** r. Try to narrow it down if you can, but sometimes you just have to live with a slightly noisy rule to make the app work. - The One Time You Need a Pun: I said wit over puns, but I have to admit: the
aa-logprofcommand is brilliantly named. You’re literally profiling your application by looking at its log of complaints. It’s perfect.
AppArmor’s beauty is in its practicality. It doesn’t try to solve every theoretical security problem. It solves the very real problem of “how do I stop this specific application from doing things it shouldn’t?” And it gives you the tools to answer that question without needing a PhD in security engineering. It’s your brilliant, pragmatic friend in the world of MAC. Use it.