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%
Find a file
faaleoleo dev team 8e57eacead
All checks were successful
CI — lint, test / Lint (push) Successful in 2s
CI — lint, test / Unit tests (push) Successful in 10s
One more CI/CD error fixed
2026-06-17 20:28:21 +03:00
.forgejo fix: use python3 -m venv for all pip installs (PEP 668 / Debian 13) 2026-06-16 00:24:36 +03:00
docs bugfix 2026-06-17 15:48:39 +03:00
src/aa_log_parser One more CI/CD error fixed 2026-06-17 20:28:21 +03:00
tests CI/CD fixed 2026-06-17 18:42:07 +03:00
.gitignore fix: add aa-log-parser.spec and unignore it (.gitignore negation) 2026-06-17 14:28:43 +03:00
aa-log-parser.spec fix: add aa-log-parser.spec and unignore it (.gitignore negation) 2026-06-17 14:28:43 +03:00
build.sh bugfix 2026-06-17 14:20:52 +03:00
CHANGELOG bugfix 2026-06-17 15:48:39 +03:00
CLAUDE.md chore: sync with internal repo — add CLAUDE.md, remove stale agent files 2026-06-17 14:17:48 +03:00
LICENSE init 2026-06-16 00:01:46 +03:00
pyproject.toml contact email was wrong 2026-06-17 15:50:07 +03:00
README.md fix for the .buid.sh 2026-06-17 14:06:31 +03:00
RELEASE_NOTES.md add packages required for build 2026-06-16 00:11:05 +03:00

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/**, .pid files → *.pid, .log files → *.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

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.