AppArmor: Profiles, Modes & Management
Chapter 17: AppArmor: Profiles, Modes & Management
Section titled “Chapter 17: AppArmor: Profiles, Modes & Management”Learning Objectives
Section titled “Learning Objectives”- Understand AppArmor profiles and how they differ from SELinux
- Know how to read, write, and manage AppArmor profiles
- Understand complain vs enforce mode
- Troubleshoot AppArmor denials
Prerequisites
Section titled “Prerequisites”- Chapter 14: Linux Security Fundamentals
- Chapter 16: SELinux (for comparison)
What is this? (Beginner On-Ramp)
Section titled “What is this? (Beginner On-Ramp)”AppArmor is a security system (an alternative to SELinux) that locks down applications. You give AppArmor a profile for a program (like a web server) saying ‘this program is only allowed to read these three folders’. If the program gets hacked and tries to read passwords, AppArmor stops it.
17.1 AppArmor vs SELinux
Section titled “17.1 AppArmor vs SELinux” AppArmor vs SELinux Comparison ─────────────────────────────────────────────────────────── Feature AppArmor SELinux ─────────────────────────────────────────────────────────── Default distros Ubuntu, Debian, SUSE RHEL, Fedora, CentOS Labeling Path-based (file paths) Type-based (labels) Complexity Low (easier to learn) High (steeper curve) Policy location /etc/apparmor.d/ SELinux modules Debugging aa-logprof, /var/log/kern sealert, audit2allow Coverage Per-application profiles System-wide TE policy Portability Tied to file paths Works with network/IPC too ───────────────────────────────────────────────────────────
AppArmor Philosophy: "Confine programs by PATH" SELinux Philosophy: "Confine by TYPE, regardless of path"17.2 AppArmor Architecture
Section titled “17.2 AppArmor Architecture” AppArmor Profile for nginx ───────────────────────────
/etc/apparmor.d/usr.sbin.nginx
#include <tunables/global>
/usr/sbin/nginx { #include <abstractions/base> #include <abstractions/nameservice>
capability net_bind_service, ← Can bind to port 80/443 capability setuid, ← Can change UID/GID capability dac_override, ← Can bypass file perms
/var/www/html/** r, ← Read files in webroot /etc/nginx/** r, ← Read nginx config /var/log/nginx/** w, ← Write logs /run/nginx.pid rw, ← Write PID file /var/cache/nginx/** rw, ← Read/write cache
network tcp, ← Can use TCP network udp, ← Can use UDP
deny /etc/shadow r, ← Explicitly deny shadow file deny /root/** rwx, ← Deny access to root home }
Everything not listed = DENIED (whitelist model)17.3 AppArmor Modes
Section titled “17.3 AppArmor Modes”# ── Check AppArmor Status ─────────────────────────────────aa-status# apparmor module is loaded.# 45 profiles are loaded.# 38 profiles are in enforce mode.# /usr/sbin/nginx# /usr/sbin/sshd# ...# 7 profiles are in complain mode.# 0 processes have profiles defined.
# ── Profile Modes ─────────────────────────────────────────# Enforce: Violations are blocked AND logged# Complain: Violations are only logged (not blocked)# Disabled: Profile not loaded
# Check mode of a specific profileaa-status | grep nginx
# ── Change Profile Mode ───────────────────────────────────aa-enforce /usr/sbin/nginx # Set to enforceaa-complain /usr/sbin/nginx # Set to complainaa-disable /usr/sbin/nginx # Disable profile
# Reload all profilessystemctl reload apparmor # Reload allapparmor_parser -r /etc/apparmor.d/usr.sbin.nginx # Reload specific17.4 Reading AppArmor Denials
Section titled “17.4 Reading AppArmor Denials”# ── Where to find denials ─────────────────────────────────# AppArmor logs to kernel ring buffer and syslogdmesg | grep "apparmor"journalctl -k | grep "apparmor.*DENIED"cat /var/log/kern.log | grep "apparmor.*DENIED"
# Example denial:# kernel: audit: type=1400 audit(1705312800.123:456):# apparmor="DENIED" operation="open"# profile="/usr/sbin/nginx" name="/var/lib/app/data.db"# pid=1234 comm="nginx"# requested_mask="r" denied_mask="r"# fsuid=33 ouid=1000
# Breaking it down:# profile="/usr/sbin/nginx" = which AppArmor profile denied it# name="/var/lib/app/data.db" = what file was accessed# requested_mask="r" = operation attempted (read)# denied_mask="r" = what was denied
# ── aa-logprof: Interactive profile builder ───────────────# Reads log file, suggests profile additionsaa-logprof# Will show each denied access and ask what to do:# (A)llow / (D)eny / (I)gnore / (G)lob17.5 Writing AppArmor Profiles
Section titled “17.5 Writing AppArmor Profiles”# ── Profile Syntax ────────────────────────────────────────
cat > /etc/apparmor.d/usr.bin.myapp << 'EOF'#include <tunables/global>
/usr/bin/myapp { # Include standard abstractions #include <abstractions/base> #include <abstractions/nameservice> #include <abstractions/ssl_certs>
# Capabilities capability net_bind_service, capability setgid, capability setuid,
# File access (format: path access_mode) # r = read, w = write, x = execute, a = append # l = link, m = memory map, k = lock /usr/bin/myapp mr, # Execute self (r=read, m=mmap exec) /etc/myapp/** r, # Read config (all files under /etc/myapp/) /var/lib/myapp/ r, # Read directory /var/lib/myapp/** rw, # Read/write data files /var/log/myapp/** w, # Write logs only (no read) /var/log/myapp/ w, # Write to log directory /run/myapp.pid rw, # PID file /tmp/myapp-* rw, # Temp files with pattern
# Deny rules (override allows) deny /etc/passwd r, deny /etc/shadow r, deny /root/** rwxl,
# Network access network inet stream, # TCP IPv4 network inet6 stream, # TCP IPv6 network inet dgram, # UDP IPv4
# Unix sockets /run/myapp.sock rw,
# Allow /proc access for self /proc/@{pid}/** r, # @{pid} is a variable (current PID)
# Deny sensitive paths deny /proc/*/mem rw, deny /proc/sysrq-trigger rw, deny /sys/kernel/security/** rwx,}EOF
# Load the new profileapparmor_parser -r /etc/apparmor.d/usr.bin.myapp
# Verify it's loadedaa-status | grep myappProfile Abstractions
Section titled “Profile Abstractions”Abstractions are reusable profile snippets included with AppArmor:
# View available abstractionsls /etc/apparmor.d/abstractions/
# Common abstractions:# base - /proc/self/*, /dev/null, etc. (always include)# nameservice - DNS, NSS lookups (/etc/hosts, /etc/resolv.conf)# ssl_certs - SSL certificates (/etc/ssl/certs, CA paths)# python - Python runtime libraries# ruby - Ruby runtime# bash - bash and common utilities
# Example: Python application needs these:/usr/bin/python3-app { #include <abstractions/base> #include <abstractions/python> #include <abstractions/nameservice> ...}17.6 Profile Development Workflow
Section titled “17.6 Profile Development Workflow”# ── Step 1: Generate initial profile ─────────────────────# Run the program once to generate a basic profile templateaa-genprof /usr/bin/myapp# This puts the profile in complain mode and guides you through it
# ── Step 2: Exercise the application ─────────────────────# In another terminal, run the application normally# Do all normal operations: read config, write logs, serve requests
# ── Step 3: Update the profile ────────────────────────────# Back in aa-genprof terminal, press 'S' to scan logs# Approve/deny each suggestion
# ── Step 4: Switch to enforce mode ────────────────────────aa-enforce /usr/bin/myapp
# ── Step 5: Monitor for issues ────────────────────────────journalctl -k -f | grep "apparmor.*DENIED"
# ── Step 6: Use aa-logprof to update profile ──────────────# If denials occur in enforce mode:aa-logprof# Review suggestions, approve as needed17.7 Docker and AppArmor
Section titled “17.7 Docker and AppArmor”# Docker automatically applies an AppArmor profile to containers# Default profile: docker-defaultdocker inspect <container> | grep AppArmor
# Apply a custom AppArmor profile to a containerdocker run --security-opt "apparmor=custom-profile" nginx
# View the default Docker AppArmor profilecat /etc/apparmor.d/docker-default
# The docker-default profile:# - Denies writes to /proc/*# - Denies writes to /sys/**# - Denies mount operations# - Denies ptrace# - Denies loading kernel modules17.8 Production Scenarios
Section titled “17.8 Production Scenarios”Scenario 1: nginx Cannot Serve Files from /srv/
Section titled “Scenario 1: nginx Cannot Serve Files from /srv/”# Symptom: nginx returns 403 Forbidden for /srv/website/ files# Nginx has correct DAC permissions but AppArmor blocks it
# Step 1: Check AppArmor denialsjournalctl -k | grep "apparmor.*DENIED.*nginx" | tail -10# apparmor="DENIED" operation="open" profile="/usr/sbin/nginx"# name="/srv/website/index.html" requested_mask="r"
# Step 2: Find the nginx AppArmor profilecat /etc/apparmor.d/usr.sbin.nginx
# Step 3: The profile only allows /var/www/ — need to add /srv/# Option A: Use aa-logprofaa-logprof# Select allow for /srv/website/** r
# Option B: Manually edit the profilesudo nano /etc/apparmor.d/usr.sbin.nginx# Add inside the profile block:# /srv/** r,
# Step 4: Reload profilesudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
# Step 5: Verify nginx can now serve the filescurl http://localhost/index.html17.9 Interview Questions
Section titled “17.9 Interview Questions”Q1: What is the main conceptual difference between AppArmor and SELinux?
Answer: AppArmor is path-based: profiles define what a program can do based on file paths. If a file moves, the profile may no longer apply. AppArmor is generally easier to understand and configure. SELinux is label-based: every process and file gets a type label, and policy defines which types can interact. Labels persist regardless of file location. SELinux provides stronger security (a file relabeled as the wrong type is still caught), while AppArmor provides a lower barrier to entry. Ubuntu uses AppArmor by default; RHEL uses SELinux.
Q2: How would you add a new allowed path to an AppArmor profile for nginx?
Answer: (1) Put the profile in complain mode:
aa-complain /usr/sbin/nginx. (2) Perform the operation that’s being denied. (3) Runaa-logprofto process the log and interactively approve the new access. Or (4) Manually edit/etc/apparmor.d/usr.sbin.nginxto add a rule like/new/path/** r,. (5) Reload the profile:apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx. (6) Return to enforce mode:aa-enforce /usr/sbin/nginx. (7) Verify the operation now works and no new denials appear injournalctl -k | grep apparmor.
17.10 Summary
Section titled “17.10 Summary”| Concept | Key Point |
|---|---|
| Profile | Whitelist of allowed paths and capabilities |
| Enforce mode | Violations blocked and logged |
| Complain mode | Violations only logged (for development) |
| aa-logprof | Interactive profile updater from logs |
| aa-genprof | Profile generator for new applications |
| Abstractions | Reusable profile snippets |
Next Chapter: Chapter 18: Seccomp, Capabilities & Syscall Filtering
Section titled “Next Chapter: Chapter 18: Seccomp, Capabilities & Syscall Filtering”Prerequisites
Section titled “Prerequisites”Chapter 14 (Security Fundamentals).
Advantages & Disadvantages
Section titled “Advantages & Disadvantages”Advantages: Much easier to learn and use than SELinux, path-based rules are intuitive. Disadvantages: Less comprehensive than SELinux, relies on paths instead of inode labels.
Common Mistakes
Section titled “Common Mistakes”- Leaving profiles in complain mode in production.
- Not updating profiles when application paths change.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Diagnosis | Fix |
|---|---|---|---|
| App unexpectedly killed or denied access | AppArmor profile too restrictive | `dmesg | grep apparmor; grep DENIED /var/log/syslog` |
Hands-on Lab
Section titled “Hands-on Lab”Objective: Create and manage an AppArmor profile.
# 1. Check AppArmor statusaa-status
# 2. Put a profile in complain modeaa-complain /usr/sbin/nginx
# 3. Generate a new profile using aa-genprof# aa-genprof /path/to/custom/binaryExercises
Section titled “Exercises”- Write a simple bash script that reads
/etc/passwd. Create an AppArmor profile that blocks this script from reading that file. - Use
aa-logprofto parse audit logs and update a profile in complain mode.
Revision Notes
Section titled “Revision Notes”- AppArmor uses path-based MAC (unlike SELinux’s label-based MAC).
- Profiles can be in enforce mode (blocking) or complain mode (logging only).
- Easier to learn and use than SELinux for many workloads.
Further Reading
Section titled “Further Reading”- Ubuntu AppArmor documentation
- AppArmor upstream Wiki
Related Chapters
Section titled “Related Chapters”- Chapter 16 — SELinux
Last Updated: July 2026