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.
Identify the Active Security Module
Read FirstCheck 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.
command -v getenforce >/dev/null && getenforcecommand -v sestatus >/dev/null && sestatuscommand -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.Reproduce the Failure and Search Recent Logs
Collect EvidenceRecord 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.
# 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"Read the Denial Before Changing Policy
InterpretFor 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.
# 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.Rule Out Ownership, Mode Bits, and ACLs
Check the BasicsMandatory 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.
namei -l /srv/app/data.dbgetfacl -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.Apply a Narrow, Persistent Correction
RemediationCorrect 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.
# 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.Retest and Confirm the Policy Still Applies
ValidationRepeat 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.
command -v getenforce >/dev/null && getenforcecommand -v aa-status >/dev/null && sudo aa-statuscommand -v ausearch >/dev/null && sudo ausearch -m AVC,USER_AVC -ts recent -icommand -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.