LinuxXXVIII · SELinux and AppArmorMAC debug
MAC denial investigation - diagnosing SELinux and AppArmor denials
What you'll learn
- Investigate a SELinux AVC denial
- Investigate an AppArmor denial
- Decide whether to fix the application, label, or policy
- Document the fix
Prerequisites
Verified against Ubuntu 24.04 LTS · Debian 12 (Bookworm) · RHEL 9.x · Rocky Linux 9.x · AlmaLinux 9.x · Linux kernel 6.1 LTS / 6.6 LTS · systemd 255+ · OpenSSH 8.7p1 (RHEL 9) / 9.6p1 (Ubuntu 24.04) · nftables 1.0.x · chrony 4.x · Pacemaker 2.1.x · Corosync 3.1.x · 2026-08-09
A MAC denial looks like a service that fails mysteriously. The investigation has a clear pattern: read the denial, identify what is denied, decide the fix, apply it. This lesson covers the process for SELinux and AppArmor.
Symptom
A service that worked before is now failing. Common patterns:
- Permission denied (when DAC would allow).
- Operation not permitted.
- File not found (when the file exists).
- Service starts but cannot bind to a port.
The first clue is “the service worked an hour ago” - if a recent change broke it, MAC is a likely cause.
SELinux investigation
Step 1: Check the mode
sestatus
getenforce
If permissive, SELinux is not enforcing - the issue is something else. If enforcing, MAC could be the cause.
Step 2: Find the AVC denial
# Substitute your own values before running:
SERVICE=nginx
sudo ausearch -m avc -ts recent
sudo ausearch -m avc -ts today
sudo ausearch -m avc -c "$SERVICE"
Output is an AVC denial record:
type=AVC msg=audit(...): avc: denied { read } for comm="nginx"
name="passwd" pid=1234 scontext=system_u:system_r:httpd_t:s0
tcontext=system_u:object_r:etc_t:s0 tclass=file
Read the fields:
denied { read }: what was denied.comm="nginx": the process.scontext=...httpd_t...: source (process) context.tcontext=...etc_t...: target (file) context.tclass=file: target class.
Step 3: Diagnose
- Wrong source context: the process is running with the wrong type. Re-label or run as the correct user.
- Wrong target context: the file has the wrong context.
Run
restorecon. - Right contexts, denial: the policy does not allow this access. Generate a policy module.
Step 4: Fix
For mislabeled files:
sudo restorecon -R /var/www/html
For policy gaps:
sudo ausearch -m avc -ts recent | audit2allow -M myapp
sudo semodule -i myapp.pp
For development hosts only:
sudo semanage permissive -a httpd_t
Step 5: Verify
# Re-run the failing operation
curl -I http://localhost
# Verify no new AVCs
sudo ausearch -m avc -ts recent
AppArmor investigation
Step 1: Check the profile
# Substitute your own values before running:
SERVICE=nginx
aa-status
sudo aa-status | grep "$SERVICE"
If the service is unconfined, AppArmor is not the cause.
Step 2: Find the denial
sudo journalctl -k | grep -E 'audit.*apparmor.*DENIED'
sudo dmesg | grep -E 'audit.*apparmor.*DENIED'
Output:
audit: type=1400 audit(...): apparmor="DENIED" operation="open"
profile="/usr/sbin/nginx" name="/etc/myapp.conf" pid=1234
comm="nginx" requested_mask="r" denied_mask="r"
Read:
profile="/usr/sbin/nginx": the profile that denied.name="/etc/myapp.conf": the file that was denied.denied_mask="r": read was denied.
Step 3: Diagnose
The profile does not allow this access. Either the profile is too restrictive (legitimate access denied) or the application is misbehaving.
Step 4: Fix
For legitimate access: add the access to the profile:
sudo aa-logprof
Interactively reviews the audit log and suggests profile changes. Or edit the profile manually:
# Add to /etc/apparmor.d/usr.sbin.nginx
/etc/myapp.conf r,
Then reload:
sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.nginx
For development only:
sudo aa-complain /usr/sbin/nginx
Step 5: Verify
# Re-run
curl -I http://localhost
# Verify no new denials
sudo journalctl -k -n 20 | grep apparmor
Common patterns
| Symptom | Likely MAC cause |
|---|---|
| New file/dir, service cannot read | Mislabeled (SELinux) or not in profile (AppArmor) |
| Service cannot bind to port | Port not in MAC list (SELinux semanage port) |
| Service cannot connect out | Booleans (SELinux httpd_can_network_connect) or network rule (AppArmor) |
| Service worked in permissive, fails in enforcing | Real MAC issue, fix policy |
| Many denials for same source | Generate policy module (audit2allow / aa-logprof) |
Decision matrix
| Symptom | SELinux fix | AppArmor fix |
|---|---|---|
| Mislabeled file | restorecon | Update profile |
| Missing port type | semanage port | Update profile |
| Network access | Boolean | Network rule |
| Policy gap | audit2allow | aa-logprof |
| Need more time | permissive domain | aa-complain |
Document the fix
For every MAC denial investigation, document:
- The denial (output of ausearch / journalctl).
- The cause (mislabel, gap, wrong context).
- The fix (restorecon, semanage, audit2allow, profile edit).
- The verification (re-run, no new denials).
Knowledge check
Knowledge check · 3 questions
Q1. Which command shows recent SELinux AVC denials?
Q2. Setting a domain to permissive mode is a permanent fix.
Q3. Which of the following are valid MAC denial fixes? Select all that apply.
Passing score: 75%. Answers are checked in this browser.