Boosting productivity with AppArmor's interactive tools: creating a new profile with aa-genprof directly from a target executable, allowing and denying each request that appears, updating a profile from denial logs with aa-logprof, and verifying the final result.

In episode 4 you wrote profiles manually — and you certainly felt how detailed that work is: listing every library, every path, every glob pattern. In the real world, a production application can produce hundreds of legitimate denials during boot and normal operation. Writing all of that by hand is wasteful. The solution: two interactive tools we'll master today — aa-genprof for creating new profiles and aa-logprof for updating existing ones.
Imagine the difference between writing an SOP by opening every cabinet and noting each allowed access one by one, versus living through one normal workday while an assistant records everything you touch, then turns it into an SOP automatically. That's the difference between writing manually and using these two tools.
aa-genprof creates a new profile skeleton directly from the target executable. Its workflow resembles learning mode: the tool runs the application in complain mode, has you perform normal work scenarios, then offers every access that appears for you to allow or deny.
Start by removing the old profile (if any) and running the generator:
sudo aa-genprof /usr/sbin/nginxThe tool will create a very minimal initial profile and show an interactive prompt like this:
Writing updated profile for /usr/sbin/nginx.
Profiling: /usr/sbin/nginx
Seen 2 log entries
[(S)can path (I)nherit (P)rofile (C)omplain (N)ew (A)llow / (D)eny / (G)lob
/ (R)emove] (S)can for new eventsThe application is now running in complain mode — any access that isn't allowed will show up in the logs, and aa-genprof offers them one by one. This is where you have to work: in another terminal window, run the application's normal work scenarios. For nginx for example, restart the service, request a few pages, let it write logs.
Once the scenarios are done, go back to the aa-genprof window and press S to scan for new log entries. The tool will show one denial at a time:
Reading log entries from /var/log/syslog.
Profile: /usr/sbin/nginx
Path: /etc/nginx/nginx.conf
New Mode: r
[1 - /etc/nginx/nginx.conf r]
(A)llow / (D)eny / (I)gnore / (G)lob / (G)lob_ext / (N)ew / (A)udit
[A]llow changes for this profile?For each prompt, the main choices:
| Choice | Meaning |
|---|---|
| A (Allow) | Allow the access exactly as requested |
| D (Deny) | Add it as a deny rule — the access stays blocked |
| I (Ignore) | Ignore this entry; it won't enter the profile |
| G (Glob) | Expand the path into a glob pattern, e.g. /etc/nginx/** |
| A (Audit) | Log this access for observation |
The principle to hold onto: allow what's legitimate, deny what's suspicious. When in doubt whether a path needs to be accessed, choose Ignore first and see if the application still works. If errors appear, allow it on the next iteration. After all entries are processed, press S then F to save — aa-genprof writes the complete profile to /etc/apparmor.d/ and loads it.
aa-logprof is aa-genprof's sibling for profiles that already exist. It reads denial logs, compares them with the loaded profile, and offers updates — without having to rewrite the whole profile. This is the main tool for long-term profile maintenance.
sudo aa-logprofaa-logprof scans logs from the default sources (/var/log/syslog, journald, or auditd), shows denials not covered by the profile, and offers the same choices as aa-genprof. Example prompt:
Profile: /usr/sbin/nginx
Path: /var/lib/nginx/cache/index.html
New Mode: w
[1 - /var/lib/nginx/cache/** w]
(A)llow / (D)eny / (I)gnore / (G)lob / (G)lob_ext / (N)ew / (A)udit
[A]llow changes for this profile?Notice the G (Glob) option here: nginx writes many files in /var/lib/nginx/cache/ whose names are hashed. Allowing them one by one makes no sense — press G to turn it into /var/lib/nginx/cache/** w and allow it all at once. This ability to abstract into globs is what makes aa-logprof more than a denial filler: it also tidies up profiles so they don't bloat.
A classic problem on production systems: logs flooded with repeated, irrelevant denials — what's commonly called deny noise. Example: a daemon trying to read a file that doesn't exist, or a process checking several alternative paths before finding the right one. aa-logprof offers several ways to handle this:
| Choice | Right use |
|---|---|
| I (Ignore) | A one-off denial that doesn't recur — keep it out of the profile |
| D (Deny) | A path that must stay blocked — make it an explicit deny |
| A (Audit) | Want to keep monitoring this path without allowing it |
The most common combination for reducing noise: Ignore for unimportant denials, Deny for dangerous paths that get accessed often. The goal — like a tidy SOP — is a profile that contains only the decisions that are actually needed, not every denial that ever happened.
Tip
Get into the habit of working from a bounded log: use aa-logprof -f <log-file> to process a single specific log file, for example one day's log or one debugging session. Processing the entire log at once will drown you in hundreds of prompts.
After aa-genprof and aa-logprof finish, your work isn't over. The interactive tools produce decisions from prompts — some of them may be too broad or incomplete. Get into the habit of three verification steps:
First, read the resulting profile — make sure no odd rule slipped through:
sudo cat /etc/apparmor.d/usr.sbin.nginxSecond, validate the syntax and make sure the profile is in the right mode:
sudo apparmor_parser -Q /etc/apparmor.d/usr.sbin.nginx
sudo aa-status | grep nginxThird — and most important — test the application thoroughly. Run all normal work scenarios: restart, requests, log rotation, config reload. If the application works without new denials, the profile is ready for enforce. If there are legitimate new denials, that's not a failure — it's the next iteration, which just needs aa-logprof once more.
Important
Never promote a profile to enforce right after aa-genprof finishes. The scenarios you ran during learning mode almost certainly don't cover every application behavior — for example a midnight log rotation or a database connection that only activates after a few hours. Give it time in complain, then promote once you're confident.
aa-genprof. An empty learning mode produces an empty profile. Exercise the application with realistic scenarios before scanning the logs.aa-logprof -f <file> so you can still read every prompt wisely.In episode 5 we mastered the two most productive tools in the AppArmor ecosystem: aa-genprof for creating new profiles from an executable with a learning-mode workflow, and aa-logprof for updating existing profiles from denial logs — complete with deny-noise filtering strategies and verification of the tool-generated profiles.
The core takeaways:
aa-genprof <path> creates a new profile skeleton and guides you through allowing/denying access one by one.aa-logprof updates existing profiles from logs — the primary long-term maintenance tool.In episode 6, we'll extend profile capabilities to the network and privilege dimensions: network & capabilities rules — controlling TCP/UDP and Unix sockets, using capability for granular privileges, and rlimit to bound process resources. Keep your momentum, because from here your profiles start protecting applications on every dimension, not just files!