SELinux Cheat Sheet
SELinux modes, reading and changing file context with semanage and restorecon (not chcon), booleans, and reading AVC denials - the parts that are a genuine knowledge gap for most people studying for RHCSA, who often just disable SELinux instead of learning it.
SELinux enforces a second, independent permission system on top of normal Unix permissions - a process can pass every rwx check and still get blocked, because its SELinux context doesn’t match what policy allows for that type of file or port. This is the single most common reason a service “should work” (permissions look fine, the config looks fine) and still returns a permission error.
SELinux modes
getenforce # current mode: Enforcing, Permissive, or Disabled
sudo setenforce 0 # switch to Permissive right now - temporary, lost on reboot
sudo setenforce 1 # switch back to Enforcing right now
sudo nano /etc/selinux/config # SELINUX=enforcing|permissive|disabled - takes effect on next reboot
Enforcing policy violations are blocked and logged
Permissive policy violations are only logged, nothing is blocked - useful for diagnosis, not a fix
Disabled no SELinux at all - no blocking and no logging either, losing the audit trail too
setenforce 0is a diagnostic tool, not a fix - using it to confirm SELinux is the cause of a problem is standard practice; leaving a system in Permissive (or switching to Disabled) because that made the error go away is not a fix, it’s turning off the thing that was correctly doing its job. See the SELinux denied troubleshooting lab for this used correctly, as one diagnostic step among several.
Reading SELinux context
ls -Z /var/www/html/ # file contexts
ps -Z -C nginx # the context a running process is actually in
id -Z # the current shell's own context
unconfined_u:object_r:httpd_sys_content_t:s0
^ ^ ^ ^
user role type sensitivity level
The type (httpd_sys_content_t above) is what matters for almost everything day-to-day - it’s what policy rules actually key off when deciding whether a process can read, write, or execute a given file. The user/role/level fields matter far less on a typical single-purpose server.
Changing context: chcon vs. semanage + restorecon
sudo chcon -t httpd_sys_content_t /srv/webdata/index.html # changes the label on this file, right now
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/webdata(/.*)?' # add a persistent policy rule for this path
sudo restorecon -Rv /srv/webdata # apply it to the files that actually exist
chcon semanage fcontext + restorecon
----- ------------------------------
Changes the label now Writes a rule to the policy database itself
Temporary Persistent
Lost on the next Survives restorecon, a full relabel,
restorecon/relabel and reinstalling the package that owns the path
Fine for a quick test The actual fix for anything meant to stay
chconis the trap here - it makes the symptom disappear immediately, which looks like success, but the very nextrestorecon -R /(or an automatic relabel, like the onetouch /.autorelabeltriggers - see the boot process cheat sheet) silently reverts it and the original problem comes back with no obvious cause. Usesemanage fcontext+restoreconfor anything that needs to survive more than the current moment.
Finding the right context type for a path
matchpathcon /var/www/html/index.html # what context *should* this path have, per current policy
semanage fcontext -l | grep httpd_sys # list existing policy rules matching a type name
matchpathcon is the fastest way to answer “what type am I actually supposed to use here” without guessing or searching documentation - it checks the same policy database restorecon reads from.
SELinux booleans - policy on/off switches
getsebool -a | grep httpd # every httpd-related boolean and its current state
sudo setsebool httpd_can_network_connect on # change it now, temporary - lost on reboot
sudo setsebool -P httpd_can_network_connect on # -P makes it persistent across reboots
Booleans toggle entire pre-defined chunks of policy without writing custom rules - httpd_can_network_connect, for example, is exactly what a reverse-proxy or anything calling out to another service over the network needs enabled, and is off by default.
Reading AVC denials
sudo ausearch -m avc -ts recent # recent denials from the audit log, in a readable form
sudo journalctl -t setroubleshoot # if setroubleshoot is installed, plain-English summaries instead
type=AVC msg=audit(...): avc: denied { read } for pid=1234 comm="nginx"
scontext=system_u:system_r:httpd_t:s0
tcontext=unconfined_u:object_r:default_t:s0
tclass=file
scontext the context the PROCESS was running as when it got blocked
tcontext the context of the TARGET (the file/port/etc it was trying to access)
tclass what kind of object it was trying to act on (file, dir, tcp_socket, ...)
The mismatch between scontext’s expected type and tcontext’s actual type is the whole story - here, httpd_t trying to read something still labeled default_t instead of an httpd_* type, which is exactly the “served from a non-standard directory” scenario in the troubleshooting lab.
Common recipes
# Confirm whether SELinux is actually the cause of a given error (diagnostic only - see the warning above)
sudo setenforce 0; <reproduce the problem>; sudo setenforce 1
# Permanently fix a "wrong context" problem for an entire directory tree
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/webdata(/.*)?'
sudo restorecon -Rv /srv/webdata
# Let a web server make outbound connections (reverse proxy, remote API calls, etc.)
sudo setsebool -P httpd_can_network_connect on
# See the last few denials in plain language
sudo ausearch -m avc -ts recent | audit2why
Safety notes
- Never leave a production system in Permissive mode, or switch it to Disabled, as “the fix” for a denial - both remove real protection; Permissive also still logs (useful), Disabled does not (actively worse for ever diagnosing anything again).
- Prefer
semanage fcontext+restoreconoverchconfor anything that should still work after the next relabel, package update, orrestorecon -R /-chconchanges are real but not written to policy, so they’re silently temporary. - Scope
restorecon/chconcommands to the exact path that needs it - running either recursively from/is destructive to the system’s labeling and not something to do “just in case.” audit2allowcan generate a custom policy module from denials automatically, but reach for the standardsemanage fcontext/setsebooltools first - a hand-rolled policy module is a last resort for something policy genuinely has no existing type or boolean for, not a shortcut around understanding the denial.- Install
setroubleshoot-server(sudo dnf install setroubleshoot-server) on a system used for learning/troubleshooting -sealert/journalctl -t setroubleshootturn cryptic AVC lines into a plain-language explanation and a suggested fix.