SELinux and AppArmor Concepts
Use Linux mandatory access control as a diagnosable security boundary by understanding LSM enforcement, SELinux labels and types, AppArmor path-based profiles, operating modes, denials, and safe policy refinement.
Learning objectives
By the end of this lesson
- Contrast discretionary access control with mandatory access control.
- Identify active Linux Security Modules and their enforcement state.
- Interpret SELinux users, roles, types, labels, booleans, and access-vector-cache denials.
- Interpret AppArmor profiles, hats, complain/enforce modes, and denial records.
- Troubleshoot access failures without disabling the security subsystem.
1. MAC adds a policy decision after ordinary permissions
Traditional Unix discretionary access control evaluates identity, ownership, mode bits, ACLs, and capabilities. A process that passes those checks may still be denied by a mandatory access control policy. The Linux Security Module framework supplies kernel hooks for security modules. SELinux and AppArmor are major MAC implementations; distributions typically select and support one as their primary system policy.
flowchart TD
R["Process requests resource"] --> D{"DAC, ACL, capabilities allow?"}
D -- no --> X["Deny"]
D -- yes --> L{"LSM policy allows?"}
L -- no --> A["Deny and record policy event"]
L -- yes --> O["Operation proceeds"]
A --> T["Inspect context, rule, and expected behavior"]
T --> F["Fix label, profile, configuration, or policy"]MAC is valuable because a compromised service can be constrained even when it runs under a privileged account. It is also contextual: copying a file into an application directory may preserve the wrong label; moving a binary may change the path AppArmor matches; a service may need a narrowly scoped network or capability permission.
# Discover the active LSM stack and common policy status tools.
cat /sys/kernel/security/lsm 2>/dev/null || true
getenforce 2>/dev/null || true
sestatus 2>/dev/null || true
sudo aa-status 2>/dev/null || true
systemctl is-active apparmor 2>/dev/null || true
# Kernel and journal messages may identify policy denials.
journalctl -k --since '-30 min' | grep -Ei 'avc:|selinux|apparmor="DENIED"' || true2. SELinux primarily authorizes interactions between labeled types
SELinux labels subjects and objects with security contexts commonly shown as user:role:type:level. Type enforcement is central to most service confinement. A web-server process may run as httpd_t; content may need an allowed file type such as httpd_sys_content_t. File path alone is not the policy identity. Policy maps labels and domains to permitted classes and operations.
# Inspect process and file contexts.
ps -eZ | head
ls -lZ /var/www 2>/dev/null || true
id -Z 2>/dev/null || true
# Explain expected labels and restore them from policy definitions.
matchpathcon /var/www/html/index.html 2>/dev/null || true
sudo restorecon -Rv /var/www/html
# Persistent custom file-context mapping; use an appropriate real path.
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/site(/.*)?'
sudo restorecon -Rv /srv/sitechcon can change a context temporarily, but a relabel or restorecon may replace it. Persistent local mappings belong in the policy store through semanage fcontext. Investigate why a file has the wrong type before granting the service broader access.
Modes and booleans
Enforcing mode blocks disallowed operations and records denials. Permissive mode records what would be denied without enforcing it. Disabled removes SELinux enforcement and usually requires reboot; it is not a normal troubleshooting step. Policy booleans expose supported, reviewable switches for common deployment variations.
getenforce
getsebool -a | grep -E '^httpd_' | head -n 20
# Inspect a boolean before changing it.
getsebool httpd_can_network_connect 2>/dev/null || true
# Example persistent change only when architecture requires outbound connections.
sudo setsebool -P httpd_can_network_connect on
# Search recent AVC records and summarize causes.
sudo ausearch -m AVC,USER_AVC -ts recent 2>/dev/null | tail -n 80
sudo sealert -a /var/log/audit/audit.log 2>/dev/null | head -n 80 || true3. AppArmor confines named programs with path-oriented profiles
AppArmor profiles are normally stored under /etc/apparmor.d/. Rules can govern file paths, capabilities, signals, mounts, networks, child-process transitions, and other operations. A profile can be enforced, placed in complain mode for learning, or disabled. Unconfined is not the same as complain mode: complain mode still loads the profile and records policy violations.
sudo aa-status
# Locate profiles and inspect the profile associated with a process.
find /etc/apparmor.d -maxdepth 1 -type f -printf '%f\n' 2>/dev/null | sort | head
cat /proc/"$(pgrep -n nginx)"/attr/current 2>/dev/null || true
# Validate and reload a profile after editing a staged copy.
sudo apparmor_parser --preprocess /etc/apparmor.d/usr.sbin.nginx >/dev/null
sudo apparmor_parser --replace /etc/apparmor.d/usr.sbin.nginx
# Change mode for one profile during a controlled diagnostic window.
sudo aa-complain /usr/sbin/nginx
sudo aa-enforce /usr/sbin/nginxPath-based policy means aliases, symlinks, mounts, and execution paths matter. Package updates may ship profile changes; local additions are often best separated through supported local include files. Use tools such as aa-logprof carefully: generated suggestions are observations of exercised behavior, not proof that every proposed permission is safe.
4. Troubleshoot denials as a four-part question
- What operation failed? Record the application error, timestamp, process identity, path, port, capability, or syscall.
- Was the request expected? A denial may reveal a compromised process, incorrect application configuration, or an unsupported deployment pattern.
- Is the object correctly labeled or addressed? Restore standard labels, verify mount and path behavior, and inspect package defaults.
- What is the narrowest supported policy change? Prefer a documented boolean, correct context, local profile rule, or small policy module over global permissive mode.
# Time-bounded evidence collection for both major MAC systems.
since='2026-08-05 00:00:00'
sudo ausearch -m AVC,USER_AVC -ts "$since" 2>/dev/null \
> /tmp/selinux-avc.txt || true
journalctl --since "$since" \
| grep -E 'apparmor="(DENIED|ALLOWED)"' \
> /tmp/apparmor-events.txt || true
# Preserve contexts and package ownership around one target.
namei -l /srv/site/index.html 2>/dev/null || true
ls -ldZ /srv /srv/site /srv/site/index.html 2>/dev/null || true
rpm -qf /usr/sbin/nginx 2>/dev/null || dpkg-query -S /usr/sbin/nginx 2>/dev/null || trueDisabling SELinux or AppArmor removes a security boundary and often hides the actual configuration or labeling defect. A temporary, scoped diagnostic mode may be justified in a lab or approved incident window, but restore enforcement and document the final narrow policy.
5. Containers still depend on host kernel policy
Containers share the host kernel. Their processes may receive SELinux container types, AppArmor profiles, seccomp filters, capability reductions, namespaces, and cgroup limits. A volume mount can fail because host labels are incompatible; a custom container profile can deny operations even when Unix permissions are permissive. Container options that disable labeling or set an unconfined profile should be treated as security exceptions, not generic troubleshooting flags.
# Read-only examples for container security context discovery.
docker inspect --format '{{json .AppArmorProfile}}' example 2>/dev/null || true
docker inspect --format '{{json .ProcessLabel}} {{json .MountLabel}}' example 2>/dev/null || true
# Kubernetes pod security context and runtime profile references.
kubectl get pod example -o jsonpath='{.spec.securityContext}{"\n"}' 2>/dev/null || true
kubectl get pod example -o jsonpath='{.spec.containers[*].securityContext}{"\n"}' 2>/dev/null || true
# Host-side labels or profile status remain authoritative.
ls -Zd /srv/container-data 2>/dev/null || true
sudo aa-status 2>/dev/null | head -n 40 || true6. Hands-on lab: build a MAC status and denial report
This lab is read-only. It detects the active LSMs, records enforcement state, captures recent denial records, and stores contextual file/process information. Run it on a disposable lab host containing a confined service.
lab="$HOME/devops-academy/linux/chapter17/lesson02"
run_dir="$lab/$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$run_dir"
cat /sys/kernel/security/lsm > "$run_dir/active-lsms.txt" 2>&1 || true
getenforce > "$run_dir/selinux-mode.txt" 2>&1 || true
sestatus > "$run_dir/selinux-status.txt" 2>&1 || true
sudo aa-status > "$run_dir/apparmor-status.txt" 2>&1 || true
sudo ausearch -m AVC,USER_AVC -ts recent \
> "$run_dir/selinux-recent-avc.txt" 2>&1 || true
journalctl -k --since '-1 hour' \
> "$run_dir/kernel-security-events.txt"
ps -eZ > "$run_dir/process-contexts.txt" 2>&1 || true
find "$run_dir" -maxdepth 1 -type f -printf '%f %s bytes\n' | sort
sha256sum "$run_dir"/* > "$run_dir/SHA256SUMS"Verification checklist
7. Common MAC mistakes
Changing permissions to 777
MAC decisions remain, while discretionary exposure becomes worse. Diagnose both layers independently.
Using temporary labels as permanent configuration
Use policy-backed mappings and restorecon for SELinux-managed paths.
Generating broad policy from one test run
Observed behavior can include attacks, mistakes, and unnecessary access. Review each permission.
Disabling enforcement during every incident
This destroys useful evidence and removes containment. Use time-bounded, scoped diagnostics only when approved.
8. Knowledge check
Why can a process be denied even when file mode bits and ACLs allow access?
Why is semanage fcontext followed by restorecon preferable to an isolated chcon for a permanent SELinux path?
What is the difference between AppArmor complain mode and an unconfined process?
9. Summary
- MAC supplements Unix permissions with kernel-enforced policy.
- SELinux commonly authorizes labeled type interactions; AppArmor commonly uses program-oriented path profiles.
- Enforcing, permissive/complain, and disabled/unconfined states are operationally different.
- Denials require validation: expected behavior, correct object identity, and the narrowest supported policy change.
- Containers rely on host LSM enforcement and should not default to unconfined operation.
10. Further reading
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0
Send only Ethereum/ERC-20 compatible assets to this address.