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.