Generic AppArmor denial log parser and rule generator — parses journalctl/auditd/syslog denials, suggests rules, compares against existing profiles. Zero dependencies, pure Python, ships as a single binary.
https://faaleoleo.io
- Python 99.8%
- Shell 0.2%
| .forgejo | ||
| docs | ||
| src/aa_log_parser | ||
| tests | ||
| .gitignore | ||
| aa-log-parser.spec | ||
| build.sh | ||
| CHANGELOG | ||
| CLAUDE.md | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| RELEASE_NOTES.md | ||
aa-log-parser
Generic AppArmor denial log parser and rule generator — works with any AppArmor profile.
Copyright © 2026 faaaleoleo.io dev team · License: BSD 2-Clause
Features
- Accurate parsing of journalctl, auditd, and syslog denial log formats (both quoted and unquoted field styles)
- Filter by profile name (substring match) to focus on one application at a time
- Smart path generalization —
/var/lib/nginx/cache/x→/var/lib/nginx/**,.pidfiles →*.pid,.logfiles →*.log - Security block detection — flags sensitive paths (
/etc/shadow, SSH keys, kernel devices,/proc/*/mem,/etc/sudoers, …) and dangerous capabilities (dac_override,net_raw,setuid, …) that must stay denied - Profile comparison — identifies which suggested rules are already covered by the existing profile, which are missing, and which are intentional security blocks
- Abstraction hints — recommends
#include <abstractions/base>,<abstractions/nameservice>,<abstractions/ssl_certs>, and 20 other standard AppArmor bundles to replace groups of low-level rules with a single include - Output as text, JSON, or CSV for terminal review, scripting, dashboards, and CI pipelines
- Zero runtime dependencies — pure Python standard library
- Distributable as a single self-contained binary via PyInstaller
Install
From source (recommended for development)
git clone https://git.faaleoleo.io/faaleoleo-dev-public/Faaleoleo-AppArmor-Parser.git
cd Faaleoleo-AppArmor-Parser
python3 -m venv .venv && source .venv/bin/activate
pip install -e .
aa-log-parser --version
As a compiled binary (no Python required on target)
./build.sh # produces dist/aa-log-parser
sudo cp dist/aa-log-parser /usr/local/bin/
aa-log-parser --version
Without installing
python3 -m aa_log_parser --help
Quick start
# Parse all AppArmor denials from the system journal
journalctl | grep apparmor | grep DENIED | aa-log-parser parse
# Filter to one application and generate suggested rules
aa-log-parser generate -i denials.log --profile-name usr.sbin.nginx
# Full analysis — profile file auto-located in /etc/apparmor.d/
aa-log-parser analyze -i denials.log --profile-name usr.sbin.bacula-dir
# Explicit profile path (useful when the profile is not yet deployed)
aa-log-parser analyze -i denials.log -p ./usr.sbin.myapp
# JSON output for scripting or dashboards
aa-log-parser analyze -i denials.log --profile-name usr.sbin.nginx --format json
Commands
| Command | Description |
|---|---|
parse |
Parse denial logs and show statistics |
generate |
Parse and generate suggested AppArmor rules |
analyze |
Generate rules and compare against an existing profile — includes abstraction hints |
compare |
Show only the comparison result (missing / covered / blocked) |
summary |
Print denial statistics only (fastest) |
Options
| Option | Description |
|---|---|
--profile-name NAME |
Filter by profile name (substring match); also infers the profile file path |
-i, --input FILE |
Input log file (default: stdin) |
-o, --output FILE |
Output file (default: stdout) |
-p, --profile FILE |
Profile file to compare against (default: /etc/apparmor.d/<profile-name>) |
--format text|json|csv |
Output format (default: text) |
--verbose |
Print extra progress information |
--version |
Print version and exit |
Example output (analyze command)
## COMPARISON WITH EXISTING PROFILE
Rules Generated: 18
Rules Covered: 6
Rules Missing: 11
Security Blocks: 1
### Missing Rules (add to local override):
#### Suggested abstractions (add near the top of the profile):
#include <abstractions/base>
# Basic system access: locale, timezone, /proc/<pid>, libc
# Covers 3 path(s): /usr/lib/locale/en_US.UTF-8/LC_CTYPE, /usr/share/zoneinfo/UTC, /proc/28341/status
#include <abstractions/ssl_certs>
# SSL/TLS certificate bundles: /etc/ssl/certs/, /etc/ca-certificates/
# Covers 1 path(s): /etc/ssl/certs/ssl-cert-snakeoil.pem
#### Remaining rules (not covered by any abstraction):
/etc/postgresql/15/main/postgresql.conf rw,
/var/log/postgresql/*.log rw,
/etc/ssl/private/ssl-cert-snakeoil.key rw,
### Security Blocks (do NOT add):
# SECURITY BLOCK: Denied 1 time(s): open on /etc/shadow
# /etc/shadow rw,
Documentation
| Document | Contents |
|---|---|
| Usage | All commands, options, and worked examples |
| Configuration | Security block lists, abstraction hints, path generalization, formats |
| Architecture | Module structure, class overview, data flow |
| Development | Dev setup, tests, extending the tool, building the binary |
| Caddy example | Full walkthrough: zero to enforce-mode profile for Caddy |
Project layout
aa-log-parser/
├── src/aa_log_parser/ Python package
│ ├── cli.py Entry point and command dispatch
│ ├── parser.py LogParser — filtering, generalization, comparison, abstraction hints
│ ├── models.py AppArmorDenial, AppArmorRule
│ ├── formatters.py text / JSON / CSV / summary formatters
│ └── constants.py SENSITIVE_PATHS, DANGEROUS_CAPABILITIES, ABSTRACTION_HINTS,
│ GENERALIZABLE_PREFIXES, EXTENSION_GLOBS, OPERATION_TO_PERMISSION
├── tests/ pytest suite (576 tests)
├── docs/ Full documentation
├── build.sh Build self-contained binary
└── aa-log-parser.spec PyInstaller spec
How it works — the three-step pipeline
1. Parse — read denial log lines; extract profile, operation, path, pid, etc.
2. Generate — group by (operation, path); generalize paths; detect security blocks
3. Compare — diff generated rules against the installed profile;
suggest AppArmor abstractions for groups of low-level rules
The tool works with any AppArmor profile on any application. It reads what the kernel actually denied — there is no hardcoded knowledge of nginx, caddy, postgresql, or any specific application. The only hardcoded knowledge is what should always remain denied (sensitive paths, dangerous capabilities) and which standard AppArmor abstraction bundles cover common system-library paths.
License
BSD 2-Clause — see LICENSE.