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.
📋 Table of Contents
🛡️ 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
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
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: 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
- Create custom web directory:
sudo mkdir -p /custom/website - Create test file:
sudo echo "Hello from custom location" > /custom/website/index.html - Check current context (wrong):
ls -Zd /custom/website drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 /custom/website - Add permanent context rule:
sudo semanage fcontext -a -t httpd_sys_content_t "/custom/website(/.*)?" - Apply the context:
sudo restorecon -Rv /custom/website - Verify the change:
ls -Zd /custom/website drwxr-xr-x. root root unconfined_u:object_r:httpd_sys_content_t:s0 /custom/website - 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
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 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
- Identify the problem: Service fails, application can't access file, etc.
- Confirm SELinux: Check if
setenforce 0fixes it - Find AVC denials:
ausearch -m AVC -ts recent - Analyze with sealert:
sealert -a /var/log/audit/audit.log - Try suggested fix: Boolean, context, port, or policy
- Test the solution: Verify application works
- 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
- Edit Apache config:
sudo vi /etc/httpd/conf/httpd.conf # Change: Listen 8888 - Try to start Apache:
sudo systemctl start httpd # Fails with "Permission denied" in SELinux enforcing mode - Check audit log:
sudo ausearch -m AVC -c httpd -ts recent # Shows denial for name_bind on tcp_socket port 8888 - Add port label:
sudo semanage port -a -t http_port_t -p tcp 8888 - Start Apache successfully:
sudo systemctl start httpd sudo systemctl status httpd - Verify:
sudo ss -tlnp | grep 8888 curl http://localhost:8888
📝 Practice Questions
Question 1: How do you permanently change SELinux to permissive mode?
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?
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?
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?
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?
semanage port adds a port label allowing httpd (http_port_t type) to bind to the specified port. This is separate from firewall configuration.