SELinux (Security-Enhanced Linux) is enabled and set to enforcing mode by default on CentOS Stream, providing mandatory access control that significantly improves security by confining processes beyond standard DAC (Discretionary Access Control) permissions.
The most common reason people disable SELinux is frustration from denials — but in 2026 production environments (web servers, containers, databases), keeping SELinux enforcing is strongly recommended. Disabling it removes a critical defense layer against privilege escalation, misconfigurations, and zero-days.
This guide focuses on safe, non-destructive configuration — understanding the system, diagnosing issues, applying targeted fixes, and avoiding blanket disablement.
1. Understand the Current State First
Always start here before changing anything:
- Check mode & policy: sestatus (shows Current mode, Mode from config file, Policy MLS/targeted, etc.)
- Quick runtime toggle (reversible, non-persistent):
- To permissive (logs violations but allows them): sudo setenforce 0
- Back to enforcing: sudo setenforce 1
- Permanent change (requires reboot): Edit /etc/selinux/config and set SELINUX=permissive or disabled. Avoid disabled in production unless you have a very specific justification (e.g., legacy app incompatible with any SELinux policy).
Best practice in 2026: Stay in enforcing. Use permissive only temporarily for troubleshooting.
2. Diagnose Denials – The Safe Way (Don’t Guess)
When something breaks (e.g., Nginx can’t read files, MariaDB refuses connections, Podman/Docker volume permission denied):
- Look at denials in real time: sudo ausearch -m avc -ts recent or tail the audit log: sudo tail -f /var/log/audit/audit.log | grep denied
- Use sealert for human-readable explanations (most useful tool): Install if missing: sudo dnf install setroubleshoot-server Then: sudo sealert -a /var/log/audit/audit.log It gives:
- What was denied
- Suggested boolean or context fix
- Command to apply the fix (often setsebool -P …)
- Common denial patterns in 2026:
- Web server on non-standard port → needs httpd_can_network_connect or port label
- Custom document root → wrong file context (httpd_sys_content_t)
- Container bind mounts → missing :z / :Z suffix
- PHP-FPM / Apache → httpd_can_network_connect_db for external DB
- NFS / Samba shares → httpd_use_nfs, samba_export_all_rw, etc.
3. Fix Issues Without Disabling SELinux
Targeted fixes preserve security:
Boolean adjustments (most common & safest): List relevant booleans: getsebool -a | grep httpd or semanage boolean -l | grep container Enable persistently: sudo setsebool -P httpd_can_network_connect 1 Or: sudo setsebool -P container_manage_cgroup on (for advanced Podman) -P makes it survive reboot.
Popular booleans in web/container environments:
- httpd_can_network_connect → allow web server outbound connections
- httpd_can_network_connect_db → allow DB connections from httpd
- httpd_enable_homedirs → serve content from user homes
- container_use_devices → give containers /dev access
File/Directory Contexts (when moving content): Wrong label → permission denied even with chmod 777. Fix: sudo restorecon -Rv /var/www/custompath Add permanent custom mapping: sudo semanage fcontext -a -t httpd_sys_content_t “/new/path(/.*)?” Then restorecon -Rv /new/path
Non-standard ports (e.g., Apache/Nginx on 8080): sudo semanage port -a -t http_port_t -p tcp 8080
Containers (Docker / Podman): Use :z (shared label) or :Z (private label per container) on volumes: -v /host/data:/container/data:z Install container-selinux if missing (usually pulled automatically). For rootless Podman, ensure user namespaces align.
Generate custom policy module (last resort, advanced): Capture denials → grep denied /var/log/audit/audit.log | audit2allow -M myfix Install: semodule -i myfix.pp Review the .te file before applying.
4. Safe Workflow for Any Change
- Make the configuration change (move dir, change port, start service).
- Test → if it fails, switch to permissive temporarily (setenforce 0).
- Reproduce failure → collect denials with ausearch / sealert.
- Apply targeted fix (boolean, fcontext, port, custom module).
- Restore enforcing: setenforce 1.
- Verify fix works in enforcing mode.
- Document the change (e.g., in Ansible role or README).
5. Production Recommendations (2026)
- Never disable in production — use permissive only during initial setup or debugging.
- Monitor denials → forward audit.log to SIEM or simple cron job emailing sealert output.
- Automate with Ansible (ansible.posix.seboolean, ansible.posix.seport, etc.) for fleets.
- Regular policy updates → sudo dnf update selinux-policy* policycoreutils* setroubleshoot*
- Test in staging — especially after major OS/package updates (kernel or policy changes can introduce new denials).
Quick Reference Table: Common Fixes
| Symptom | Likely Cause | Safe Fix Command(s) |
|---|---|---|
| Web server 403 on custom path | Wrong context | restorecon -Rv /path or semanage fcontext … |
| Nginx/Apache can’t connect to DB | Boolean | setsebool -P httpd_can_network_connect_db 1 |
| Container volume permission denied | Mount label | Add :z or :Z to -v / –volume |
| Service on non-80/443 port blocked | Port label | semanage port -a -t http_port_t -p tcp 8080 |
| Home dir content not served | Boolean | setsebool -P httpd_enable_homedirs 1 |
| Random denials after update | Policy change | sealert -a /var/log/audit/audit.log → follow advice |
SELinux isn’t the enemy — mislabeled files and unadjusted booleans are. Master sestatus, ausearch, sealert, setsebool -P, and restorecon, and you’ll keep enforcing mode while running modern workloads reliably.