Ubuntu 26.04 Tutorial / Inspect AppArmor Profiles and Denial Logs

AppArmor adds per-program controls over files, execution, networking, and other operations. Investigate ordinary filesystem permissions alongside the active profile and denial logs rather than disabling AppArmor globally as the first fix.

Distinguish ordinary permissions from profiles

A program can be denied by AppArmor even when user, group, and file mode allow access. Conversely, no AppArmor message does not establish correct directory permissions, mount state, or service identity. Record the failing path and executing account first.

Inspect loaded profiles and modes

sudo aa-status
systemctl status apparmor --no-pager

Enforce and complain modes differ: enforce applies policy, while complain helps investigation but can reduce protection. Service status alone does not prove every process is confined; inspect loaded profiles and associated processes in aa-status.

Ubuntu’s security documentation explains AppArmor enforcement, modes, and confinement scope.

Correlate the failure with recent logs

sudo journalctl -k -b --since '10 minutes ago' --no-pager | grep 'apparmor="DENIED"'

Record the time of one controlled reproduction and correlate profile, operation, name, pid, requested_mask, and denied_mask. Use the actual output instead of presenting fabricated logs as a machine result. Share only necessary lines because paths and user details can be sensitive.

If no output appears, check boot, time range, collection path, and the actual profile. Explicit deny rules and other conditions may not produce the expected message, so absence of logs is not proof of permission.

Read denial fields using an illustrative example

The line below is an invented field-reading example, not a log collected from a particular Ubuntu machine. profile identifies the policy, operation the attempted action, and name the target path. The r in requested_mask and denied_mask represents requested and denied reading.

apparmor="DENIED" operation="open" profile="/usr/sbin/example-app" name="/srv/example-data/settings.ini" requested_mask="r" denied_mask="r"

In a real investigation, correlate failure time, process, identity, and path. This is not a prescription to grant access to the illustrative path. Restoring a supported data location may be the appropriate fix; do not allow every logged request.

Identify the profile and its owner

For ordinary profiles, identify the name and executable under /etc/apparmor.d. For generated policies such as Snap profiles, inspect interfaces and publisher settings rather than editing generated output. Do not invent a profile filename or copy another program’s policy blindly.

If an unsupported data-path change caused the issue, first consider restoring a supported location. A policy change needs a documented operation, exact path, access scope, and backups of the profile and local includes. Do not allow the whole disk through broad wildcards.

Check a profile without loading it into the kernel

Identify one ordinary profile’s actual source file and enter it below; exclude Snap-generated policies from this editing exercise. -Q skips kernel loading and -K disables caching. Successful compilation does not establish correct application allow/deny behavior.

(
set -eu
read -r -p 'Actual ordinary profile path under /etc/apparmor.d/: ' profile
case "$profile" in
    /etc/apparmor.d/*) ;;
    *) printf 'Choose the verified profile source.\n' >&2; exit 1 ;;
esac
sudo test -f "$profile"
sudo apparmor_parser -Q -K "$profile"
)

If a scoped policy edit is actually required, back up the source and local includes, pass this check, and reload that profile with sudo apparmor_parser -r ACTUAL_PROFILE_FILE. On failure, restore those backups, validate/reapply, and check aa-status and the original enforce state. Do not reload anything for inspection-only practice.

Check the official AppArmor 5.0 manual for compilation, cache, and kernel replacement behavior.

Define narrow changes, checks, and rollback

Validate an edited profile with official tools, reload only that profile, and test both the operation that should work and operations that should remain denied. Review diagnostic tools’ proposed rules rather than approving every logged request.

On failure, restore only the policy change and verify the original enforce state and application behavior. Do not make the test pass by disabling AppArmor or user-namespace protections globally. Record cause, profile, scope, allowed and denied tests, and rollback steps.