skip to content

On a RHEL host, what is the difference between SELinux running in enforcing mode, permissive mode, and being disabled, and why is `setenforce 0` not the same as setting SELINUX=disabled in /etc/selinux/config?

level: juniorimportance: must knowfreq 70%

answer

  1. three states, not two
  2. permissive still writes the log
  3. runtime toggle versus boot-time file
  4. disabled stops maintaining labels
  5. coming back costs a relabel

basics

~20 s

Enforcing blocks policy violations and logs them. Permissive blocks nothing but still logs every would-be denial. Disabled loads no policy and stops maintaining file labels. setenforce 0 switches to permissive only until reboot; /etc/selinux/config sets the mode chosen at boot.

solid answer

~40 s

SELinux has three states. **Enforcing** evaluates the policy and denies anything it does not allow, writing an AVC denial to the audit log. **Permissive** evaluates the same policy and logs the same denials but allows the operation anyway — it is a diagnostic mode, not an off switch. **Disabled** means no policy is loaded at all, so nothing is checked and, importantly, file labels stop being maintained. `setenforce 0` and `setenforce 1` only move between enforcing and permissive at runtime and are lost on reboot; they cannot bring a disabled system up into enforcing, because there is no policy loaded. The persistent setting lives in `/etc/selinux/config` (`SELINUX=` plus `SELINUXTYPE=targeted`), and `getenforce` or `sestatus` shows where you actually are. Coming back from disabled normally needs a full relabel, typically by touching `/.autorelabel` and rebooting.

code

bash · 14 lines
bash
# where am I right now?
getenforce
sestatus

# temporary, until reboot: enforcing <-> permissive
setenforce 0
setenforce 1

# persistent boot-time mode
grep -E '^SELINUX(TYPE)?=' /etc/selinux/config

# debug one service instead of the whole machine
semanage permissive -a httpd_t
semanage permissive -d httpd_t

go deeper

for a junior

Be ready to name the three states and say plainly that permissive logs denials but never blocks. Know that getenforce prints the current mode and that setenforce does not survive a reboot.

for a middle

Explain that the runtime toggle only moves between enforcing and permissive, that the boot-time mode comes from /etc/selinux/config, and that a disabled system stops maintaining file labels.

for a senior

Show that you use permissive as a bounded diagnostic stage to collect the complete denial set before returning to enforcing, and that you prefer a per-domain permissive over making the whole machine permissive.

for a principal

Argue the fleet-wide position: what containment mandatory access control actually buys against the cost of maintaining policy exceptions, who owns those exceptions, and how they ship through configuration management rather than by hand.

## What SELinux is, in one paragraph SELinux is a Linux Security Module: kernel code that sits on the syscall paths and answers a second question after the classic owner/group/mode check has already said yes. That second question is asked against a loaded *policy* — a compiled ruleset that says which process domains may perform which operations on which object types. Because the policy is loaded into the kernel, its state is a property of the running system, and that state is exactly what the three modes describe. ## The three states **Enforcing.** The policy is loaded and consulted, and any access it does not allow is refused. The calling program receives an ordinary error — usually `EACCES` — with no hint that SELinux was involved, and the kernel writes an AVC (Access Vector Cache) denial record to the audit log. This is the only mode that provides containment. **Permissive.** The policy is loaded and evaluated, denials are logged in exactly the same way, but the operation proceeds. Nothing is protected. Its value is diagnostic and it is more useful than it first looks: in enforcing mode a program often dies at the *first* denial, so you only ever see one. In permissive mode the program runs to completion and the log accumulates the *whole* set of accesses the policy is missing, which is what you need before you write a fix. **Disabled.** No policy is loaded. No checks happen — and, the part people forget, the labelling machinery is not running either. Newly created files get no SELinux label, and files whose labels would normally have been maintained drift out of date. That is why disabling is not a free, reversible experiment. ## Runtime toggle versus boot-time configuration `setenforce 0` (permissive) and `setenforce 1` (enforcing) flip the running kernel between the two *loaded-policy* states. They are runtime-only, they vanish on reboot, and they cannot rescue a disabled system: with no policy loaded there is nothing to enforce. The boot-time state comes from `/etc/selinux/config`: ``` SELINUX=enforcing SELINUXTYPE=targeted ``` `SELINUX=` takes `enforcing`, `permissive` or `disabled`; `SELINUXTYPE=` names the policy to load, and `targeted` is the default on RHEL and Fedora — it confines listed system services and leaves ordinary user sessions in a largely unrestricted domain. The kernel command line can also override this: `enforcing=0` boots permissive, and `selinux=0` turns the subsystem off entirely. On RHEL 9, turning SELinux off through the config file is deprecated and the supported way to disable it is the `selinux=0` kernel parameter. Inspect the result with `getenforce` (prints one word) or `sestatus` (prints current mode, mode from the config file, and the loaded policy). ## Coming back from disabled Because labels were not maintained while the policy was unloaded, re-enabling straight into enforcing usually produces a flood of denials from mislabelled files. The standard procedure is to set the config back to `enforcing` (or `permissive` for a first pass), create the relabel flag and reboot: ```bash touch /.autorelabel reboot ``` The init system sees the flag, relabels every filesystem against the policy's default-context database, removes the flag and reboots again. On a large filesystem this takes real time, which is the practical argument against ever disabling SELinux on a machine you intend to keep. ## Using permissive without going global Whole-machine permissive removes protection from every confined service just to debug one. SELinux supports *permissive domains* instead: `semanage permissive -a httpd_t` makes that single domain behave permissively while everything else stays enforcing, and `semanage permissive -d httpd_t` reverses it. That is the professional version of `setenforce 0`. ## The mistakes that show up in interviews The first is treating permissive as "off" — it is not, and a system left permissive gives you audit noise with no containment. The second is answering a denial with `setenforce 0` and calling it fixed; that is disabling the alarm, and it will come back at the next reboot or, worse, silently stay off. The third is assuming disabling and re-enabling is symmetric, when the asymmetry is the relabel. The fourth is forgetting that a denial surfaces to the application as a generic permission error, so the application's own log rarely says SELinux anywhere — you have to go to the audit log to see it.

  • Why does a service often show only one SELinux denial in enforcing mode but many in permissive mode?
    In enforcing mode the first refused access usually makes the program fail and exit, so the policy never gets asked about the accesses that would have come later. In permissive mode the operation is allowed, the program keeps running, and every subsequent violation is logged too. That is why you collect the denial set in permissive before writing a fix and then switch back.
  • A machine has been running with SELinux disabled for a year and you want it enforcing again. What is your sequence?
    Set `SELINUX=permissive` in `/etc/selinux/config` first, `touch /.autorelabel`, and reboot so every filesystem is relabelled from the policy's default contexts. Run the workload, collect the denials that remain, fix labels or policy, then move to `SELINUX=enforcing` and reboot again. Going straight to enforcing after a relabel risks an outage from whatever the relabel did not cover.
  • Where do you actually see that SELinux caused a failure, given the application only reports a permission error?
    In the audit log — `/var/log/audit/audit.log` on a system running auditd — as records of type AVC containing `avc: denied`, the source and target contexts, the object class and the permission. If auditd is not running, the messages fall back to the kernel ring buffer and the journal. The application itself sees a plain `EACCES` and normally says nothing about SELinux.

Enforcing is a locked door; permissive is an unlocked door with a camera recording everyone who walks through; disabled is no door and no camera, and putting the door back means re-checking every room.

saying these in an interview costs you the question

  • Permissive mode means SELinux is turned off
  • setenforce 0 disables SELinux permanently
  • You can go from disabled to enforcing with setenforce
  • Disabling SELinux is the normal fix for a denial
  • Nothing is logged while running in permissive mode

context