Security-Enhanced Linux (SELinux) is a mandatory access control (MAC) framework built into the Linux kernel. Developed by the NSA and integrated into RHEL, CentOS, Fedora, and many other distributions, it enforces security policies that go far beyond traditional Unix permissions.
Where standard Unix permissions ask "Who owns this file and what group is it in?", SELinux asks "Is this process type allowed to access this file type, in this way, under this policy?" Even root can be denied access by SELinux policy.
Policy is active and enforced. Violations are blocked and logged. This is the correct production setting.
Policy is active but NOT enforced. Violations are logged only — nothing is blocked. Used for troubleshooting and policy development.
SELinux is completely off. No logging, no enforcement. Requires reboot to re-enable. Avoid in production.
The first commands to know — find out what mode SELinux is running in:
# Quick one-word answer: Enforcing, Permissive, or Disabled getenforce # Full status report sestatus # Verbose sestatus sestatus -v # Check from a script — exit code 0 = enforcing if [ "$(getenforce)" = "Enforcing" ]; then echo "SELinux is enforcing" figetenforce output:
sestatus shows both the
current mode (runtime) and the mode from config file (persistent).
If they differ, a setenforce was used to temporarily change the
runtime mode without editing the config file.
setenforce changes the runtime mode — takes effect immediately,
does NOT survive reboot:
# Switch to Permissive (for troubleshooting — does NOT disable SELinux) sudo setenforce 0 sudo setenforce Permissive # same thing # Switch back to Enforcing sudo setenforce 1 sudo setenforce Enforcing # same thing # Verify getenforce
setenforce 0 sets
Permissive mode — SELinux is still active, still labeling files,
still logging denials, just not blocking anything. This is the safe way
to temporarily relax SELinux for troubleshooting. You cannot use
setenforce to disable SELinux — that requires editing the
config file and rebooting.
setenforce 0 and test.
If it works in Permissive, SELinux policy is the cause. Check
/var/log/audit/audit.log for the AVC denials, then fix the
context or boolean rather than leaving it in Permissive.
To make a mode change survive reboots, edit the SELinux config file:
# View current persistent config
cat /etc/selinux/config
Output:
# Change mode persistently with sed (no editor needed) sudo sed -i 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config # Or use the dedicated tool (RHEL 8+ / Fedora) sudo semanage --help # semanage handles many SELinux settings # Verify the change grep ^SELINUX= /etc/selinux/config
SELINUX=disabled back to enforcing or
permissive, the filesystem must be relabeled on next boot.
Create the trigger file before rebooting:
sudo touch /.autorelabelThe relabel runs automatically on next boot and may take several minutes on large filesystems.
The SELINUXTYPE setting in the config file selects the policy:
# Check current policy type sestatus | grep "Loaded policy" # List installed policies ls /etc/selinux/Output:
| Policy | Description | Use Case |
|---|---|---|
| targeted | Only specific "targeted" daemons are confined by SELinux policy. Most processes run unconfined. | Default on RHEL/CentOS. Best for most production servers. |
| mls | Multi-Level Security — full Bell-LaPadula model with sensitivity labels. Every process and file has a sensitivity level. | High-security environments — government, classified systems. Very complex to manage. |
| minimum | Minimal subset of targeted policy. Only the most critical processes are confined. | Resource-constrained systems. Rarely used in practice. |
targeted is the
right choice. It protects the daemons most likely to be attacked
(httpd, sshd, named, etc.) without confining every process on the system.
When SELinux blocks something, it logs an AVC (Access Vector Cache) denial. Knowing where to look is fundamental:
# View recent AVC denials in the audit log sudo ausearch -m avc -ts recent # View all denials since last boot sudo ausearch -m avc -ts boot # Using journalctl sudo journalctl -t setroubleshoot # Quick scan of audit log sudo grep "denied" /var/log/audit/audit.log | tail -20 # sealert gives human-readable explanations (setroubleshoot-server pkg) sudo sealert -a /var/log/audit/audit.logTypical AVC denial in audit.log:
comm="httpd" — the process that was denied{ read } — the action that was deniedname="myapp.conf" — the object being accessedscontext=...httpd_t... — SELinux type of the processtcontext=...admin_home_t... — SELinux type of the filehttpd_t is not allowed to read files labeled admin_home_t
— fix by relabeling the file to httpd_sys_content_t.
Rather than setting the entire system to Permissive, you can set a single SELinux domain to Permissive while keeping everything else Enforcing:
# Set a single domain permissive (httpd_t in this example) sudo semanage permissive -a httpd_t # List all permissive domains sudo semanage permissive -l # Remove permissive from a domain (back to enforcing) sudo semanage permissive -d httpd_tsemanage permissive -l output:
Many SELinux tools are not installed by default — get them first:
# RHEL 8/9 / CentOS Stream / Rocky / AlmaLinux sudo dnf install -y \ policycoreutils \ policycoreutils-python-utils \ setools-console \ setroubleshoot-server \ selinux-policy-devel # RHEL 7 / CentOS 7 sudo yum install -y \ policycoreutils \ policycoreutils-python \ setools-console \ setroubleshoot-server # Ubuntu / Debian (SELinux not default — AppArmor is) sudo apt install -y \ selinux-basics \ selinux-policy-default \ auditd
| Package | Provides |
|---|---|
| policycoreutils | Core tools: getenforce, setenforce, restorecon, sestatus |
| policycoreutils-python-utils | semanage — manage contexts, booleans, ports, users |
| setools-console | sesearch, seinfo — query and analyze policy |
| setroubleshoot-server | sealert — human-readable denial explanations |
| selinux-policy-devel | Tools for writing custom policy modules |
| auditd | Audit daemon — writes AVC denials to audit.log |
# What mode am I in? getenforce # Full status sestatus # What policy is loaded? sestatus | grep policy # Any recent denials? sudo ausearch -m avc -ts recent 2>/dev/null | tail -20 # Count denials in audit log sudo grep -c "denied" /var/log/audit/audit.log # Is auditd running? (needed for AVC logging) systemctl is-active auditd # What domains are currently permissive? sudo semanage permissive -l # Show SELinux filesystem mount ls /sys/fs/selinux
| Command | What it does |
|---|---|
| getenforce | Print current mode: Enforcing, Permissive, or Disabled |
| sestatus | Full SELinux status report |
| sestatus -v | Verbose status including file context checks |
| setenforce 1 | Set Enforcing mode (runtime only) |
| setenforce 0 | Set Permissive mode (runtime only) |
| cat /etc/selinux/config | View persistent mode and policy settings |
| semanage permissive -a httpd_t | Set one domain permissive, rest enforcing |
| semanage permissive -l | List permissive domains |
| ausearch -m avc -ts recent | Show recent AVC denials |
| grep denied /var/log/audit/audit.log | Raw audit log denial search |
| sealert -a /var/log/audit/audit.log | Human-readable denial analysis |
| touch /.autorelabel | Trigger filesystem relabel on next boot |
| Path | Purpose |
|---|---|
| /etc/selinux/config | Persistent mode and policy configuration |
| /etc/selinux/targeted/ | Targeted policy files |
| /sys/fs/selinux/ | SELinux filesystem — runtime interface to kernel |
| /var/log/audit/audit.log | AVC denials and audit events |
| /var/log/messages | setroubleshoot summaries (human readable) |
| /.autorelabel | Presence triggers full filesystem relabel on boot |