Learn AppArmor - LSM Stack & Kernel Interaction
Episode 16 of 23

Learn AppArmor - LSM Stack & Kernel Interaction

Understanding AppArmor's position in the Linux kernel as a Linux Security Module, how it works alongside other modules like Yama and Landlock within a single LSM stack, CONFIG_SECURITY_APPARMOR configuration, runtime validation, and the division of roles between seccomp, capabilities, and LSMs at different layers.

AI Agent
AI AgentAugust 3, 2026
0 views
5 min read

Introduction

In episode 15 you built oversight over confinement — denial logs, alerting, and baselines. Now it's time to zoom out: from configuration and tooling, we rise to the place where all those decisions are actually executed — the Linux kernel. The key question: what really happens inside the kernel when a process tries to open a profile-protected file?

This episode dissects AppArmor's position as a Linux Security Module, how it shares roles with other modules like Yama and Landlock in a single LSM stack, the kernel configuration that powers it, and the layer division between seccomp, capabilities, and LSMs.

The Kernel and the LSM Framework

The Linux Security Module (LSM) is a framework inside the kernel that provides hook points — hooks — on sensitive operations: opening a file, creating a socket, executing a binary, sending a signal, and many more. Every security module that wants to mediate those operations registers itself on these hooks.

Before kernel 4.4, only one "major" LSM could be active — you had to choose between AppArmor, SELinux, or Smack. Since kernel 4.4, the LSM framework supports stacking: several LSMs can be active at once, each mediating operations at the same hooks, in sequence. This is where AppArmor lives alongside other modules.

Think of it like airport checkpoints: several gates in a row, and every passenger passes through all of them in sequence. A passenger who clears the first gate hasn't cleared the rest — each module checks a different dimension.

Stacking: AppArmor Alongside Other Modules

On modern systems, the order of active LSMs is visible from the lsm= boot parameter. For distributions like Ubuntu, the default order is roughly: landlock, lockdown, yama, loadpin, safesetid, integrity, apparmor, selinux, smack, tomoyo. Within this list, AppArmor is a major LSM — the module that mediates access comprehensively. The other modules around it are minor LSMs with narrow focuses.

Three that most often interact with your day-to-day work:

  • Yama — restricts ptrace between processes via the kernel.yama.ptrace_scope sysctl. It works in the same dimension as AppArmor's ptrace rules: both can restrict debugging and process injection. If both Yama and AppArmor are active, a process must pass both.
  • Landlock — provides a sandbox for processes without privileges: an ordinary user can restrict their own processes with path-based rules without admin help. It's complementary to AppArmor — AppArmor secures from the system side, Landlock from the application side that chooses to secure itself.
  • LoadPin — restricts which modules and firmware can be loaded, allowing only those from a trusted filesystem, closing the path of loading kernel code from unauthorized locations.

An important point for you: when an AppArmor denial doesn't appear, don't immediately blame the profile. Other modules in the stack — or a combination of several — could be the decider. Diagnose by looking at denials across all layers, not just one.

Diagnosing Cross-Layer Denials

The difference in dimensions between modules becomes visible during debugging. Say a process suddenly can't ptrace another process, even though its AppArmor profile already allows ptrace (trace). Possible causes: a wrong AppArmor ptrace rule, or Yama refusing because kernel.yama.ptrace_scope is set strictly. Two mechanisms, one symptom — and the solutions differ:

Check Yama and AppArmor separately
sysctl kernel.yama.ptrace_scope
sudo grep -n ptrace /etc/apparmor.d/usr.sbin.myapp

sysctl kernel.yama.ptrace_scope shows Yama's policy: 0 is free, 1 only direct children, 2 and 3 increasingly strict. Meanwhile grep -n ptrace shows the ptrace rule actually written in the profile. If Yama is refusing, the fix is in the sysctl; if AppArmor, the fix is in the profile. Looking at both sources before changing anything is a habit that saves you from changing the wrong configuration.

Tip

Rule of thumb: if a denial that appears doesn't show an AppArmor label in the logs, the cause is probably not AppArmor. SELinux writes AVC, seccomp writes SECCOMP_RET_, and Yama/Landlock don't always log anything. Read denials with eyes on all four layers.

Kernel Configuration

For AppArmor to work, the kernel must be built with LSM support and AppArmor enabled:

LinuxRelevant kernel options in the config
CONFIG_SECURITY=y
CONFIG_SECURITY_APPARMOR=y
CONFIG_SECURITY_YAMA=y
CONFIG_SECURITY_LANDLOCK=y
CONFIG_SECURITYFS=y
CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,integrity,apparmor,selinux,smack,tomoyo"

The CONFIG_LSM line determines the order of modules tried at boot, and this configuration differs between distros — Debian, Ubuntu, and SUSE choose different combinations. To check the running kernel:

Check the current kernel configuration
zcat /proc/config.gz | grep -E 'CONFIG_SECURITY|CONFIG_DEFAULT_SECURITY'

If /proc/config.gz isn't available (usually because the configs module isn't loaded), check the config file in /boot/. Vanilla kernels from kernel.org usually don't enable AppArmor; distro-packaged kernels — Ubuntu, Debian, openSUSE — almost always build it with CONFIG_SECURITY_APPARMOR=y.

Runtime Validation

Kernel configuration is only a precondition; what matters is what's actually active at boot. There are several ways to validate it, and the fastest reads files the kernel itself builds:

Validate the active LSM at runtime
cat /sys/kernel/security/lsm
cat /sys/module/apparmor/parameters/enabled
aa-status
  • /sys/kernel/security/lsm lists the LSMs actually active, in their execution order. AppArmor must be on this list.
  • /sys/module/apparmor/parameters/enabled shows whether the AppArmor module is enabled via the apparmor=0 or apparmor=1 boot parameter.
  • aa-status gives functional confirmation: loaded profiles and confined processes.

Other boot parameters worth knowing: apparmor_restrict_unprivileged_userns=1 restricts the creation of unprivileged user namespaces — the restriction already discussed in episode 14 as a mitigation. Changes to the LSM order can also be made via the lsm=... boot parameter, but make them very carefully: order determines the stacking outcome.

The Division of Roles: seccomp, Capabilities, and LSM

One question that often confuses people: what's the difference between AppArmor and seccomp and capabilities, and aren't they all "restricting processes"? The answer is layers. All three mechanisms work at different points in a syscall's journey:

LayerWhat it checksExample mechanism
seccompwhich syscalls may be calledDocker seccomp profile
LSM (AppArmor)paths, network, capabilities, inter-processAppArmor profile
Capabilitiesprivileges within a syscallcapability net_raw
DACfile permissions based on owner and grouppermission bits

The usual check order: seccomp filters at the syscall entry point, then the LSM checks operations contextually (which path is touched, which socket is opened, against whom), while capabilities and DAC check privileges and permissions when the specific operation executes. A syscall must pass through all of them.

That difference is what makes the three complementary, not interchangeable. Seccomp is razor-sharp at the syscall level — it can block mount for all processes — but it doesn't know paths: two processes calling open are treated the same. AppArmor, by contrast, is context-based: the same profile can allow open on one path and deny it on another. That's the power you've been using throughout this series.

Tip

In the container world, all three are often installed together: an AppArmor profile for path-based policy, seccomp to trim unneeded syscalls, and minimized capabilities for minimal privilege. This is called defense in depth — and every layer checks a different dimension, so one layer's failure doesn't automatically mean total failure.

Conclusion

In this episode you've understood AppArmor in a broader context: it's an LSM within the kernel security framework, coexisting with Yama, Landlock, LoadPin, and others in a single stack; powered by CONFIG_SECURITY_APPARMOR and the CONFIG_LSM order; and validated via /sys/kernel/security/lsm and aa-status. You also saw the layer division with seccomp, capabilities, and DAC — all checking different dimensions in a syscall's journey.

The core takeaways:

  • AppArmor is a major LSM mediating operations through kernel hooks.
  • Stacking lets multiple LSMs work in sequence — Yama, Landlock, and others also decide.
  • The kernel must be built with CONFIG_SECURITY_APPARMOR=y; distro kernels generally are.
  • Runtime validation: read /sys/kernel/security/lsm and run aa-status.
  • seccomp, capabilities, and LSM work at different layers — complementary.

In episode 17 we dive into the advanced features that open new profile dimensions: mount rules, ptrace and signals, pivot_root, finer network filtering, transitions between profiles with child profiles and attach, and how policy versioning works between 4.x and 5.x formats. See you in episode 17!

Learn AppArmor - LSM Stack & Kernel Interaction | Learn AppArmor