Learn AppArmor - Advanced Profile Features
Episode 17 of 23

Learn AppArmor - Advanced Profile Features

Mastering advanced AppArmor profile features: mount rules, ptrace and signals, pivot_root, finer network filtering, transitions between profiles with child profiles and attach, and understanding policy versioning between the 4.x and 5.x formats.

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

Introduction

In episode 16 you understood where AppArmor stands in the kernel: one LSM in a stack, mediating alongside other modules. Now we turn back to policy — but to the parts rarely explored. So far your profiles have been dominated by file, network, and capability rules. Yet AppArmor can mediate far deeper: mount, ptrace and signals, pivot_root, finer network filtering, and transitions between profiles.

This episode covers those features, then closes with policy versioning: how the 4.x and 5.x formats coexist, and what that means for the profiles you write today.

Mount Rules

Processes allowed to mount can be controlled with mount rules — source, target, and options. This matters for daemons that need additional filesystems, for example container services or applications that mount media, while also preventing other daemons from mounting anything at all:

LinuxRestricted mount rules
mount options=(ro) -> /mnt/readonly/,
mount /dev/sr0 -> /mnt/media/,
deny mount -> /proc/sys/**,
deny mount -> /etc/**,

The first line allows a read-only mount to a single target point — an important note: specifying the ro option here isn't just documentation, the kernel mediates it. The third and fourth lines are denies that close sensitive areas: no process in this profile may mount anything to /proc/sys or /etc. As always, deny wins over allow.

ptrace and Signal Rules

Inter-process communication is a dimension often overlooked. ptrace governs debugging and injection (like gdb), while signal governs signals such as kill, TERM, and HUP. Both rules can be restricted to specific peers:

LinuxRestrict ptrace and signals to specific peers
ptrace (read) peer=myapp_helper,
signal (send) peer=myapp_helper,
deny ptrace peer=unconfined,
deny signal (receive) peer=other,

The first rule allows a process in this profile to read the state of processes owned by myapp_helper; the second allows sending signals to the same peer. The last two denies close off interaction with the outside world: no tracing of unconfined processes, and no receiving signals from other processes. This limits lateral movement — an attacker who seizes one process can't use it to influence others.

Tip

If your application does debugging or inter-process communication and suddenly fails after the profile is activated, suspect the ptrace and signal rules first. Both are often invisible denies because they aren't recorded as file denies.

pivot_root for Containers and Chroot

pivot_root is the syscall that switches the root filesystem — the core mechanism when a container or chroot-like runtime prepares its new filesystem. In AppArmor, access to it can be allowed fully or restricted by path:

Linuxpivot_root rules
pivot_root,
deny pivot_root -> /var/lib/**,

The first line allows pivot_root with no target restriction. The second line refuses pivot_root into the /var/lib tree — for example protecting data volumes from a process that tries to use them as a new root. For container workloads that build their own rootfs, allowing pivot_root with a clear target is better than granting sys_chroot capability wildly.

Finer Network Filtering

The network rules from episode 6 can be refined by explicitly combining domain, type, and protocol. The deny patterns below show how to narrow network behavior:

LinuxNetwork filtering per domain and type
network inet tcp,
network inet6 tcp,
network unix stream,
deny network inet raw,
deny network netlink,

Here the profile allows IPv4/IPv6 TCP and UNIX stream — then explicitly forbids raw sockets and netlink. The raw deny matters for daemons that don't need to capture packets; the netlink deny prevents a process from manipulating network configuration. Note that mediation details, including support for more granular unix rules, can differ depending on the kernel version and the policy ABI — back to the versioning section below.

Transitions Between Profiles and Child Profiles

So far one profile guards one binary. But AppArmor supports transitions: when a binary executes another program, confinement can move to a different profile. The most common transition is px (discrete profile transition):

LinuxChild profile via px transition
profile myapp /opt/myapp/bin/myapp {
  #include <abstractions/base>
 
  /opt/myapp/bin/myapp mr,
  /opt/myapp/bin/helper px -> myapp_helper,
}
 
profile myapp_helper {
  #include <abstractions/base>
 
  /opt/myapp/bin/helper mr,
  /opt/myapp/data/ r,
  /opt/myapp/data/** r,
}

When myapp executes helper, the process moves to the stricter myapp_helper profile — it no longer inherits all of its parent's permissions. This is the cleanest way to restrict risky parts of an application: separate its functions into its own binary, then confine each binary with a narrow profile.

For the case of one binary with several modes — for example an admin mode and a worker mode — AppArmor provides hats (child profiles prefixed with ^), activated through the change_hat call from inside the application:

LinuxHats for separate modes in one profile
profile myapp /opt/myapp/bin/myapp {
  #include <abstractions/base>
 
  /opt/myapp/bin/myapp mr,
 
  ^admin {
    /etc/myapp/admin.conf r,
  }
}

There's also the attach keyword on transitions, which binds execution to certain conditions (for example extended attributes on a binary). It's useful for scenarios where one executable is served by more than one profile. The full syntax details are in the apparmor.d man page, but the concept you should carry away is this: AppArmor doesn't lock a process to a single profile for life — it can move between profiles in a controlled way.

Policy Versioning: 4.x vs 5.x

All the features above depend on the policy ABI — the agreement between the policy format, the parser, and the kernel. Every profile can declare the ABI it uses:

LinuxDeclaring an ABI at the top of a profile
# top of profile
abi <abi/4.0>,
 
#include <tunables/global>
#include <abstractions/base>

A profile that doesn't declare an ABI is considered to use the legacy ABI pinned by the parser configuration. The parser performs rule downgrade: features the kernel doesn't support are downgraded or dropped, and you can ask the parser to tell you when that happens:

Detect rules that aren't enforced
apparmor_parser --skip-kernel-load --warn=rule-not-enforced /etc/apparmor.d/myapp
aa-features-abi -x

apparmor_parser --warn=rule-not-enforced warns about rules that can't be enforced; aa-features-abi -x prints the current kernel's feature capabilities in ABI format — redirect its output to a file to compare easily across hosts.

The core of what you need to understand about versioning:

  • Policy 4.x is backward compatible with 3.x policy through the ABI mechanism — old profiles keep working with a new parser.
  • AppArmor 5.x (released 2026) brings a newer policy format — including expanded permission tables and more granular network mediation like unix rules — and a number of its new features aren't backward compatible with the 4.x format.
  • Migrating between versions means reviewing the ABI, rebuilding the parser cache, and testing profiles in complain mode before deciding — exactly the cycle you mastered in episode 12.

Warning

When raising the parser to a new version, don't forget the compiled profile cache. A cache built by an old parser can hold a format the new kernel doesn't recognize — or vice versa. Make a habit of re-verifying all profiles with apparmor_parser --skip-cache --skip-kernel-load whenever you upgrade the userspace.

Conclusion

In this episode you've opened new dimensions of AppArmor profiles: mount rules restricted by source, target, and options; ptrace and signal rules that limit inter-process interaction; pivot_root for container needs; finer network filtering; and transitions between profiles — child profiles via px, hats via ^, and the attach keyword — that let a single process switch confinement in a controlled way. You also understood policy versioning: ABI declarations, rule downgrade, and the compatibility boundaries between the 4.x and 5.x formats.

The core takeaways:

  • Mount, ptrace, signal, and pivot_root are mediation dimensions often forgotten — and just as important as file rules.
  • px transitions separate risky parts of an application into narrower profiles.
  • Hats with ^ split one binary into isolated modes.
  • The ABI determines which features are actually enforced — declare it and verify it.
  • Upgrading the parser or kernel must be followed by an ABI review and profile verification.

In episode 18 next we turn all this knowledge into a repeatable workflow: batch mode switching with aa-enforce and aa-complain, profile merging with aa-mergeprof, validation with apparmor_parser -Q, and integrating profile linting and compile checks into a CI pipeline with a multi-distro test matrix. See you in episode 18!

Learn AppArmor - Advanced Profile Features | Learn AppArmor