skip to content

questions

6

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

open as a page

A daemon on a RHEL host is refused access to a file that it owns and that is mode 0644, and `chmod 777` on the file changes nothing. Which access-control layer is denying it, and how does a process's domain and a file's type produce that decision?

level: middleimportance: must knowfreq 62%

basics

~20 s

SELinux is denying it. Traditional owner/mode checks run first and already passed, so loosening them cannot help. SELinux then compares the process's domain label with the file's type label against the loaded policy, and refuses because no rule allows that pair.

open as a page

Ubuntu ships AppArmor and RHEL ships SELinux as their mandatory access control system. What is the fundamental difference in how each one identifies the things it is protecting, and what practical consequences does that difference have?

level: middleimportance: should knowfreq 48%

basics

~20 s

AppArmor mediates by pathname: profiles attach to an executable and list the paths it may touch. SELinux mediates by label: every process and object carries a type stored on the inode, and policy is written over those types. Path-based rules are far easier to read; label-based rules follow the object wherever it goes.

open as a page

You clear an SELinux denial by running `chcon -t httpd_sys_content_t` on a data directory and the service starts working; weeks later, after the machine is relabelled, it breaks again. Where do SELinux file labels live, and what should you have done instead?

level: middleimportance: should knowfreq 52%

basics

~20 s

A label lives in the file's security.selinux extended attribute, and chcon writes it directly. A relabel replays the policy's default-context database, which still says the old type, so it overwrites your change. Record the intent with semanage fcontext, then apply it with restorecon.

open as a page

A service on a RHEL host is failing and the audit log shows an SELinux AVC denial. Walk through how you decide between correcting a label, flipping a policy boolean, and generating a custom policy module — and say what it could mean when the denial you expect never appears in the log at all.

level: seniorimportance: should knowfreq 50%

basics

~20 s

Read the denial's source context, target context, object class and permission. A wrong target label means relabel it. A supported optional behaviour means set the boolean that enables it. Only a genuinely new access justifies a reviewed custom module. Missing denials usually mean a dontaudit rule is suppressing them.

open as a page

On a RHEL host with SELinux enforcing, a daemon that starts fine on its default port fails to bind after you reconfigure it to listen on port 8081. Why would SELinux be involved in a port bind at all, and what is the correct fix?

level: middleimportance: nice to knowfreq 40%

basics

~20 s

SELinux labels TCP and UDP port numbers with types, just as it labels files, and policy only lets a domain bind ports of the types it is allowed. Port 8081 carries a different type than the daemon's default port, so the bind is refused until you add the label with semanage port.

open as a page