SELinux on RHEL family is excellent — and it is the first thing many operators reach to disable when an app misbehaves. Don't. Permissive mode logs would-be denials without enforcing them, which lets you debug without losing the safety net.
Prerequisites
- Rocky 9 / Alma 9 / RHEL 9 with
sudo. - A service or app that "works on the dev box and breaks on the RHEL server."
Step 1: Confirm current SELinux state
sestatus
getenforce # Enforcing | Permissive | Disabled
Three modes:
- Enforcing — default. Blocks anything not allowed by policy.
- Permissive — logs denials but allows the operation. Ideal for debugging.
- Disabled — turns SELinux off entirely. Only as a last resort.
Step 2: Switch to permissive — runtime only
sudo setenforce 0
getenforce # Permissive
This is non-persistent. A reboot puts it back to Enforcing. Use this while you reproduce the bug.
Step 3: Look at the denials
sudo grep -i 'avc:.*denied' /var/log/audit/audit.log | tail -30
sudo ausearch -m AVC -ts recent | tail
sudo journalctl _TRANSPORT=audit -p err -n 50
Better, use sealert (from setroubleshoot-server):
sudo dnf install -y setroubleshoot-server
sudo sealert -a /var/log/audit/audit.log | less
sealert summarises each denial AND prints the exact semanage / chcon / audit2allow command to allow it.
Step 4: Fix the policy properly
The typical resolutions:
Wrong file label — most common:
sudo ls -lZ /var/www/html/missing-page.html
sudo restorecon -Rv /var/www/html
Custom directory outside the standard tree:
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/web(/.*)?'
sudo restorecon -Rv /srv/web
Custom port for a known service:
sudo semanage port -a -t http_port_t -p tcp 8081
Real policy gap — use audit2allow to generate a module:
sudo ausearch -m AVC -ts recent | audit2allow -M skyline-myapp
sudo semodule -i skyline-myapp.pp
Inspect the generated .te file before loading — audit2allow is happy to write a too-permissive policy.
Step 5: Switch back to enforcing
sudo setenforce 1
getenforce # Enforcing
If you must make permissive persist across reboot (e.g., for an extended outage), edit /etc/selinux/config:
SELINUX=permissive
SELINUX=disabled is strongly discouraged — it skips relabelling on next boot and is hard to recover from cleanly.
Verify
sestatus
sudo ausearch -m AVC -ts today | wc -l
sudo grep "denied" /var/log/audit/audit.log | wc -l
After your fix, denials should drop to zero.
Conclusion
SELinux is a friend, not an obstacle. Use permissive + sealert + audit2allow to learn what your app actually needs, ship a tiny policy module, and stay enforcing. Five minutes of policy work beats a year of "we just turn it off."
Next steps
- For firewall rules see firewalld zones.
- Understand DNF and modules with DNF basics.
- For systemd service hardening see manage systemd services.
Comments
0 total · 0 threads