Keeping SELinux Enforcing on RHEL: Diagnose AVC Denials and Fix Labels, Ports, and Booleans
Keep RHEL's targeted SELinux policy in enforcing mode: diagnose AVC denials, fix file contexts, ports, and booleans correctly, and recover a system that breaks under enforcement.
03 Jun 2026, 16:07 UTC

When an application breaks on RHEL with SELinux enabled, the tempting fix is setenforce 0 — and on many systems that temporary test quietly becomes permanent. The better outcome: keep the default targeted policy in enforcing mode and fix the actual denial, which is almost always a mislabeled path, a missing boolean, or a nonstandard port. This guide walks through that diagnosis-and-fix loop, plus how to recover a system that misbehaves badly under enforcement. Commands assume a recent RHEL major release (8/9-era tooling); verify package names and boolean defaults on your specific release, since they do change.
Prerequisites
- Root access (or full sudo) on a RHEL test or staging system — do not first try this on production.
- The SELinux userland tools:
getenforce,sestatus,restorecon,semanage, andaudit2allow. The last two ship in a policycoreutils Python tools package whose exact name varies by release; find it withdnf provides '*/semanage'if missing. auditdrunning, so AVC (Access Vector Cache) denials are logged:systemctl status auditd.
Confirm the baseline
First establish what mode and policy you actually have. Run as root:
sestatus
getenforce
grep -E '^SELINUX' /etc/selinux/config
You want getenforce to report Enforcing and the config file to show SELINUX=enforcing with SELINUXTYPE=targeted. Note the distinction that trips people up: setenforce changes only the runtime mode and is lost at reboot; persistent mode lives in /etc/selinux/config.
If a new application is misbehaving and you suspect SELinux, run setenforce 0, retest the application, then immediately run setenforce 1. This confirms SELinux is the cause — it is a diagnostic step, not the fix.
Collect and classify the denials
Reproduce the failure under enforcing mode, then pull recent denials:
ausearch -m avc -ts recent
journalctl -t kernel | grep 'avc: denied'
If the setroubleshoot tooling is installed, sealert -a /var/log/audit/audit.log translates raw AVCs into plain-language explanations and often names the exact boolean or file type you need. Classify each denial into one of four buckets:
- Mislabeled path — a daemon can't read/write files in a nonstandard location (the most common case).
- Missing boolean — a confined daemon needs an optional behavior, such as HTTPD making outbound network connections.
- Nonstandard port — a service listening on a port its type isn't allowed to bind.
- Genuinely new permission — rare; this is the only case that justifies a custom module.
Fix the denial the right way
Mislabeled files
Suppose you moved a web app's document root to /srv/myapp and httpd gets avc: denied { read } on it. Check what the path should look like, define the mapping, and apply it:
matchpathcon /srv/myapp
semanage fcontext -a -t httpd_sys_content_t '/srv/myapp(/.*)?'
restorecon -Rv /srv/myapp
semanage fcontext -a records the rule persistently so it survives relabels; restorecon -R applies it now. matchpathcon afterwards should report the type you set.
Nonstandard ports
If you run httpd on TCP 8085 and it fails to bind, check existing assignments and add yours:
semanage port -l | grep http_port
semanage port -a -t http_port_t -p tcp 8085
If the port is already assigned to another type, use -m (modify) instead of -a — but first confirm that reassignment won't break the service that owns it.
Booleans
For behavioral toggles, find the candidate and set it persistently:
getsebool -a | grep httpd
setsebool -P httpd_can_network_connect on
The -P flag writes the change to disk; without it the boolean resets at reboot.
Custom modules as a last resort
If none of the above fits, generate a local module from the denials — but review it before loading:
ausearch -m avc -ts recent | audit2allow -M myapp_local
cat myapp_local.te # read this before installing
semodule -i myapp_local.pp
audit2allow can grant broader access than the application needs, so inspect the generated .te file and trim anything you can't justify. Treat a hand-reviewed module as a documented exception, not a routine fix.
Verify the fix holds
getenforcereportsEnforcingand the application works.ausearch -m avc -ts recentshows no new denials after exercising the app.matchpathconon fixed paths reports the intended context.- Reboot once and re-check — some denials only appear when services start at boot.
Recovery when enforcement breaks the system
If a critical service fails at boot under enforcement, interrupt the GRUB menu, edit the kernel line, and append enforcing=0 for a one-time permissive boot. To make it persistent while you repair, use grubby --update-kernel=ALL --args=enforcing=0 — and remove the argument when done.
If file labels are broadly corrupted (common after restoring from a backup made with SELinux off), force a full relabel:
touch /.autorelabel
reboot
The relabel runs during early boot and can take substantial time on large filesystems — schedule it in a maintenance window. (fixfiles relabel is the equivalent without the marker file.)
Make the decision stick
The engineering decision worth writing down: permissive mode is a diagnostic state, and disabling SELinux is an exceptional, documented choice — never the default response to a denial. Record every local change (fcontext rules, port assignments, booleans, custom modules) in your configuration management so rebuilt or additional nodes reproduce the same policy state. And before publishing any of this as team procedure, test it on the exact RHEL major release you run — boolean names, defaults, and tool packaging do shift between releases.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.