Skip to content

Troubleshoot SELinux and AppArmor Denials Safely

Linux file permissions and ACLs are not the only access checks. SELinux and AppArmor add mandatory access controls that can deny an operation even when ordinary ownership and mode bits allow it. This guide helps you identify which policy is active, find a denial, and fix the underlying label or profile rule with the smallest appropriate change.

01

Identify the Active Security Module

Read First

Check which tools are installed and whether a policy is loaded. Many distributions primarily use one of these systems, but inspect the host rather than assuming from its name. Run the commands that apply; a missing command is not evidence that a denial occurred.

Terminal window
command -v getenforce >/dev/null && getenforce
command -v sestatus >/dev/null && sestatus
command -v aa-status >/dev/null && sudo aa-status
❯ Illustrative Output (Results Vary)
SELinux may report Enforcing or Permissive.
AppArmor reports whether the module is loaded and which profiles are enforced.

02

Reproduce the Failure and Search Recent Logs

Collect Evidence

Record the service, requested action, target path, and approximate time. Reproduce the same action once, then search the matching policy log. SELinux usually records AVC events in the Audit log; AppArmor denials commonly appear in the kernel journal.

Terminal window
# SELinux hosts with audit utilities:
sudo ausearch -m AVC,USER_AVC,SELINUX_ERR,USER_SELINUX_ERR -ts recent -i
# AppArmor hosts using systemd journal:
sudo journalctl -k --since "15 minutes ago" | grep -F 'apparmor="DENIED"'
❯ Illustrative Output (Results Vary)
SELinux: avc: denied { write } ... comm="example" path="/srv/app/data.db"
AppArmor: apparmor="DENIED" operation="open" profile="/usr/bin/example" name="/srv/app/data.db"

03

Read the Denial Before Changing Policy

Interpret

For SELinux, note the process context (scontext), target context (tcontext), object class, denied permission, and path. For AppArmor, note the profile, operation, requested access, and path. Confirm the event belongs to the affected process and not an unrelated background task.

Terminal window
# SELinux: inspect the process and target labels.
ps -eZ | grep '[e]xample'
ls -Zd /srv/app /srv/app/data.db
# AppArmor: check whether the named profile is loaded and enforced.
sudo aa-status
❯ Illustrative Output (Results Vary)
Match the process or profile, requested operation, and target path to the failed action.

04

Rule Out Ownership, Mode Bits, and ACLs

Check the Basics

Mandatory access control does not replace normal Unix permission checks. Inspect every parent directory and the target file, then review any ACL. A missing execute/search bit on a parent directory can look like a policy denial from the application’s point of view.

Terminal window
namei -l /srv/app/data.db
getfacl -p /srv/app/data.db
# SELinux only: compare the current label with the policy default.
matchpathcon -V /var/www/html/index.html
❯ Illustrative Output (Results Vary)
Confirm the service account can traverse parent directories and has the intended file access.
On SELinux, check whether a file copied or moved into place kept an unexpected label.

05

Apply a Narrow, Persistent Correction

Remediation

Correct a known label mismatch with restorecon. For a nonstandard location, add a persistent file-context mapping that matches the service’s real purpose, then apply it. On AppArmor, use aa-logprof to review logged denials interactively and add only the accesses the application requires.

Terminal window
# SELinux example: static web content stored in a custom directory.
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/myweb(/.*)?'
sudo restorecon -Rv /srv/myweb
# AppArmor example: review proposed profile changes interactively.
sudo aa-logprof
❯ Illustrative Output (Results Vary)
Use only the command for the policy system and application you have verified.
Review each proposed rule and its profile before saving changes.

06

Retest and Confirm the Policy Still Applies

Validation

Repeat the original application action, check its health and logs, then search for new denials. Confirm that SELinux remains enforcing or that the intended AppArmor profile remains enforced. If the fix does not match the evidence, roll back the local change and continue investigating the application path or configuration.

Terminal window
command -v getenforce >/dev/null && getenforce
command -v aa-status >/dev/null && sudo aa-status
command -v ausearch >/dev/null && sudo ausearch -m AVC,USER_AVC -ts recent -i
command -v journalctl >/dev/null && sudo journalctl -k --since "15 minutes ago" | grep -F 'apparmor="DENIED"'
❯ Illustrative Output (Results Vary)
Confirm the service works as intended and no new denial is associated with the test.

Comments