Chapter 8: Mandatory Access Control: SELinux and AppArmor
Chapter 8: Mandatory Access Control: SELinux and AppArmor
Discretionary Access Control (DAC)—the standard Unix user, group, and permission bit model—suffers from a fatal structural flaw: the owner of a resource completely dictates who can access it, and any process running as root can bypass all DAC checks entirely. If an unprivileged daemon running as root (or compromised via a local exploit) is tricked into reading /etc/shadow or writing to /etc/ld.so.preload, the operating system permits the action without hesitation.
Mandatory Access Control (MAC) solves this vulnerability. Under MAC, the operating system enforces centralized, policy-defined constraints that neither users nor root processes can override. This chapter examines the Linux Security Module (LSM) framework and explores the two premier MAC implementations: SELinux and AppArmor.
Mandatory Access Control Fundamentals
In DAC, access decisions depend on the identity of the user and the file’s permission bits. In MAC, every operation is evaluated against an immutable system security policy:
┌────────────────────────────────────────────────────────────────────────┐
│ DAC VS MAC DECISION ENGINE │
│ │
│ Discretionary Access Control (DAC) │
│ Process (UID 0 / root) ──► Wants /etc/shadow ──► Allowed immediately │
│ │
│ Mandatory Access Control (MAC) │
│ Process (httpd_t) ──► Wants /etc/shadow ──► DENIED BY POLICY │
│ (Even if process runs with EUID 0, policy forbids httpd_t from │
│ reading shadow_t objects) │
└────────────────────────────────────────────────────────────────────────┘
- Subjects: Active entities that request operations (processes executing in specific domains or profiles).
- Objects: Passive system resources requested by subjects (files, sockets, IPC queues, block devices, memory regions).
- The Principle of Strict Containment: A compromised web server process should only be capable of reading web files, writing to its designated log directory, and binding to its assigned TCP port. Even if an attacker executes a shell inside that process, MAC rules prevent the shell from reading user home directories, inspecting other processes, or tampering with system configurations.
SELinux Architecture and Policy Models
Developed initially by the United States National Security Agency (NSA) and Red Hat, Security-Enhanced Linux (SELinux) implements Flask architecture via the Linux Security Module (LSM) hooks:
┌────────────────────────────────────────────────────────────────────────┐
│ SELINUX ARCHITECTURE │
│ │
│ Userspace Process (Subject) │
│ │ │
│ ▼ Syscall (e.g., open("/var/www/index.html")) │
│ Linux Kernel VFS / DAC Check (Passes) │
│ │ │
│ ▼ │
│ LSM Hook: selinux_file_open() │
│ │ │
│ ▼ Query Access Vector Cache (AVC) │
│ ┌──────────────────────────────────────────────────────────────┐ │
│ │ AVC (In-Memory Cache): Cached decision? │ │
│ │ ├─► YES: Allow or Deny immediately │ │
│ │ └─► NO: Consult Security Policy in Security Server │ │
│ └──────────────────────────────┬───────────────────────────────┘ │
│ ▼ │
│ Security Server: Evaluates Rule (httpd_t -> httpd_sys_content_t:read)│
│ │ │
│ Result: Allow ──► Update AVC Cache ──► Complete Syscall │
│ Deny ──► Generate Audit Log ──► Return -EACCES │
└────────────────────────────────────────────────────────────────────────┘
The SELinux Context Format
Every process and file in SELinux carries a security context composed of four fields:
\[\text{user} : \text{role} : \text{type} : \text{level}\]
Example file context: system_u:object_r:httpd_sys_content_t:s0
- User (
system_u): The SELinux user identity. - Role (
object_r): The role (used in Role-Based Access Control). - Type (
httpd_sys_content_t): The core attribute for Type Enforcement (TE). - Level (
s0): Multi-Level Security / Multi-Category Security (MLS/MCS) sensitivity rating.
SELinux Configuration and Administration
SELinux operates in one of three modes: 1. Enforcing: Security policy is strictly enforced; unauthorized actions are blocked and logged. 2. Permissive: Unauthorized actions are allowed, but denials are logged to the audit system. Essential for testing and troubleshooting. 3. Disabled: SELinux hooks are completely deactivated in the kernel.
# Query current SELinux status
sestatus
# Temporarily switch between Permissive and Enforcing modes
sudo setenforce 0 # Permissive
sudo setenforce 1 # Enforcing
# Inspect security contexts on files and processes
ls -Z /var/www/html/
ps -eZ | grep nginxRestoring File Contexts with restorecon
When files are copied or moved, their SELinux types may become mismatched, leading to spurious permission errors. Use restorecon to reset file labels to match the system policy specifications:
# Recursively restore standard contexts according to policy database
sudo restorecon -Rv /var/www/html/SELinux Policy Writing and Customization
When an application requires legitimate access blocked by default policies:
Analyze Denials with
ausearchandaudit2why:# Search audit logs for AVC denials sudo ausearch -m AVC -ts recent # Translate cryptic denial messages into human-readable explanations sudo ausearch -m AVC -ts recent | audit2whyGenerate and Install Custom Policy Modules with
audit2allow:# Generate a type enforcement (.te) draft and compiled module (.pp) sudo ausearch -m AVC -c "mydaemon" | audit2allow -M mydaemon_policy # Inspect generated rules before loading! cat mydaemon_policy.te # Install the compiled policy module sudo semodule -i mydaemon_policy.pp
AppArmor Architecture and Profiles
AppArmor is the default MAC system in Ubuntu, Debian, and openSUSE. Unlike SELinux’s label-based approach, AppArmor is path-based: it binds security profiles directly to the absolute filesystem path of the executable program (/usr/sbin/nginx).
┌────────────────────────────────────────────────────────────────────────┐
│ APPARMOR PROFILE STATES │
│ │
│ Profile Mode Behavior │
│ ──────────── ───────────────────────────────────────────────── │
│ Enforce Violations are blocked immediately and logged. │
│ Complain Violations are permitted, but logged as warnings. │
│ Disabled Profile is unloaded from the kernel. │
└────────────────────────────────────────────────────────────────────────┘
Profiles are defined as human-readable plain text files located in /etc/apparmor.d/.
Anatomy of an AppArmor Profile
# /etc/apparmor.d/usr.sbin.myapp
#include <tunables/global>
/usr/sbin/myapp {
#include <abstractions/base>
#include <abstractions/nameservice>
# Network permissions
network inet stream,
network inet6 stream,
# Capability restrictions (Only allow binding low ports)
capability net_bind_service,
# Explicit filesystem access rules
/usr/sbin/myapp mr,
/etc/myapp/** r,
/var/log/myapp/** rw,
/var/lib/myapp/** rw,
/run/myapp/myapp.pid rw,
# Explicitly deny access to sensitive files
deny /etc/shadow r,
deny /etc/ssh/** r,
}
r: Readw: Writem: Memory map executablex: Execute
AppArmor Configuration and Administration
AppArmor provides simple, interactive utilities for creating, testing, and managing profiles:
# Check loaded profiles and running confined processes
sudo aa-status
# Place a profile in complain mode for testing
sudo aa-complain /usr/sbin/myapp
# Place a profile in strict enforcement mode
sudo aa-enforce /usr/sbin/myapp
# Interactive profile profiling: parse audit logs and generate rule updates
sudo aa-logprof
# Wizard for authoring a new profile from scratch by monitoring execution
sudo aa-genprof /usr/sbin/mydaemonChoosing Between SELinux and AppArmor
Organizations frequently debate which MAC framework to deploy:
| Criterion | SELinux | AppArmor |
|---|---|---|
| Primary Distributions | RHEL, Rocky, Alma, Fedora | Ubuntu, Debian, SUSE |
| Model Type | Label/Type-based (Inodes tagged with xattrs) | Path-based (Bound to executable paths) |
| Granularity | Extremely fine (System-wide, MLS/MCS, users/roles) | Application-focused, straightforward |
| Learning Curve | Steep (Complex policy language, debugging tooling) | Gentle (Human-readable rules, simple syntax) |
| Container Integration | First-class (container_t, automated MCS categories) |
Standard profile support via Docker/CRI |
| Path Rename Robustness | Invariant across file moves or renames | Requires updated path rules if binary moves |
Recommendation: Align with your distribution’s upstream standard. Do not attempt to force SELinux onto Ubuntu or AppArmor onto RHEL in production. Both frameworks, when actively running in Enforcing mode, neutralize over 90% of zero-day local privilege escalation and service breakout attacks.