RED HAT ENTERPRISE LINUX

SELinux Security

Protecting Systems with Mandatory Access Control

College-Level Course Module | RHEL System Administration

Learning Objectives

1
Understand SELinux concepts and modes

Enforcing, permissive, and disabled modes; MAC vs DAC

2
Manage SELinux file contexts

View, change, and restore security contexts on files

3
Configure SELinux booleans

Toggle policy behaviors without modifying the policy itself

4
Troubleshoot SELinux denials

Analyze audit logs and resolve access violations

What is SELinux?

SELinux (Security-Enhanced Linux) is a mandatory access control (MAC) system built into the Linux kernel that enforces security policies controlling what processes can access.

Mandatory Access Control

System-enforced rules that even root cannot override. Policy decides all access.

Discretionary Access Control

Traditional Unix permissions. Owners control access. Root bypasses everything.

Defense in Depth

SELinux adds a layer. If an attacker compromises a service, SELinux limits damage.

Principle of Least Privilege

Processes get only the access they need. Nothing more.

Real Example: A compromised web server can read web content, but SELinux prevents it from reading /etc/shadow - even though the process runs as root.

SELinux Modes

Enforcing

Policy is enforced. Violations are blocked and logged. This is the production setting.

Permissive

Policy is not enforced but violations are logged. Useful for troubleshooting.

Disabled

SELinux is completely off. No protection, no logging. Avoid this mode!

# Check current SELinux status
[root@server ~]# getenforce
Enforcing

# Detailed status information
[root@server ~]# sestatus
SELinux status:                 enabled
SELinuxfs mount:                /sys/fs/selinux
SELinux root directory:         /etc/selinux
Loaded policy name:             targeted
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

Changing SELinux Mode

# Temporarily change mode (until reboot)
[root@server ~]# setenforce 0           # Set to Permissive
[root@server ~]# getenforce
Permissive

[root@server ~]# setenforce 1           # Set to Enforcing
[root@server ~]# getenforce
Enforcing

# Permanent change: edit configuration file
[root@server ~]# vim /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 values:
#     targeted - Targeted processes are protected.
#     minimum - Modification of targeted policy.
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted
⚠ Warning: Changing from Disabled to Enforcing requires a reboot and full filesystem relabel (can take a long time). Going to Disabled loses all file contexts.

SELinux Contexts

Every process, file, and resource has an SELinux context (also called a label). The policy uses contexts to determine what access is allowed.

user:role:type:level
system_u:object_r:httpd_sys_content_t:s0

User: SELinux user (not Linux user)
system_u, unconfined_u

Role: SELinux role
object_r for files, system_r for processes

Type: The most important part!
Determines what access is allowed

Level: MLS/MCS security level
Usually s0, used for advanced scenarios

Viewing Contexts

# View file contexts with ls -Z
[root@server ~]# ls -Z /var/www/html/
system_u:object_r:httpd_sys_content_t:s0 index.html

# View contexts on /etc files
[root@server ~]# ls -Z /etc/passwd /etc/shadow
system_u:object_r:passwd_file_t:s0 /etc/passwd
system_u:object_r:shadow_t:s0     /etc/shadow

# View process contexts with ps -Z
[root@server ~]# ps -eZ | grep httpd
system_u:system_r:httpd_t:s0          1234 ?    00:00:01 httpd

# View your current context
[root@server ~]# id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

# View context of a directory and contents
[root@server ~]# ls -laZ /var/www/
Key Point: The -Z option works with ls, ps, id, cp, and other commands to show SELinux contexts.

Changing File Contexts

# Change context temporarily with chcon
[root@server ~]# chcon -t httpd_sys_content_t /var/www/html/newfile.html

# Change context recursively
[root@server ~]# chcon -R -t httpd_sys_content_t /var/www/html/newdir/

# Copy context from another file
[root@server ~]# chcon --reference=/var/www/html/index.html /var/www/html/newfile.html

# Verify the change
[root@server ~]# ls -Z /var/www/html/newfile.html
system_u:object_r:httpd_sys_content_t:s0 newfile.html
⚠ Important: chcon changes are temporary! They will be lost if you run restorecon or during a filesystem relabel. Use semanage fcontext for permanent changes.
# chcon is useful for quick testing, but not persistent
[root@server ~]# restorecon /var/www/html/newfile.html   # Reverts to default!

Permanent Context Changes

# Define a permanent context rule
[root@server ~]# semanage fcontext -a -t httpd_sys_content_t "/srv/www(/.*)?"

# Apply the rule to existing files
[root@server ~]# restorecon -Rv /srv/www
Relabeled /srv/www from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0
Relabeled /srv/www/index.html from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0

# View defined context rules
[root@server ~]# semanage fcontext -l | grep /srv/www
/srv/www(/.*)?      all files    system_u:object_r:httpd_sys_content_t:s0

# Delete a custom context rule
[root@server ~]# semanage fcontext -d "/srv/www(/.*)?"

# List only locally added rules (not policy defaults)
[root@server ~]# semanage fcontext -l -C
Two-Step Process: 1) semanage fcontext defines the rule, 2) restorecon applies it to files.

Restoring Default Contexts

# Restore context on a single file
[root@server ~]# restorecon -v /var/www/html/index.html
Relabeled /var/www/html/index.html from unconfined_u:object_r:default_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0

# Restore contexts recursively
[root@server ~]# restorecon -Rv /var/www/

# Preview what would change (dry run)
[root@server ~]# restorecon -Rvn /var/www/

# Force relabel even if context seems correct
[root@server ~]# restorecon -RFv /var/www/

# Relabel entire filesystem (creates /.autorelabel and reboots)
[root@server ~]# touch /.autorelabel
[root@server ~]# reboot

# Or use fixfiles (more options)
[root@server ~]# fixfiles -F onboot
[root@server ~]# reboot
Common Fix: Files copied or moved often have wrong contexts. restorecon is frequently the solution to SELinux denials.

SELinux Booleans

Booleans are switches that modify SELinux policy behavior. They enable or disable specific policy rules without editing the policy itself.

# List all booleans
[root@server ~]# getsebool -a
abrt_anon_write --> off
allow_console_login --> on
allow_cvs_read_shadow --> off
...
httpd_can_network_connect --> off
httpd_can_network_connect_db --> off
httpd_enable_homedirs --> off
...

# List booleans with descriptions
[root@server ~]# semanage boolean -l | grep httpd
httpd_can_network_connect      (off  ,  off)  Allow httpd to connect to network
httpd_can_network_connect_db   (off  ,  off)  Allow httpd to connect to databases
httpd_enable_homedirs          (off  ,  off)  Allow httpd to read user home directories

# Check a specific boolean
[root@server ~]# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> off

Setting Booleans

# Set boolean temporarily (until reboot)
[root@server ~]# setsebool httpd_enable_homedirs on
[root@server ~]# getsebool httpd_enable_homedirs
httpd_enable_homedirs --> on

# Set boolean permanently (survives reboot)
[root@server ~]# setsebool -P httpd_enable_homedirs on

# Alternative: use semanage
[root@server ~]# semanage boolean -m --on httpd_can_network_connect

# Set multiple booleans at once
[root@server ~]# setsebool -P httpd_enable_homedirs=on httpd_can_network_connect=on

# View locally modified booleans
[root@server ~]# semanage boolean -l -C
SELinux boolean                State  Default Description
httpd_enable_homedirs          (on   ,   off)  Allow httpd to read user home dirs
⚠ Remember: Without -P, boolean changes are temporary and lost on reboot!

SELinux Port Contexts

# List all port contexts
[root@server ~]# semanage port -l | head
SELinux Port Type              Proto    Port Number
afs3_callback_port_t           tcp      7001
afs3_callback_port_t           udp      7001
...
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000

# Find which type a port uses
[root@server ~]# semanage port -l | grep 8080
http_cache_port_t              tcp      8080, 8118, ...

# Add a non-standard port for httpd
[root@server ~]# semanage port -a -t http_port_t -p tcp 8888

# Verify the addition
[root@server ~]# semanage port -l | grep 8888
http_port_t                    tcp      8888, 80, 81, 443, ...

# Delete a custom port assignment
[root@server ~]# semanage port -d -t http_port_t -p tcp 8888

# Modify existing (if port already assigned to different type)
[root@server ~]# semanage port -m -t http_port_t -p tcp 8080

Troubleshooting with Audit Logs

# View SELinux denials in audit log
[root@server ~]# grep "denied" /var/log/audit/audit.log
type=AVC msg=audit(1705774800.123:456): avc:  denied  { read } for  pid=1234 comm="httpd" name="index.html" dev="sda1" ino=12345 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0

# Use ausearch for better formatting
[root@server ~]# ausearch -m AVC -ts recent
----
time->Sat Jan 20 14:00:00 2024
type=AVC msg=audit(1705774800.123:456): avc:  denied  { read } for  pid=1234 
  comm="httpd" name="index.html" 
  scontext=system_u:system_r:httpd_t:s0 
  tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file

# Search for denials from specific source
[root@server ~]# ausearch -m AVC -c httpd

# Today's denials
[root@server ~]# ausearch -m AVC -ts today
Reading the denial: httpd_t (process) tried to read a file with user_home_t context. The policy doesn't allow httpd_t to read user_home_t files.

Using sealert

# Install setroubleshoot if not present
[root@server ~]# dnf install setroubleshoot-server

# Analyze audit log for SELinux issues
[root@server ~]# sealert -a /var/log/audit/audit.log
SELinux is preventing httpd from read access on the file index.html.

*****  Plugin catchall_labels (83.8 confidence) suggests   *******************

If you want to allow httpd to have read access on the index.html file
Then you need to change the label on index.html
Do
# semanage fcontext -a -t httpd_sys_content_t '/srv/www/index.html'
# restorecon -v '/srv/www/index.html'

*****  Plugin catchall (17.1 confidence) suggests   **************************

If you believe that httpd should be allowed read access on the index.html
Then you should report this as a bug.
You can generate a local policy module to allow this access.
Do
# ausearch -c 'httpd' --raw | audit2allow -M my-httpd
# semodule -i my-httpd.pp
sealert is amazing! It analyzes denials and suggests specific commands to fix them.

audit2allow and Custom Policy

# See what policy would allow recent denials
[root@server ~]# ausearch -m AVC -ts recent | audit2allow
#============= httpd_t ==============
allow httpd_t user_home_t:file read;

# Generate a loadable policy module
[root@server ~]# ausearch -m AVC -ts recent | audit2allow -M myapp
******************** IMPORTANT ***********************
To make this policy package active, execute:

semodule -i myapp.pp

# Load the custom policy module
[root@server ~]# semodule -i myapp.pp

# List loaded policy modules
[root@server ~]# semodule -l | grep myapp
myapp

# Remove custom module if needed
[root@server ~]# semodule -r myapp
⚠ Warning: Custom policy is a last resort! First try: fix file contexts, enable booleans, or add port labels. Custom policy can create security holes.

Common Scenarios

Web Content in Non-Standard Location

Problem: Apache can't read /srv/www
Fix: semanage fcontext + restorecon

Apache Connecting to Backend

Problem: Apache can't reach app server
Fix: setsebool -P httpd_can_network_connect on

Service on Non-Standard Port

Problem: Service won't bind to port 8888
Fix: semanage port -a -t type -p tcp 8888

Samba Sharing Home Dirs

Problem: Can't access home directories
Fix: setsebool -P samba_enable_home_dirs on

Files Copied Have Wrong Context

Problem: Copied files inaccessible
Fix: restorecon -Rv /path

Apache Running CGI Scripts

Problem: CGI scripts don't execute
Fix: setsebool -P httpd_enable_cgi on + correct context

# Quick diagnosis workflow
[root@server ~]# ausearch -m AVC -ts recent              # Find denials
[root@server ~]# sealert -a /var/log/audit/audit.log    # Get suggestions
[root@server ~]# ls -Z /path/to/file                    # Check contexts
[root@server ~]# semanage boolean -l | grep service     # Find booleans

Troubleshooting Workflow

1
Verify SELinux is the cause

setenforce 0 - if it works in permissive, SELinux is blocking it

2
Find the denial

ausearch -m AVC -ts recent or check /var/log/audit/audit.log

3
Analyze with sealert

sealert -a /var/log/audit/audit.log for suggestions

4
Apply the appropriate fix

Fix context, enable boolean, or add port label (rarely: custom policy)

5
Return to enforcing and test

setenforce 1 - verify the fix works with SELinux enforcing

# Complete troubleshooting example
[root@server ~]# setenforce 0           # Test without SELinux
[root@server ~]# curl localhost         # Works! SELinux was blocking
[root@server ~]# setenforce 1           # Re-enable
[root@server ~]# ausearch -m AVC -c httpd -ts recent   # Find denial
[root@server ~]# restorecon -Rv /var/www/html           # Fix context
[root@server ~]# curl localhost         # Test again - works!

SELinux for Containers

SELinux provides additional isolation for containers, preventing container escapes and limiting the impact of compromised containers.

# Containers run with container_t or similar domains
[root@server ~]# ps -eZ | grep container
system_u:system_r:container_t:s0:c123,c456  12345 ?  00:00:01 nginx

# Each container gets unique MCS categories (c123,c456)
# This prevents containers from accessing each other's data

# View volume contexts
[root@server ~]# ls -Z /var/lib/containers/storage/
system_u:object_r:container_file_t:s0 ...

# When mounting host directories, label appropriately
[root@server ~]# podman run -v /data:/data:Z myimage   # :Z relabels

# Or set context manually
[root@server ~]# semanage fcontext -a -t container_file_t "/data(/.*)?"
[root@server ~]# restorecon -Rv /data
:Z option: Automatically relabels mounted volumes with the container's context. Use for single-container volumes.

Best Practices

✓ Do

  • Keep SELinux in enforcing mode
  • Use permissive only for troubleshooting
  • Fix contexts before trying booleans
  • Use semanage for permanent changes
  • Run restorecon after copying files
  • Check audit logs when things don't work
  • Use sealert for guidance
  • Document your customizations

✗ Don't

  • Disable SELinux to "fix" problems
  • Run audit2allow blindly
  • Use chcon for permanent changes
  • Ignore denials in permissive mode
  • Enable booleans without understanding them
  • Create overly permissive custom policy
  • Forget to test in enforcing mode
  • Assume SELinux is the problem without checking
Remember: SELinux is security infrastructure. Disabling it removes a critical defense layer. Learn to work with it, not against it.

Key Takeaways

1

Modes: getenforce/setenforce for status. Enforcing = security on. Edit /etc/selinux/config for permanent.

2

Contexts: ls -Z, ps -Z to view. semanage fcontext + restorecon for permanent changes. chcon only for testing.

3

Booleans: getsebool -a to list. setsebool -P for permanent changes. Easy policy adjustments.

4

Troubleshooting: ausearch -m AVC for denials. sealert for analysis. Fix contexts → booleans → ports → custom policy.

LAB EXERCISES

  • Check SELinux mode with getenforce and sestatus
  • View file contexts in /var/www/html with ls -Z
  • Create files in a new directory and set proper web content context
  • Find and enable the httpd_enable_homedirs boolean
  • Intentionally cause an SELinux denial and analyze with sealert
  • Add a non-standard port for httpd with semanage port

Next: Managing Basic Storage