skip to content

Ubuntu ships AppArmor and RHEL ships SELinux as their mandatory access control system. What is the fundamental difference in how each one identifies the things it is protecting, and what practical consequences does that difference have?

level: middleimportance: should knowfreq 48%

answer

  1. paths versus labels
  2. profile attaches to an executable
  3. label rides on the inode
  4. no profile means unconfined
  5. per-profile complain versus global permissive

basics

~20 s

AppArmor mediates by pathname: profiles attach to an executable and list the paths it may touch. SELinux mediates by label: every process and object carries a type stored on the inode, and policy is written over those types. Path-based rules are far easier to read; label-based rules follow the object wherever it goes.

solid answer

~50 s

AppArmor is **path-based**. A profile attaches to an executable and enumerates the pathnames it may read, write or execute, with globs. It is readable, self-contained per program, and a program with no profile is simply unconfined. SELinux is **label-based**: every process runs in a domain and every object carries a type stored in the inode's `security.selinux` attribute, and the policy is a system-wide ruleset over those labels. The practical consequences follow directly. AppArmor rules match the path used to reach an object, so the same file reachable under a second name — a bind mount, an alias — is not automatically covered unless the profile names it too. SELinux labels stick to the object regardless of path, but that means data must be *labelled correctly*, which is where relabelling and `restorecon` come from. AppArmor's per-profile complain mode (`aa-complain`) is finer-grained than SELinux's global permissive, and SELinux covers many non-file object classes such as ports and sockets that AppArmor's file rules do not model the same way.

code

bash · 11 lines
bash
# which MAC system is active on this host?
sestatus 2>/dev/null || echo 'no SELinux'
aa-status 2>/dev/null || echo 'no AppArmor'

# AppArmor: put one program into log-only mode, then back
aa-complain /etc/apparmor.d/usr.sbin.nginx
aa-enforce  /etc/apparmor.d/usr.sbin.nginx

# SELinux: the per-domain equivalent of the same idea
semanage permissive -a httpd_t
semanage permissive -d httpd_t

go deeper

for a junior

Know which distributions ship which system, and that both add a second permission check on top of the ordinary owner and mode bits.

for a middle

Explain path-based versus label-based mediation as the root difference, and derive the consequences: profiles attach to executables and enumerate paths, while SELinux types ride on inodes and cover ports and sockets too.

for a senior

On an unfamiliar host, identify which system is active from the audit record shape and relax it in the narrowest way available — one profile into complain, or one domain into permissive — rather than switching the whole machine off.

for a principal

Weigh the operational cost of each model across a mixed fleet: profile authorship and coverage gaps on one side, labelling discipline and relabel operations on the other, and decide where confinement is mandatory versus advisory.

## Same job, two different naming schemes Both AppArmor and SELinux are Linux Security Modules that implement mandatory access control: a second check, after the classic owner/mode check, that the resource's owner cannot relax. They differ in how they *name* the things they are talking about, and almost every practical difference falls out of that one choice. **AppArmor names objects by pathname.** A profile is a text file, conventionally `/etc/apparmor.d/usr.sbin.nginx` for `/usr/sbin/nginx`, that attaches to an executable and enumerates path rules with permission letters, plus rules for capabilities and network access. A program that has no profile is unconfined — confinement is opt-in, program by program. **SELinux names objects by label.** Every process runs in a domain and every file, directory, socket and port carries a type. The file's type lives in the `security.selinux` extended attribute on the inode. The policy is one system-wide compiled ruleset of allow rules over `(domain, type, class, permission)` tuples, and under the targeted policy anything not covered is confined by a catch-all unconfined domain rather than being ignored. ## What follows for the administrator **Readability and authorship.** An AppArmor profile can be read and edited by anyone who understands file paths, and the tooling (`aa-genprof`, `aa-logprof`) will walk you through building one from observed behaviour. SELinux policy is a compiled artifact; you interact with it through booleans, labels and generated modules rather than by editing rules directly. This is the single biggest reason Ubuntu-family shops find AppArmor approachable. **Identity of the protected object.** Because a SELinux label rides on the inode, it survives renames and hard links and applies no matter which path reached the file. Because AppArmor matches on the resolved pathname, a file exposed under an additional path — through a bind mount, for instance — is evaluated against whatever rules match *that* path; AppArmor provides `alias` rules precisely so a profile can declare two paths equivalent. The mirror-image cost of SELinux's model is that labels can be wrong: data moved rather than copied keeps its old type, and getting a service working can mean relabelling rather than editing rules. **Granularity of the escape hatch.** AppArmor profiles are individually in enforce or complain mode — `aa-complain /etc/apparmor.d/usr.sbin.nginx` puts exactly one program into log-only mode, and `aa-status` shows which profiles are in which mode. SELinux's permissive mode is global by default, though `semanage permissive -a <domain>` gives an equivalent per-domain escape. **Coverage beyond files.** SELinux labels object classes that have no pathname at all — TCP and UDP ports, sockets, IPC objects, capabilities — so a policy can express "this domain may bind only ports of this type". AppArmor has network rules and capability rules, but its file model is fundamentally about paths, and things without a path are covered differently. **Failure signature.** Both write to the audit subsystem, but the records look different. SELinux emits `avc: denied` with `scontext`/`tcontext`/`tclass`; AppArmor emits records containing `apparmor="DENIED"` with `operation=`, `profile=` and `name=` naming the path. Knowing which shape to grep for tells you immediately which system is confining the host. ```bash # Ubuntu-family: which profiles exist and what mode are they in? aa-status aa-complain /etc/apparmor.d/usr.sbin.nginx aa-enforce /etc/apparmor.d/usr.sbin.nginx # RHEL-family sestatus getenforce ``` ## Which one is on this machine? In practice a system runs one of them, not both: Debian and Ubuntu ship AppArmor enabled with profiles for a set of common services, while RHEL, Fedora and derivatives ship SELinux with the targeted policy. SUSE also uses AppArmor by default. When you land on an unfamiliar host, `sestatus` and `aa-status` answer the question in one command each, and a failure whose cause is invisible in the mode bits should send you to whichever of the two is active. ## The judgement an interviewer is listening for The weak answer is "SELinux is stricter and AppArmor is easier" — a slogan that survives no follow-up. The strong answer identifies the naming model as the root difference and derives the rest: why AppArmor profiles are readable but need every path enumerated, why SELinux confinement follows data across renames but drags relabelling along with it, and why an unprofiled binary is unconfined under AppArmor while SELinux has an opinion about everything on the system.

  • Why does AppArmor need an alias rule when the same file becomes reachable under a second path?
    Because AppArmor's file rules match the pathname used to reach the object, not the object itself. Expose the same data under a different path — a bind mount, for example — and the profile's rules for the original path no longer match. An `alias` rule declares the two paths equivalent so the existing rules apply to both. SELinux does not have this problem because the label is on the inode.
  • On an unfamiliar Linux host, how do you tell in seconds which MAC system is confining you?
    Run `sestatus` and `aa-status`. One of them will report an active system and the other will not exist or report nothing loaded. The audit records are the other tell: SELinux denials read `avc: denied` with scontext and tcontext, while AppArmor denials read `apparmor="DENIED"` with a profile name and a path.
  • Under AppArmor, what confines a daemon that has no profile at all?
    Nothing — AppArmor confinement is opt-in per executable, so an unprofiled program runs with only the ordinary discretionary permissions. That is a real coverage difference from SELinux's targeted policy, which places every process in some domain, even if that domain is broadly permissive. It also means "AppArmor is enabled" says nothing about whether your particular service is actually confined.

saying these in an interview costs you the question

  • AppArmor and SELinux both work by labelling files
  • A host can run SELinux and AppArmor at the same time
  • AppArmor confines every program on the system automatically
  • SELinux enforcement depends on the file's path
  • Complain mode and permissive mode both disable the system globally

context