SELinux (Security-Enhanced Linux) is a mandatory access control (MAC) framework built into the Linux kernel and enabled by default on every RHEL 8 installation. Unlike discretionary access controls such as standard Unix permissions, SELinux enforces policy rules that confine processes to only the files, sockets, and capabilities they legitimately require, limiting the blast radius of a compromised service. Many administrators disable SELinux at the first sign of trouble, which eliminates a critical security layer; this guide instead demonstrates how to manage SELinux correctly. You will learn to read contexts, toggle booleans, write custom policy modules from audit denials, restore contexts, and manage port labels — all while keeping SELinux in enforcing mode.
Prerequisites
- RHEL 8 server with a non-root sudo user
- SELinux in enforcing mode (verify with
getenforce) - The
policycoreutils-python-utilsandsetroubleshoot-serverpackages installed - Basic familiarity with the services you intend to configure (Apache, nginx, etc.)
- Audit daemon running (
systemctl status auditd)
Step 1 — Check SELinux Status and Understand Modes
RHEL 8 ships with SELinux in enforcing mode and the targeted policy active. Understand the three operational modes before making any changes.
# Quick mode check
getenforce
# Detailed status including policy type and loaded modules
sestatus
# Temporarily switch to permissive mode for troubleshooting (resets on reboot)
sudo setenforce 0
getenforce
# Switch back to enforcing without rebooting
sudo setenforce 1
To make a mode change permanent, edit /etc/selinux/config:
# /etc/selinux/config
SELINUX=enforcing # enforcing | permissive | disabled
SELINUXTYPE=targeted
Never set SELINUX=disabled in production. Use permissive mode for troubleshooting, then return to enforcing once the policy is corrected.
Step 2 — Read SELinux Contexts on Files and Processes
Every file, directory, port, and process has an SELinux label called a security context in the format user:role:type:level. The type field drives most policy decisions.
# View file contexts
ls -Z /var/www/html/
ls -Z /etc/httpd/conf/
# View process contexts
ps -eZ | grep httpd
ps -eZ | grep sshd
# View context on a socket
ls -Z /run/clamd.scan/
# Install policy tools if not present
sudo dnf install -y policycoreutils-python-utils setroubleshoot-server
Step 3 — Manage SELinux Booleans
Booleans are on/off switches that adjust policy behavior without writing custom modules. Use getsebool and setsebool to query and set them. The -P flag makes changes persistent across reboots.
# List all booleans and their current state
getsebool -a | grep httpd
# Allow Apache to make outbound network connections (e.g. for a PHP proxy)
sudo setsebool -P httpd_can_network_connect on
# Allow Apache to connect to databases
sudo setsebool -P httpd_can_network_connect_db on
# Allow Apache to serve files from user home directories
sudo setsebool -P httpd_enable_homedirs on
# Allow SMTP from application processes
sudo setsebool -P httpd_can_sendmail on
# Verify change
getsebool httpd_can_network_connect
Step 4 — Diagnose and Resolve AVC Denials with audit2allow
When SELinux blocks an action, it logs an AVC (Access Vector Cache) denial to /var/log/audit/audit.log. Use ausearch and audit2allow to analyze denials and generate a targeted policy module that permits only the specific access needed.
# View recent AVC denials
sudo ausearch -m AVC -ts recent
# Get a human-readable explanation of the most recent denial
sudo sealert -a /var/log/audit/audit.log | head -60
# Generate a policy module from recent AVC denials
sudo ausearch -m AVC -ts recent | audit2allow -M mypolicy
# Review the generated .te (type enforcement) file before loading
cat mypolicy.te
# Load the module into the running policy
sudo semodule -i mypolicy.pp
# Verify the module is loaded
sudo semodule -l | grep mypolicy
Step 5 — Restore File Contexts with restorecon and semanage fcontext
Files placed in non-standard locations or created by copying rather than moving can inherit incorrect contexts. Use restorecon to reset them to the policy default, and semanage fcontext to teach SELinux about custom paths.
# Restore default context on a file or directory
sudo restorecon -v /var/www/html/myapp.php
# Recursively restore an entire directory
sudo restorecon -Rv /var/www/html/
# Assign a custom path the same context as the standard web root
sudo semanage fcontext -a -t httpd_sys_content_t "/opt/myapp(/.*)?"
# Apply the new mapping
sudo restorecon -Rv /opt/myapp/
# Verify
ls -Z /opt/myapp/
Step 6 — Manage Port Contexts with semanage port
SELinux labels TCP/UDP ports just as it labels files. If a service is configured to listen on a non-default port, add the port label to avoid an AVC denial blocking the bind.
# List all currently labeled ports for HTTP
sudo semanage port -l | grep http
# Allow Apache to listen on port 8443
sudo semanage port -a -t http_port_t -p tcp 8443
# Allow SSH to listen on port 2222 (if not already set during SSH hardening)
sudo semanage port -a -t ssh_port_t -p tcp 2222
# Verify
sudo semanage port -l | grep ssh
# If a port label already exists for the wrong type, modify instead of add
sudo semanage port -m -t http_port_t -p tcp 8443
Conclusion
You now have the skills to operate effectively with SELinux in enforcing mode on RHEL 8: reading and interpreting security contexts, toggling booleans for common service requirements, generating and loading custom policy modules from real AVC denials, restoring file contexts after path changes, and labeling non-standard ports. Keeping SELinux in enforcing mode is one of the most impactful security controls available on RHEL 8 and should never be disabled as a troubleshooting shortcut — use permissive mode temporarily, resolve the policy issue, then re-engage enforcing.
Next steps: How to Harden SSH on RHEL 8, How to Install ClamAV Antivirus on RHEL 8, and How to Set Up Auditd on RHEL 8.