Chapter 5 ⏱️ 60 min read 📚 Advanced

Manage SELinux Security

Master Security-Enhanced Linux (SELinux), a mandatory access control (MAC) security mechanism. Learn to configure SELinux modes, manage contexts, work with booleans, and troubleshoot access denials.

🛡️ Introduction to SELinux

SELinux (Security-Enhanced Linux) is a mandatory access control (MAC) security system developed by the NSA. Unlike traditional discretionary access control (DAC) based on file permissions, SELinux enforces security policies that cannot be overridden by users, even root.

Why SELinux?

  • Defense in Depth: Additional security layer beyond traditional permissions
  • Least Privilege: Processes run with minimum necessary permissions
  • Containment: Compromised processes are restricted to their security context
  • Compliance: Required for many security standards (PCI-DSS, HIPAA, etc.)

SELinux Architecture

SELinux operates on the principle of labeling everything:

  • Subjects: Processes (labeled with security contexts)
  • Objects: Files, directories, ports, devices (also labeled)
  • Policy: Rules defining which subjects can access which objects
📘 SELinux vs Traditional Permissions

Traditional: If you're the owner or root, you can access the file
SELinux: Even root must follow the SELinux policy rules

Key Concepts

Term Description
Context Security label on files/processes (user:role:type:level)
Type Most important part of context; defines what can access what
Boolean On/off switch to enable/disable specific policy features
Policy Set of rules (RHEL uses 'targeted' policy by default)

⚙️ SELinux Modes

SELinux can operate in three modes:

Mode Behavior Use Case
Enforcing SELinux enforces policy; denials are logged and blocked Production systems (default)
Permissive SELinux logs denials but allows access Troubleshooting, testing policies
Disabled SELinux is turned off Not recommended (requires reboot)

Checking SELinux Status

# Check current SELinux status
getenforce
Enforcing

# Detailed status information
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 Modes

Temporary Change (Runtime - Lost on Reboot)

# Switch to permissive mode (temporary)
sudo setenforce 0
# or
sudo setenforce Permissive

# Switch to enforcing mode (temporary)
sudo setenforce 1
# or
sudo setenforce Enforcing

# Verify the change
getenforce

Permanent Change (Survives Reboot)

# Edit SELinux configuration file
sudo vi /etc/selinux/config

# Set SELINUX variable:
SELINUX=enforcing    # or permissive or disabled

# Example config file:
# 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. Only selected processes are protected.
#     mls - Multi Level Security protection.
SELINUXTYPE=targeted

# Reboot to apply changes (required for disabled mode)
sudo reboot
⚠️ Warning: Changing from Disabled to Enforcing

When enabling SELinux on a system where it was disabled, all files must be relabeled. This happens automatically on reboot but can take significant time:

# Force relabeling on next boot
sudo touch /.autorelabel
sudo reboot

# System will relabel all files on boot (may take 5-30 minutes)

Per-Domain Permissive Mode

You can set specific domains to permissive while keeping the system in enforcing mode:

# Make httpd_t domain permissive (troubleshooting web server)
sudo semanage permissive -a httpd_t

# List permissive domains
sudo semanage permissive -l

# Remove permissive setting
sudo semanage permissive -d httpd_t

🏷️ SELinux Contexts

Every file, process, and port has an SELinux context (label). The context format is:
user:role:type:level

Context Components

Component Description Example
User SELinux user (not Linux user) system_u, unconfined_u
Role Role-based access control object_r, system_r
Type Most important; used in policy rules httpd_sys_content_t, user_home_t
Level MLS/MCS security level s0, s0-s0:c0.c1023

Viewing Contexts

# View file contexts with ls -Z
ls -Z /var/www/html/
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 index.html

# View process contexts with ps -Z
ps -eZ | grep httpd
system_u:system_r:httpd_t:s0    1234 ?        00:00:01 httpd

# View current process context
id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

# View port contexts
sudo semanage port -l | grep http
http_cache_port_t              tcp      8080, 8118, 8123, 10001-10010
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000

Changing File Contexts

Temporary Context Change (chcon)

Use chcon for temporary changes that will be reset by relabeling:

# Change type only
sudo chcon -t httpd_sys_content_t /var/www/html/index.html

# Change entire context
sudo chcon -u system_u -r object_r -t httpd_sys_content_t /var/www/html/index.html

# Copy context from reference file
sudo chcon --reference=/var/www/html/template.html /var/www/html/newfile.html

# Recursive change
sudo chcon -R -t httpd_sys_content_t /var/www/html/
⚠️ chcon vs semanage fcontext

chcon: Temporary change, lost when filesystem is relabeled
semanage fcontext: Permanent change, survives relabeling (preferred)

Permanent Context Change (semanage fcontext)

Use semanage fcontext for permanent changes:

# Add file context rule
sudo semanage fcontext -a -t httpd_sys_content_t "/web/html(/.*)?"

# Apply the new context (restorecon)
sudo restorecon -Rv /web/html/

# Verify the context
ls -Zd /web/html/

# List all custom file context rules
sudo semanage fcontext -l -C

# Modify existing rule
sudo semanage fcontext -m -t httpd_sys_rw_content_t "/web/uploads(/.*)?"

# Delete custom rule
sudo semanage fcontext -d "/web/html(/.*)?"

Restoring Default Contexts

# Restore context for single file
sudo restorecon -v /var/www/html/index.html

# Restore context recursively
sudo restorecon -Rv /var/www/html/

# Preview what would be changed (no actual changes)
sudo restorecon -Rvn /var/www/html/

# Restore context for entire filesystem (useful after enabling SELinux)
sudo restorecon -Rv /

# Force reset to default (ignore current context)
sudo restorecon -Fv /path/to/file

Procedure: Setting Up Custom Web Directory

  1. Create custom web directory:
    sudo mkdir -p /custom/website
  2. Create test file:
    sudo echo "Hello from custom location" > /custom/website/index.html
  3. Check current context (wrong):
    ls -Zd /custom/website
    drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 /custom/website
  4. Add permanent context rule:
    sudo semanage fcontext -a -t httpd_sys_content_t "/custom/website(/.*)?"
  5. Apply the context:
    sudo restorecon -Rv /custom/website
  6. Verify the change:
    ls -Zd /custom/website
    drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 /custom/website
  7. Configure Apache to use the directory:
    sudo vi /etc/httpd/conf.d/custom.conf
    # Add DocumentRoot /custom/website

Common SELinux Types for Services

Service Type for Content Process Type
Apache (httpd) httpd_sys_content_t httpd_t
SSH (sshd) ssh_home_t sshd_t
FTP (vsftpd) public_content_t ftpd_t
Samba (smbd) samba_share_t smbd_t

🔘 SELinux Booleans

SELinux booleans are on/off switches that enable or disable specific features in the SELinux policy without requiring policy recompilation. They provide a flexible way to adjust policy behavior.

Working with Booleans

# List all booleans
sudo getsebool -a

# List booleans with descriptions
sudo semanage boolean -l

# Check specific boolean
getsebool httpd_can_network_connect
httpd_can_network_connect --> off

# Search for related booleans
sudo getsebool -a | grep httpd
httpd_anon_write --> off
httpd_builtin_scripting --> on
httpd_can_connect_ftp --> off
httpd_can_connect_ldap --> off
httpd_can_network_connect --> off
httpd_can_network_connect_db --> off
httpd_can_sendmail --> off
httpd_enable_cgi --> on
httpd_enable_homedirs --> off
httpd_use_nfs --> off
httpd_use_cifs --> off

Setting Booleans

# Set boolean temporarily (runtime only)
sudo setsebool httpd_can_network_connect on

# Set boolean permanently (survives reboot)
sudo setsebool -P httpd_can_network_connect on

# Turn off a boolean
sudo setsebool -P httpd_enable_homedirs off

# Set multiple booleans at once
sudo setsebool -P httpd_can_network_connect=on httpd_can_network_connect_db=on
💡 Always Use -P Flag

In practice, always use setsebool -P to make changes permanent. Temporary changes are lost on reboot and can cause confusion.

Common SELinux Booleans

Boolean Purpose
httpd_can_network_connect Allow httpd to make network connections (proxy, API calls)
httpd_can_network_connect_db Allow httpd to connect to databases
httpd_enable_homedirs Allow httpd to access user home directories
httpd_use_nfs Allow httpd to access NFS mounted files
ftpd_anon_write Allow anonymous FTP users to upload files
samba_enable_home_dirs Allow Samba to share home directories

Finding the Right Boolean

# Search by service name
sudo getsebool -a | grep httpd
sudo getsebool -a | grep ftp
sudo getsebool -a | grep nfs

# Get boolean description
sudo semanage boolean -l | grep httpd_can_network_connect

# Use man pages for service-specific booleans
man httpd_selinux
man ftpd_selinux
man samba_selinux

🔧 Troubleshooting SELinux

When applications fail due to SELinux, follow this systematic approach:

Step 1: Confirm SELinux is the Issue

# Temporarily set SELinux to permissive
sudo setenforce 0

# Test if application works now
# If yes, SELinux is blocking it
# If no, problem is elsewhere

# Remember to re-enable enforcing
sudo setenforce 1

Step 2: Check AVC Denials

SELinux logs denials as AVC (Access Vector Cache) messages:

# View recent denials in audit log
sudo ausearch -m AVC -ts recent

# View denials from last 10 minutes
sudo ausearch -m AVC -ts recent -i

# Search for specific service denials
sudo ausearch -m AVC -c httpd -ts today

# Tail audit log in real-time
sudo tail -f /var/log/audit/audit.log | grep AVC

Step 3: Use sealert for Analysis

The sealert tool provides human-readable analysis and suggestions:

# Install setroubleshoot tools if not present
sudo dnf install setroubleshoot-server

# Analyze all denials
sudo sealert -a /var/log/audit/audit.log

# Analyze specific denial by ID
sudo sealert -l 

# Example output:
SELinux is preventing httpd from name_connect access on the tcp_socket port 8080.

***** Plugin catchall_boolean (47.5 confidence) suggests *******************

If you want to allow httpd to can network connect
Then you must tell SELinux about this by enabling the 'httpd_can_network_connect' boolean.

Do
setsebool -P httpd_can_network_connect 1

***** Plugin catchall (6.38 confidence) suggests **************************

If you believe that httpd should be allowed name_connect access on the port 8080 tcp_socket by default.
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 -X 300 -i my-httpd.pp

Step 4: Common Solutions

Solution 1: Fix File Contexts

# Restore default contexts
sudo restorecon -Rv /var/www/html/

# Or set custom context
sudo semanage fcontext -a -t httpd_sys_content_t "/custom/path(/.*)?"
sudo restorecon -Rv /custom/path/

Solution 2: Enable Boolean

# Enable required boolean
sudo setsebool -P httpd_can_network_connect on

Solution 3: Add Port Label

# Allow httpd to bind to custom port
sudo semanage port -a -t http_port_t -p tcp 8080

Solution 4: Create Custom Policy Module

When no other solution works, create a custom policy:

# Generate policy from audit log
sudo ausearch -m AVC -ts recent | audit2allow -M mypolicy

# Review the generated policy
cat mypolicy.te

# Install the policy module
sudo semodule -i mypolicy.pp

# List installed modules
sudo semodule -l | grep mypolicy

# Remove module if needed
sudo semodule -r mypolicy
⚠️ Custom Policies: Last Resort

Custom policy modules should be a last resort. They can create security holes if not properly reviewed. Always try file context changes, booleans, or port labeling first.

Troubleshooting Workflow

  1. Identify the problem: Service fails, application can't access file, etc.
  2. Confirm SELinux: Check if setenforce 0 fixes it
  3. Find AVC denials: ausearch -m AVC -ts recent
  4. Analyze with sealert: sealert -a /var/log/audit/audit.log
  5. Try suggested fix: Boolean, context, port, or policy
  6. Test the solution: Verify application works
  7. Re-enable enforcing: setenforce 1

Useful Troubleshooting Commands

# Check which process is being denied
sudo ausearch -m AVC -c httpd -ts recent

# Find all denials for a specific file
sudo ausearch -m AVC -f /var/www/html/index.html

# Generate allow rules from recent denials
sudo ausearch -m AVC -ts recent | audit2allow

# Check if a specific boolean would help
sudo getsebool -a | grep httpd

# Verify file has correct context
matchpathcon /var/www/html/index.html
ls -Z /var/www/html/index.html

🔌 Port Labeling

SELinux controls which ports services can bind to. Each port is labeled with a type, and only processes with matching types can use those ports.

Viewing Port Labels

# List all port labels
sudo semanage port -l

# Search for specific service ports
sudo semanage port -l | grep http
http_cache_port_t              tcp      8080, 8118, 8123, 10001-10010
http_port_t                    tcp      80, 81, 443, 488, 8008, 8009, 8443, 9000

# Check if a port is labeled
sudo semanage port -l | grep 8080

Adding Port Labels

# Allow httpd to bind to port 8080
sudo semanage port -a -t http_port_t -p tcp 8080

# Allow httpd to bind to port range
sudo semanage port -a -t http_port_t -p tcp 8080-8090

# Add UDP port
sudo semanage port -a -t dns_port_t -p udp 5353

Modifying Port Labels

# Change port type
sudo semanage port -m -t http_cache_port_t -p tcp 8080

# List custom port definitions only
sudo semanage port -l -C

Removing Port Labels

# Remove custom port label
sudo semanage port -d -t http_port_t -p tcp 8080

# Remove port range
sudo semanage port -d -t http_port_t -p tcp 8080-8090

Procedure: Configuring Apache on Custom Port

  1. Edit Apache config:
    sudo vi /etc/httpd/conf/httpd.conf
    # Change: Listen 8888
  2. Try to start Apache:
    sudo systemctl start httpd
    # Fails with "Permission denied" in SELinux enforcing mode
  3. Check audit log:
    sudo ausearch -m AVC -c httpd -ts recent
    # Shows denial for name_bind on tcp_socket port 8888
  4. Add port label:
    sudo semanage port -a -t http_port_t -p tcp 8888
  5. Start Apache successfully:
    sudo systemctl start httpd
    sudo systemctl status httpd
  6. Verify:
    sudo ss -tlnp | grep 8888
    curl http://localhost:8888

📝 Practice Questions

Question 1: How do you permanently change SELinux to permissive mode?

  • A) setenforce 0
  • B) Edit /etc/selinux/config, set SELINUX=permissive, reboot
  • C) systemctl disable selinux
  • D) chmod 000 /sys/fs/selinux
Answer: B) Edit /etc/selinux/config, set SELINUX=permissive, reboot
setenforce 0 is temporary (lost on reboot). Permanent changes require editing /etc/selinux/config and setting SELINUX=permissive, then rebooting.

Question 2: Which command permanently sets a file's SELinux context?

  • A) chcon -t httpd_sys_content_t /var/www/html/file.html
  • B) semanage fcontext -a -t httpd_sys_content_t "/path(/.*)?" && restorecon -Rv /path
  • C) chmod --context httpd_sys_content_t file.html
  • D) setfacl -m t:httpd_sys_content_t file.html
Answer: B) semanage fcontext -a -t httpd_sys_content_t "/path(/.*)?" && restorecon -Rv /path
semanage fcontext creates a permanent rule in the SELinux policy database. restorecon applies it. chcon makes temporary changes that are lost when the filesystem is relabeled.

Question 3: How do you allow httpd to connect to network databases permanently?

  • A) setsebool httpd_can_network_connect_db on
  • B) setsebool -P httpd_can_network_connect_db on
  • C) semanage boolean -a httpd_can_network_connect_db
  • D) getenforce -s httpd_can_network_connect_db
Answer: B) setsebool -P httpd_can_network_connect_db on
The -P flag makes the boolean change persistent across reboots. Without -P, the change is temporary and lost on reboot.

Question 4: What tool provides human-readable SELinux denial explanations?

  • A) ausearch
  • B) sealert
  • C) seinfo
  • D) getenforce
Answer: B) sealert
sealert analyzes audit logs and provides detailed, human-readable explanations of SELinux denials with suggested solutions. Usage: sealert -a /var/log/audit/audit.log

Question 5: How do you allow Apache to bind to port 8080?

  • A) firewall-cmd --add-port=8080/tcp
  • B) semanage port -a -t http_port_t -p tcp 8080
  • C) setsebool -P httpd_port_8080 on
  • D) chcon -t http_port_t 8080
Answer: B) semanage port -a -t http_port_t -p tcp 8080
semanage port adds a port label allowing httpd (http_port_t type) to bind to the specified port. This is separate from firewall configuration.