Understanding the foundations of AppArmor: the concept of per-application profiles that govern file, network, capability, and signal access; the two working modes, enforce and complain; a comparison of AppArmor with SELinux; and the kernel LSM and userspace tools such as apparmor_parser and libapparmor.

In episode 1 we traced AppArmor's history: why the world needs MAC, its birth at Immunix, and its mass adoption by Ubuntu, Debian, Docker, and even ChromeOS. Now it's time to understand how AppArmor actually works underneath. This episode is the technical foundation: every episode after this one will stand on the concepts we build here.
Three things we'll master: the concept of a profile as AppArmor's policy unit, the modes that determine whether rules are enforced or merely logged, and the architecture that connects the kernel with the userspace tools. After this episode, you'll be able to explain to anyone what AppArmor is without opening the documentation.
AppArmor's basic unit is the profile — a set of rules attached to a single application (executable) that determines what its descendant processes may do. Profile names usually follow the executable's path, e.g. /usr/sbin/nginx. The analogy: a profile is an employee's employment contract — before entering the building, the employee is handed a contract stating which rooms they may enter, which documents they may read, and which equipment they may use.
An AppArmor profile governs several access dimensions:
| Dimension | What it controls | Example |
|---|---|---|
| File | File access based on path | r, w, m, k |
| Network | Sockets and protocols | network tcp, network udp |
| Capabilities | Linux privileges | capability net_bind_service |
| Mount | Filesystem operations | mount, remount, umount |
| ptrace | Cross-process inspection | ptrace trace peer=... |
| Signals | Signal delivery | signal send peer=... |
The most important principle to internalize: a process without a profile is an invulnerable process. If an application has no profile at all, AppArmor restricts nothing — it runs exactly as if there were no MAC. Security value appears precisely when we give it a profile. This differs from some other MAC systems that try to label every object.
In the file dimension, AppArmor recognizes four basic accesses you'll keep encountering:
| Access | Meaning |
|---|---|
r | Read a file |
w | Write a file |
m | Memory-map a file (mmap) — including loading libraries |
k | Lock a file (file lock) |
m is often the source of confusing denials for beginners: a program may be allowed to execute its binary, but fails to run because a library isn't allowed to be mmaped. We'll dig deeper into this in episode 4 when we write profiles.
Every profile runs in one of two modes:
A fitting analogy: enforce is a friend who holds you back when you try to enter a forbidden room, while complain is a friend who only logs every door you try to open without holding you back — then hands you a report at the end of the day. That log is the raw material for perfecting the profile.
Complain mode is one of AppArmor's biggest advantages for production: you can launch an application with a new profile in complain mode, collect its real behavior over several days, then promote it to enforce with confidence.
This is the question that comes up most often in the community: why isn't AppArmor SELinux? The answer lies in the decision basis:
owner /etc/nginx/nginx.conf r. When a process accesses a file, the kernel compares that file's path with the profile rules. Easy to understand and write because it directly mirrors the filesystem structure.| Aspect | AppArmor | SELinux |
|---|---|---|
| Decision basis | File path | Label/type context |
| Writing rules | Write the path directly | Set labels + write rules |
| Beginner friendliness | Relatively easy | Steeper |
| Expressiveness for non-file objects | Limited | Very high |
| Default distros | Ubuntu, Debian, openSUSE | RHEL, Fedora, Rocky |
This comparison isn't about which is "better" — both are proven MAC systems. The question is context: in a path-based ecosystem (Debian/Ubuntu), AppArmor is the natural choice. And remember, both can coexist on one system through modern LSM stacking.
AppArmor consists of two worlds working together:
On the kernel side, AppArmor is implemented as a Linux Security Module (LSM). The LSM is a framework of security hooks in the kernel that gets called on every sensitive operation: opening a file, creating a socket, calling mount, and so on. The AppArmor hook checks the active process profile — if one exists, the decision is made from the rules; if not, the operation is allowed through.
The kernel exposes two important areas:
/sys/kernel/security/apparmor — securityfs where the kernel publishes status and loaded profiles, and where userspace writes policy.On the userspace side, three main components:
| Component | Role |
|---|---|
apparmor_parser | Compiles text profile files into binary and loads them into the kernel |
aa-status | Reads runtime status from the kernel and prints a report |
libapparmor | Shared library used by tools to communicate with the kernel |
The source of truth lives in /etc/apparmor.d/ — the directory where all text profile files are stored. At system boot (or when you run apparmor_parser), the profiles in this directory are compiled and loaded into the kernel. The full flow:
/etc/apparmor.d/nginx
│ ditulis sebagai teks
▼
apparmor_parser (kompilasi + load)
│
▼
Kernel LSM (memuat aturan ke memori)
│
▼
Hook LSM menilai akses proses terhadap pathThis is the flow you'll rely on in episodes 3 and 4: writing profiles in /etc/apparmor.d/, loading them with apparmor_parser, then verifying with aa-status.
m is the same as r. m allows memory-mapping a file — critical for libraries. Without m, a program can fail to run even when r is allowed.In episode 2 we built AppArmor's technical foundation: the profile concept as the per-application policy unit, the r w m k file rules plus other dimensions like network, capabilities, mount, ptrace, and signals; the two working modes, enforce and complain; the path-based vs type-based comparison with SELinux; and the kernel LSM architecture with the userspace components apparmor_parser, aa-status, and libapparmor.
The core takeaways:
/etc/apparmor.d/, are processed by apparmor_parser, and verified with aa-status.In episode 3, we move to our first hands-on practice: status, modes, and loading/unloading profiles — reading full status with aa-status, checking whether AppArmor is active with aa-enabled, switching enforce/complain modes, and loading and unloading profiles with apparmor_parser. Keep your momentum, because starting with this episode you're touching AppArmor directly!