Skip to main content
RunBook Academy

LinuxXXVIII · SELinux and AppArmorMAC debug

MAC denial investigation - diagnosing SELinux and AppArmor denials

Intermediate⏱ ~10 minausearchaudit2allowaa-logprofaa-complain

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

Not yet marked complete on this device.

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

SymptomLikely MAC cause
New file/dir, service cannot readMislabeled (SELinux) or not in profile (AppArmor)
Service cannot bind to portPort not in MAC list (SELinux semanage port)
Service cannot connect outBooleans (SELinux httpd_can_network_connect) or network rule (AppArmor)
Service worked in permissive, fails in enforcingReal MAC issue, fix policy
Many denials for same sourceGenerate policy module (audit2allow / aa-logprof)

Decision matrix

SymptomSELinux fixAppArmor fix
Mislabeled filerestoreconUpdate profile
Missing port typesemanage portUpdate profile
Network accessBooleanNetwork rule
Policy gapaudit2allowaa-logprof
Need more timepermissive domainaa-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

  1. Q1. Which command shows recent SELinux AVC denials?

  2. Q2. Setting a domain to permissive mode is a permanent fix.

  3. Q3. Which of the following are valid MAC denial fixes? Select all that apply.

Passing score: 75%. Answers are checked in this browser.