cheat-sheets

SELinux and AppArmor

Two Linux Mandatory Access Control systems. SELinux (RHEL, Fedora, Rocky, Alma) labels every file and process and applies a policy. AppArmor (Ubuntu, Debian, SUSE) confines programs with per-path profiles. Both limit what a compromised service can reach, even when it runs as root. Use permissive or complain mode to debug; do not switch them off.

Documentation: Red Hat SELinux guide · Ubuntu AppArmor · AppArmor wiki

Verification: commands follow the distribution documentation and man pages. No command was executed. Package names differ by distribution.

See also: systemd · Linux · Docker · UFW (uncomplicated firewall)


SELinux #

Status and Modes #

COMMANDDESCRIPTION
getenforceEnforcing, Permissive or Disabled
sestatusMode, policy, config file mode
sudo setenforce 0Permissive until reboot (logs denials, blocks nothing)
sudo setenforce 1Enforcing again
sudo grubby --update-kernel ALL --args selinux=0Disable at boot (not recommended)

Persistent mode: SELINUX=enforcing|permissive|disabled in /etc/selinux/config. Moving from disabled to enforcing triggers a full relabel on the next boot (sudo touch /.autorelabel).

Contexts #

A context looks like user:role:type:level. The type (for example httpd_sys_content_t) is what policy matches.

COMMANDDESCRIPTION
ls -Z <PATH>File contexts
ps -eZ | grep <NAME>Process contexts
id -ZYour own context
sudo restorecon -Rv <PATH>Reset file contexts to the policy default
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?"Persistent rule for a path
sudo semanage fcontext -l | grep <PATH>List rules
sudo chcon -t <TYPE> <FILE>Temporary change (lost on relabel). Prefer semanage + restorecon

Typical fix for a web root in a non-standard place:

bash
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?"
sudo restorecon -Rv /srv/www

Package: policycoreutils-python-utils (RHEL 8+) provides semanage.

Booleans and Ports #

COMMANDDESCRIPTION
getsebool -a | grep httpdList booleans
sudo setsebool -P httpd_can_network_connect onAllow httpd outbound connections (-P persists)
sudo setsebool -P httpd_can_network_connect_db onAllow httpd to reach a database
sudo semanage boolean -lBooleans with descriptions
sudo semanage port -l | grep httpPorts labelled for a service
sudo semanage port -a -t http_port_t -p tcp 8081Allow a service on a non-standard port
sudo semanage port -a -t ssh_port_t -p tcp 2222Same for SSH

Read Denials #

COMMANDDESCRIPTION
sudo ausearch -m AVC -ts recentRecent denials
sudo ausearch -m AVC -ts today | audit2whyExplain why, often names the boolean to flip
sudo journalctl -t setroubleshootPlain-English alerts (needs setroubleshoot-server)
sudo sealert -a /var/log/audit/audit.logAnalyse the audit log
sudo grep denied /var/log/audit/audit.logRaw denials

Build a Local Policy Module (audit2allow) #

Only after you have ruled out a wrong label, a boolean or a port label, and have read what you are allowing.

bash
sudo ausearch -m AVC -ts recent | audit2allow -M myapp
cat myapp.te                      # review it
sudo semodule -i myapp.pp
COMMANDDESCRIPTION
sudo semodule -lInstalled modules
sudo semodule -r myappRemove a module
sudo semanage permissive -a httpd_tMake one domain permissive, the rest stay enforcing
sudo semanage permissive -d httpd_tUndo

Containers and Volumes #

Bind mounts for Docker and Podman need a label or the container is denied:

bash
docker run -v /srv/app:/data:Z <IMAGE>    # private label for this container
docker run -v /srv/shared:/data:z <IMAGE> # shared label for several containers

Do not use :Z on system directories such as /home or /etc. See Docker.


AppArmor #

Status #

COMMANDDESCRIPTION
sudo aa-statusLoaded profiles, modes and confined processes
sudo apparmor_statusSame
systemctl status apparmorService state
ps auxZ | grep -v unconfinedConfined processes
ls /etc/apparmor.d/Profiles
cat /sys/module/apparmor/parameters/enabledY if the kernel has it on

Modes #

MODEEFFECT
enforceViolations are blocked and logged
complainViolations are logged only (like SELinux permissive)
unconfinedNo profile
COMMANDDESCRIPTION
sudo aa-complain /etc/apparmor.d/<PROFILE>Put a profile in complain mode
sudo aa-enforce /etc/apparmor.d/<PROFILE>Back to enforce
sudo aa-disable /etc/apparmor.d/<PROFILE>Disable a profile (links it into disable/)
sudo apparmor_parser -r /etc/apparmor.d/<PROFILE>Reload after editing
sudo apparmor_parser -R /etc/apparmor.d/<PROFILE>Unload
sudo aa-complain /etc/apparmor.d/*All profiles to complain (debugging only)

Read Denials #

COMMANDDESCRIPTION
sudo journalctl -k | grep -i apparmorKernel log lines with apparmor="DENIED"
sudo dmesg | grep -i 'apparmor="DENIED"'Same from dmesg
sudo aa-logprofWalk through logged events and update profiles interactively
sudo aa-genprof <PROGRAM>Create a new profile by running the program
sudo aa-notify -s 1 -vSummary of denials (desktop)

Install the tools: sudo apt install apparmor-utils apparmor-profiles.

Local Overrides #

Do not edit packaged profiles. Add rules to the local include file named in the profile, for example /etc/apparmor.d/local/usr.sbin.mysqld:

/srv/mysql/ r,
/srv/mysql/** rwk,

Then reload: sudo apparmor_parser -r /etc/apparmor.d/usr.sbin.mysqld. This is the usual fix when moving a database data directory (MySQL, PostgreSQL Cheat-Sheet).

Docker #

Docker applies the docker-default AppArmor profile to containers when AppArmor is on.

COMMANDDESCRIPTION
docker run --security-opt apparmor=<PROFILE> <IMAGE>Use a custom profile
docker run --security-opt apparmor=unconfined <IMAGE>No AppArmor profile (debugging only)
docker inspect -f '{{.AppArmorProfile}}' <CONTAINER>Profile in use

Quick Comparison #

SELinuxAppArmor
ModelLabels on files and processesPaths in profiles
Default onRHEL familyUbuntu, Debian, SUSE
Debug modePermissiveComplain
Denial logaudit.log (ausearch)Kernel log / journalctl -k
Fix helperaudit2allow, booleans, semanageaa-logprof, local include files
Search all sheets

Tip: / or Ctrl/Cmd+K to open, Esc to close.