Linux Security

AppArmor vs SELinux overview

Practical Linux guide: appArmor vs SELinux overview without the usual guesswork.

18 min read Advanced Updated 9 Jun 2026

Step-by-step guide

Work through each section in order. Stop when your issue is resolved — you do not need every step for every situation.

What you will achieve

Know whether AppArmor or SELinux is enforcing on your system, how to spot denials, and when disabling is a terrible idea versus tuning a profile.

1) Which MAC system?

# Debian/Ubuntu
sudo aa-status

# Fedora/RHEL
getenforce
sestatus

2) AppArmor basics (Ubuntu)

sudo aa-enforce /usr/sbin/nginx
sudo journalctl | grep -i apparmor
sudo dmesg | grep -i denied

Profiles live in /etc/apparmor.d/. Use aa-complain mode temporarily while debugging.

3) SELinux basics (Fedora/RHEL)

sudo ausearch -m avc -ts recent
sudo setsebool -P httpd_can_network_connect 1
sudo restorecon -Rv /var/www/html

4) Containers and MAC

Docker/Podman on SELinux systems need :z or :Z volume labels. AppArmor adds docker-default profile on Ubuntu.

Verify

After fixing denials, rerun the blocked action — logs should be clean and the service should start.

5) Generate custom AppArmor profile

sudo aa-genprof /usr/local/bin/myapp

Run app through normal use; tool learns required paths and capabilities.

6) SELinux port labeling

sudo semanage port -a -t http_port_t -p tcp 8080

Binding Apache to non-standard port without label change = permission denied.

7) Permissive vs enforcing

sudo setenforce 0   # temporary permissive — logs only
sudo setenforce 1   # back to enforcing

Never disable SELinux in /etc/selinux/config permanently — fix contexts instead.

Ubuntu Docker

sudo aa-status | grep docker

Custom containers may need AppArmor profile overrides or --security-opt apparmor=unconfined only as last resort.

8) Confined snap/flatpak overlap

Desktop apps may hit AppArmor, SELinux, and snap security profiles simultaneously — check denial logs from each layer when sandboxed browser cannot download.

Prerequisites

Know which MAC is active (aa-status vs getenforce). Root to switch modes temporarily. Application logs and audit log access. Never disable MAC permanently without change approval.

auditd storage

SELinux AVC logs can fill /var/log/audit — rotate via logrotate audit rule on busy servers.

Container breakout context

MAC limits container escape damage — pairing with seccomp and user namespaces on Docker/Podman is defence in depth, not either-or.

Writing minimal policy

Start permissive/complain, exercise app fully, promote to enforce — never enforce empty profile on day one. SELinux audit2allow generates boilerplate but review every allow rule — lazy audit2allow -M local creates overly broad policy. AppArmor abstractions in /etc/apparmor.d/abstractions/ reuse nginx/apache patterns instead of reinventing.

Disabling for one service only

Prefer per-profile complain over global setenforce 0 — global permissive hides problems across all daemons. Document time-bound permissive window in change ticket with rollback to enforce.

Related guides

apparmor linux overview selinux