SELinux Hardening: Linux Mandatory Access Control Config Guide
SELinux hardening path from Disabled to Enforcing. Security contexts, semanage types, booleans, ausearch/audit2allow. Nginx verify commands. Meets MLPS baseline.
Scope
SELinux hardening applies to Linux servers that must meet MLPS compliance, production hosts running Web/DB/container services, and baseline findings of "SELinux not enabled". MAC confines process access. A compromised Web app still cannot leave its domain to read sensitive files.
Prerequisites
- Distros with SELinux: CentOS / RHEL / Rocky / AlmaLinux
- root, and
/etc/selinux/configbacked up - Basic ops for common services (Nginx, SSH)
- Validate policy on a test host before production
Mechanism
SELinux (Security-Enhanced Linux) is an NSA-led Linux security module. Core is MAC: every process and file has a security context (e.g. system_u:object_r:httpd_t:s0). Policy decides whether a process Domain may access a resource Type. Unlike DAC (rwx file perms), a deny in SELinux still blocks even if root and file perms allow it. Modes: Enforcing / Permissive / Disabled.
Procedure
1. Check current state
`# Check running mode
getenforce
# View detailed configuration
sestatus
# View policy package version
semanage --version`
If output is Disabled, edit the config to enable. If Permissive, watch denials then switch to enforcing.
2. Enable SELinux (Enforcing)
`# Edit config file (persistent)
sudo sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config
grep '^SELINUX=' /etc/selinux/config
# Apply immediately (temporary switch)
sudo setenforce 1
# Confirm again
getenforce # Output: Enforcing`
Note: files written on ext4/xfs while Disabled lack labels. Jumping Disabled → Enforcing can fail services. Safer: touch /.autorelabel then reboot so the system relabels.
3. Inspect process and file contexts
`# View process context
ps -eZ | grep nginx
# View file context
ls -Z /usr/share/nginx/html/
# View port context
semanage port -l | grep http`
4. Set the right type on custom dirs
`# Label /data/www as Web content type (readable by httpd)
sudo semanage fcontext -a -t httpd_sys_content_t '/data/www(/.*)?'
# Apply rules (immediate)
sudo restorecon -Rv /data/www
# Verify
ls -Z /data/www`
5. Flip booleans for common features
`# List httpd-related booleans
getsebool -a | grep httpd
# Allow httpd outbound network (e.g. reverse proxy)
sudo setsebool -P httpd_can_network_connect on
# Allow httpd to connect to database ports
sudo setsebool -P httpd_can_network_connect_db on
# -P persists across reboot`
6. Debug SELinux denials
`# View denial records
sudo ausearch -m avc -ts recent
# Explain denials with audit2why
sudo ausearch -m avc -ts recent | audit2why
# Generate and inspect allow policy (temporary debug)
sudo ausearch -m avc -ts recent | audit2allow -M mynginx
cat mynginx.te`
After confirming the cause, load the module: sudo semanage module -a mynginx.pp. In production, prefer semanage type/boolean changes over allowing every AVC.
7. Verify Nginx is up
`sudo systemctl restart nginx
curl -s -o /dev/null -w "%{http_code}\n" http://127.0.0.1/
# Expected 200`
Verification
`# Verify 1: running mode
getenforce # Should be Enforcing
# Verify 2: service context is correct
ps -eZ | grep nginx # Should contain httpd_t
# Verify 3: custom directory labels are correct
ls -Zd /data/www # Should contain httpd_sys_content_t
# Verify 4: no new AVC denials recently (when service is healthy)
sudo ausearch -m avc -ts recent | tail -5`
FAQ
FAQ 1: Nginx 403 or won't start after enabling SELinux?
Almost always a wrong file type. ls -Z the site dir; it must be httpd_sys_content_t. If not, run step 4 (semanage fcontext + restorecon). For proxy/DB, check the matching booleans.
FAQ 2: App conflicts with SELinux. Disable it?
No. You lose MAC and fail MLPS scoring. Switch to Permissive for 1–2 weeks, collect AVCs with ausearch, allow them one by one, then Enforcing. That is the standard production path.
FAQ 3: Does SELinux affect Docker?
Yes. Containers need container_t policy. Docker is adapted by default. On permission errors, check docker run --security-opt label=disable or host boolean container_manage_cgroup. Prefer the official runtime; avoid custom policy clashes.
Takeaway
SELinux is the cheapest, highest-ROI MAC layer on Linux. Path from Disabled to Enforcing: Permissive first, fix types/booleans with semanage, allow precisely via ausearch/audit2allow. Put getenforce in baseline scripts. Production stays Enforcing.