Set Enforcing and
Permissive Modes for SELinux

Set enforcing and permissive modes for SELinux

CIS126RH | RHEL System Administration 1
Mesa Community College

SELinux (Security-Enhanced Linux) is a mandatory access control system built into the Linux kernel. Where standard Linux permissions ask "who owns this file?", SELinux asks "is this process allowed to perform this action on this resource according to policy?". On RHEL 9, SELinux is enabled and enforcing by default — providing a critical second layer of protection. Administrators must be able to check SELinux status, switch between modes, make mode changes persistent, and understand why keeping SELinux enforcing matters. These skills are tested on the RHCSA exam.

Learning Objectives

  1. Explain the SELinux security model — Describe how SELinux adds mandatory access control beyond standard Linux permissions, and the role of security contexts and policy
  2. Check and interpret SELinux status — Use getenforce and sestatus to determine the current mode and policy in effect
  3. Switch SELinux modes at runtime and persistently — Use setenforce to change the runtime mode and /etc/selinux/config to make the change persist across reboots
  4. Explain the three SELinux modes and when to use each — Distinguish enforcing, permissive, and disabled, and understand why permissive is preferred over disabled for troubleshooting

What is SELinux?

SELinux is a Mandatory Access Control (MAC) system. Unlike standard Linux permissions (Discretionary Access Control / DAC), SELinux policy is enforced by the kernel regardless of user or root decisions.

  • Standard permissions: owner decides who can access a file (discretionary)
  • SELinux: central policy enforced by the kernel — even root cannot bypass it
  • Every process and file has a security context (label)
  • The SELinux policy defines which process contexts can access which file contexts
  • Access is denied unless the policy explicitly allows it
SELinux operates as a second, independent access control layer

For a process to access a resource, both standard permissions AND SELinux policy must allow it. SELinux denials appear as "Permission denied" even when the standard file permissions look correct — this is the most common SELinux diagnostic scenario.

SELinux Security Contexts

Every process and file has a security context — a label that SELinux policy uses to determine whether an access is allowed.

# View security context of files (-Z flag)
$ ls -Z /var/www/html/index.html
system_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html
#   ^user  ^role     ^type              ^level

# View security context of a running process (-Z flag)
$ ps auxZ | grep httpd
system_u:system_r:httpd_t:s0  ... /usr/sbin/httpd

# The type field (third field) is what SELinux policy primarily uses
# httpd_t process CAN read httpd_sys_content_t files — policy allows it
# httpd_t process CANNOT read shadow_t files — policy denies it

# View context of the current shell process
$ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
# unconfined_t = standard users run unconfined (no SELinux restrictions)

The Three SELinux Modes

Mode Policy enforced? Denials logged? Context labels? Use
Enforcing Yes — access denied Yes Yes Normal production operation — maximum protection
Permissive No — access allowed Yes Yes Troubleshooting — log what would be denied without blocking
Disabled No — SELinux off No No Avoid — requires full relabel at next boot to re-enable
Never use disabled — use permissive instead

Switching from disabled to enforcing requires a full filesystem relabel at the next reboot, which can take many minutes and may leave files mislabelled. Permissive achieves the same diagnostic goal without this cost. The RHCSA exam expects SELinux to remain enabled (either enforcing or permissive).

Checking SELinux Status

# Quick check: current mode only
$ getenforce
Enforcing

# Full status report
$ sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux mount point:            /sys/fs/selinux
SELinuxfs mount:                /sys/fs/selinux
Current mode:                   enforcing
Mode from config file:          enforcing
Policy MLS status:              enabled
Policy deny_unknown status:     allowed
Memory protection checking:     actual (secure)
Max kernel policy version:      33
Policy type:                    targeted

# Note: "Current mode" vs "Mode from config file"
# These can differ when setenforce has been used without editing the config file
sestatus shows both runtime AND persistent mode

"Current mode" = the mode SELinux is in right now (from setenforce). "Mode from config file" = the mode that will apply after the next reboot (from /etc/selinux/config). When they differ, a reboot will change the mode.

Changing Mode at Runtime: setenforce

setenforce changes the SELinux mode immediately without rebooting. The change is temporary — lost after the next reboot.

# Switch to permissive mode (for troubleshooting)
$ sudo setenforce 0     # 0 = permissive
$ sudo setenforce Permissive   # equivalent

# Switch back to enforcing mode
$ sudo setenforce 1     # 1 = enforcing
$ sudo setenforce Enforcing    # equivalent

# Verify the change
$ getenforce
Permissive

$ sestatus
Current mode:          permissive   ← runtime mode changed
Mode from config file: enforcing   ← config still says enforcing

# Note: setenforce cannot switch to disabled or from disabled
# Disabled requires editing /etc/selinux/config and rebooting
$ sudo setenforce 0
# If already in disabled mode, setenforce gives:
setenforce: SELinux is disabled

Persistent Mode: /etc/selinux/config

To make a mode change survive reboots, edit /etc/selinux/config — the file the kernel reads at boot to determine the SELinux starting mode.

# View the current SELinux configuration file
$ cat /etc/selinux/config
# This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
#     enforcing  - SELinux security policy is enforced.
#     permissive - SELinux prints warnings instead of enforcing.
#     disabled   - No SELinux policy is loaded.
SELINUX=enforcing
# SELINUXTYPE= can take one of these three values:
#     targeted - Targeted processes are protected
#     minimum  - Modification of targeted policy.
#     mls      - Multi Level Security protection.
SELINUXTYPE=targeted

# Change to permissive (persistent across reboots)
$ sudo sed -i 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config

# Or edit with vim
$ sudo vim /etc/selinux/config
# Change: SELINUX=enforcing  →  SELINUX=permissive

# Verify the change
$ grep ^SELINUX= /etc/selinux/config
SELINUX=permissive

The Complete Mode Change Workflow

To change the SELinux mode both immediately AND persistently, two steps are always required.

Switch from Enforcing to Permissive (with persistence)

# Step 1: Change runtime mode immediately
$ sudo setenforce 0

# Step 2: Make it persistent in the config file
$ sudo sed -i 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config

# Verify both runtime and persistent modes match
$ sestatus
Current mode:          permissive
Mode from config file: permissive

Switch from Permissive back to Enforcing (with persistence)

# Step 1: Change runtime mode immediately
$ sudo setenforce 1

# Step 2: Make it persistent
$ sudo sed -i 's/^SELINUX=.*/SELINUX=enforcing/' /etc/selinux/config

# Verify
$ getenforce
Enforcing
Two-step pattern: setenforce + config file edit

The exam grader checks both the current runtime mode AND the persistent config. Always do both steps. setenforce alone is not persistent; config file alone requires a reboot.

SELinux AVC Denials: The Audit Log

When SELinux denies an action (in enforcing mode) or would deny it (in permissive mode), it logs an AVC (Access Vector Cache) denial to the audit log.

# View SELinux denials in the audit log
$ sudo grep AVC /var/log/audit/audit.log | tail -5
type=AVC msg=audit(1716638401.123:456): avc:  denied  { read } for
  pid=1234 comm="httpd" name="data.txt" dev="sda1" ino=67890
  scontext=system_u:system_r:httpd_t:s0
  tcontext=system_u:object_r:user_home_t:s0 tclass=file
# httpd_t (Apache) was denied READ access to a user_home_t file

# Use ausearch for filtered, human-readable output
$ sudo ausearch -m avc -ts recent

# Use audit2why to explain what the denial means
$ sudo ausearch -m avc -ts recent | audit2why
Was caused by:
  Missing type enforcement (TE) allow rule.
  You can use audit2allow to generate a loadable module...

Why Permissive is Better Than Disabled

When SELinux is causing problems, the temptation is to disable it. This is almost always wrong. Permissive mode achieves the same diagnostic goal safely.

Aspect Permissive Disabled
Files created while in this modeCorrectly labelled with SELinux contextsNo SELinux contexts — mislabelled
Re-enabling SELinuxRun setenforce 1 — immediate, no relabelEdit config, reboot, full filesystem relabel (can take hours)
Denial loggingYes — AVC denials logged for diagnosisNo — no way to know what policy violations exist
Security during troubleshootingStandard DAC permissions still enforceBoth MAC and some protections unavailable
Troubleshooting valueHigh — logs what would be deniedNone — hides the problem rather than revealing it
The RHCSA exam expects SELinux to stay enabled

The exam environment uses SELinux enforcing. Setting SELinux to disabled is a severe exam penalty — many tasks depend on correct SELinux configuration. Use permissive only temporarily for troubleshooting; return to enforcing when done.

Viewing Contexts and the . Indicator

The security context appears in many command outputs using the -Z flag. The . at the end of permissions in ls -l indicates an SELinux context is set.

# The dot after permissions = SELinux context is present
$ ls -l /etc/passwd
-rw-r--r--. 1 root root 2803 May 25 10:00 /etc/passwd
#          ^ dot = SELinux context assigned

# View context with -Z
$ ls -lZ /etc/passwd
-rw-r--r--. root root system_u:object_r:passwd_file_t:s0 /etc/passwd

# View context of a directory
$ ls -ldZ /var/www/html
drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 /var/www/html

# View context of processes
$ ps -eZ | grep httpd
system_u:system_r:httpd_t:s0 1234 ? 00:00:00 httpd

# A + after permissions = ACL is set (not SELinux)
-rw-r--r--+ 1 alice alice 0 May 25 10:00 acl-file

SELinux Mode Management Quick Reference

Task Command or file
Check current SELinux mode (quick)getenforce
Check full SELinux statussestatus
Switch to permissive (temporary)sudo setenforce 0
Switch to enforcing (temporary)sudo setenforce 1
Make permissive persistentSet SELINUX=permissive in /etc/selinux/config
Make enforcing persistentSet SELINUX=enforcing in /etc/selinux/config
Verify config file settinggrep ^SELINUX= /etc/selinux/config
View file SELinux contextls -Z FILE or ls -lZ FILE
View process SELinux contextps -eZ | grep PROCESS
View recent AVC denialssudo ausearch -m avc -ts recent
Explain an AVC denialsudo ausearch -m avc -ts recent | audit2why
View audit log for denialssudo grep AVC /var/log/audit/audit.log

Booting with SELinux Mode Options

The SELinux mode can also be overridden at boot time via kernel parameters — useful for recovery when the config file cannot be edited.

# Boot into permissive mode (one-time override at GRUB menu)
# At the GRUB menu, press 'e', then add to the kernel line:
enforcing=0
# This boots permissive regardless of /etc/selinux/config

# Check if SELinux was overridden at boot
$ cat /sys/fs/selinux/enforce
0   # 0=permissive, 1=enforcing

# The /etc/selinux/config SELINUX= is read at boot
# The kernel parameter enforcing=0 overrides it for one boot only

# Trigger a full filesystem relabel on next boot (after re-enabling from disabled)
$ sudo touch /.autorelabel
$ sudo reboot
# The relabel may take several minutes on large filesystems
enforcing=0 at GRUB is the recovery method

When a misconfigured SELinux policy prevents booting or login, adding enforcing=0 to the kernel line at the GRUB menu boots into permissive mode — enough to diagnose and fix the problem.

Common Mistakes

Mistake What goes wrong Correct approach
Setting SELINUX=disabled instead of permissive Files created without contexts; re-enabling requires full relabel; exam penalty Always use SELINUX=permissive for troubleshooting — never disabled
Using only setenforce without editing the config file Mode reverts to config file value after next reboot — change is not persistent Always do both: setenforce (runtime) AND edit /etc/selinux/config (persistent)
Editing only the config file without running setenforce Config change is correct but does not take effect until next reboot Run setenforce for immediate effect; edit config for persistence
Confusing setenforce 0 and 1 Sets wrong mode: 0=permissive (off), 1=enforcing (on) Mnemonic: 1=enforcing (one = on); 0=permissive (zero = off/relaxed)
Assuming SELinux is the only cause when a service fails Wastes time on SELinux when the real cause is firewall, config, or permissions Check standard permissions and firewall first; use ausearch -m avc to confirm SELinux is involved
Using setenforce when SELinux is disabled in config setenforce returns: "SELinux is disabled" — cannot change to enforcing this way Edit /etc/selinux/config to set enforcing and reboot

Practical Troubleshooting Workflow

Scenario: Apache cannot serve a page that appears to have correct permissions. Is SELinux the cause?

# Step 1: Check if SELinux is enforcing
$ getenforce
Enforcing

# Step 2: Check for recent AVC denials
$ sudo ausearch -m avc -ts recent
...denied { read } for comm="httpd" scontext=httpd_t tcontext=user_home_t...
# SELinux is blocking httpd from reading a user_home_t file

# Step 3: Temporarily switch to permissive to confirm SELinux is the cause
$ sudo setenforce 0

# Step 4: Test the service — if it now works, SELinux was the cause
$ curl http://localhost/data.html
<!DOCTYPE html>...   ← works in permissive!

# Step 5: Fix the root cause (e.g. restore correct file context)
# (covered in the SELinux contexts module)

# Step 6: Return to enforcing
$ sudo setenforce 1
$ getenforce
Enforcing

Knowledge Check

Answer these before moving to the next slide.

  1. What are the three SELinux modes, and what is the key difference between permissive and disabled?
  2. Write the command to check the current SELinux mode and the command that shows both the runtime mode AND the persistent config file mode.
  3. Write the two commands to switch SELinux to permissive mode both immediately AND persistently across reboots.
  4. After running sudo setenforce 0, sestatus shows "Current mode: permissive" but "Mode from config file: enforcing". What does this mean, and what happens after the next reboot?
  5. Why should you never set SELINUX=disabled? What should you use instead, and why is it safer?
  6. Write the command to verify the persistent SELinux mode setting in the configuration file without opening the file in an editor.

Knowledge Check — Answers

  1. The three modes are enforcing (policy enforced, denials logged), permissive (policy not enforced but denials logged, contexts maintained), and disabled (SELinux completely inactive, no contexts applied). The key difference: permissive still maintains SELinux contexts on files and logs denials, making it easy to re-enable. Disabled removes all context labels, requiring a full filesystem relabel to re-enable.
  2. Quick mode check: getenforce
    Full status (runtime + persistent): sestatus
  3. (1) sudo setenforce 0 — changes runtime mode immediately
    (2) sudo sed -i 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config — makes it persistent. Also accept: edit /etc/selinux/config with vim and change SELINUX=enforcing to SELINUX=permissive.
  4. The runtime mode is permissive (setenforce 0 was applied). The config file still says enforcing — the config file was not edited. After the next reboot, the system will read the config file and boot into enforcing mode — the setenforce change was not persistent.
  5. Setting SELINUX=disabled means files created while disabled receive no SELinux context labels. When you re-enable SELinux, a full filesystem relabel is required at the next reboot — this can take many minutes. Additionally, no AVC denials are logged while disabled, so you learn nothing about what SELinux would block. Use SELINUX=permissive instead — it keeps context labels on all files and logs denials without enforcing them.
  6. grep ^SELINUX= /etc/selinux/config
    Expected output: SELINUX=enforcing or SELINUX=permissive

Key Takeaways

  1. SELinux has three modes: enforcing, permissive, and disabled. Enforcing = policy enforced, denials blocked. Permissive = denials logged but allowed. Disabled = SELinux completely off, no contexts. Never use disabled — use permissive for troubleshooting.
  2. Check mode with getenforce; check both runtime and persistent with sestatus. sestatus shows "Current mode" (runtime) and "Mode from config file" (persistent). When they differ, a reboot will change the mode.
  3. Two steps for a complete persistent mode change. Runtime: sudo setenforce 0 (permissive) or sudo setenforce 1 (enforcing). Persistent: edit SELINUX= in /etc/selinux/config. Do both — one alone is incomplete.
  4. Permissive is for troubleshooting — always return to enforcing. In permissive mode, AVC denials are logged but not blocked — use ausearch -m avc -ts recent to identify what SELinux is denying. Fix the root cause, then setenforce 1 and update the config file to enforcing.

Graded Lab

  • Run getenforce and sestatus to record the current SELinux mode and the mode configured in the config file. Confirm they match. View the /etc/selinux/config file and identify the SELINUX= line.
  • Switch SELinux to permissive mode with sudo setenforce 0. Verify with getenforce (should show Permissive). Run sestatus and observe that "Current mode" is permissive but "Mode from config file" still shows the original value — demonstrating the change is not yet persistent.
  • Make the permissive mode persistent by editing /etc/selinux/config and setting SELINUX=permissive. Verify with grep ^SELINUX= /etc/selinux/config. Run sestatus again and confirm both "Current mode" and "Mode from config file" now show permissive.
  • Switch back to enforcing mode: sudo setenforce 1 for runtime, then edit /etc/selinux/config to set SELINUX=enforcing. Verify with getenforce and sestatus that both match enforcing.
  • Run ls -lZ /etc/passwd /var/www/html to view SELinux contexts on these files. Identify the type field (third colon-separated field) in each context. Run id -Z to see your own process context.
  • Run sudo ausearch -m avc -ts recent. If there are recent denials, pipe one through audit2why to get a human-readable explanation. If there are no denials, confirm SELinux is enforcing correctly with getenforce.
RHCSA Objective

"Set enforcing and permissive modes for SELinux." Runtime: setenforce 0|1. Persistent: edit SELINUX= in /etc/selinux/config. Verify: getenforce and sestatus. Never use disabled.