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.