SELinux: Architecture, Policies & Troubleshooting
Chapter 16: SELinux: Architecture, Policies & Troubleshooting
Section titled “Chapter 16: SELinux: Architecture, Policies & Troubleshooting”Learning Objectives
Section titled “Learning Objectives”- Understand SELinux architecture and the Type Enforcement model
- Know SELinux modes and how to manage them
- Read and understand SELinux denials (AVC)
- Write custom SELinux policies
- Troubleshoot SELinux issues without disabling it
Prerequisites
Section titled “Prerequisites”What is this? (Beginner On-Ramp)
Section titled “What is this? (Beginner On-Ramp)”SELinux (Security-Enhanced Linux) is an advanced security guard for your system. Standard Linux security just checks if a user owns a file. SELinux checks a strict rulebook to see if a specific program is allowed to touch a specific file, stopping hackers even if they gain root access.
16.1 What is SELinux?
Section titled “16.1 What is SELinux?”SELinux (Security-Enhanced Linux) is a Mandatory Access Control (MAC) system originally developed by the NSA and now maintained by Red Hat. It enforces security policies at the kernel level, even constraining root.
SELinux Core Concept: Everything Has a Label ─────────────────────────────────────────────
Every object in Linux gets a SELinux label (security context):
Files: system_u:object_r:httpd_sys_content_t:s0 Process: system_u:system_r:httpd_t:s0 Port: system_u:object_r:http_port_t:s0
Format: user:role:type:level
SELinux Policy defines: "Can process with type httpd_t access file with type httpd_sys_content_t?"
If not explicitly allowed: DENIED (regardless of file permissions)
DAC says: "Can alice read this file?" (user-based) SELinux says: "Can this process type access this resource type?" (type-based)16.2 SELinux Architecture
Section titled “16.2 SELinux Architecture” Type Enforcement (TE) — The Core Model ──────────────────────────────────────
SELinux Policy Rule: allow httpd_t httpd_sys_content_t:file { read open getattr }; │ │ │ │ └── Operations allowed │ │ │ └── Object class (file, dir, socket...) │ │ └── Object type (the resource being accessed) │ └── Subject type (the process type doing the access) └── allow (permit this access)
Access Decision Flow:
nginx (httpd_t) tries to read /var/www/html/index.html (httpd_sys_content_t) │ ▼ ┌─────────────────────────────────────────────────────┐ │ SELinux Access Vector Cache (AVC) │ │ │ │ Check: allow httpd_t httpd_sys_content_t:file read?│ │ Policy says: YES → GRANTED │ └─────────────────────────────────────────────────────┘
nginx (httpd_t) tries to read /etc/shadow (shadow_t) │ ▼ ┌─────────────────────────────────────────────────────┐ │ SELinux Access Vector Cache (AVC) │ │ │ │ Check: allow httpd_t shadow_t:file read? │ │ Policy says: NOT FOUND → DENIED │ │ AVC denial logged to audit.log │ └─────────────────────────────────────────────────────┘
Even if nginx runs as root and /etc/shadow is readable: → SELinux STILL BLOCKS it → This is the power of MAC16.3 SELinux Modes
Section titled “16.3 SELinux Modes” SELinux Modes ────────────── Enforcing: Policy violations are BLOCKED and LOGGED → Production systems should use this
Permissive: Policy violations are only LOGGED (not blocked) → Use for debugging new policies → NEVER use in production for extended periods
Disabled: SELinux is completely off → All denials ignored, no logging → Switching from disabled → enforcing requires relabel → NEVER do this on production!# ── Check SELinux Status ──────────────────────────────────getenforce# Enforcing / Permissive / Disabled
sestatus# SELinux status: enabled# SELinuxfs mount: /sys/fs/selinux# SELinux mount point: /sys/fs/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
# ── Change Mode Temporarily (survives until reboot) ───────setenforce 0 # Switch to Permissive (for debugging)setenforce 1 # Switch back to Enforcing
# ── Change Mode Permanently ───────────────────────────────# /etc/selinux/config (or /etc/sysconfig/selinux)SELINUX=enforcing # enforcing | permissive | disabled
# ── SELinux Policies (Targeted is the standard) ────────────# targeted: only specific processes (httpd, sshd, etc.) are confined# minimum: even fewer processes confined# mls: Multi-Level Security (government, top secret / secret)SELINUXTYPE=targeted16.4 SELinux Contexts
Section titled “16.4 SELinux Contexts”# ── View Security Contexts ────────────────────────────────# File contextls -laZ /var/www/html/# drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 html/
# Process contextps auxZ | grep httpd# system_u:system_r:httpd_t:s0 root 1234 ...
# Port contextsemanage port -l | grep http# http_port_t tcp 80, 443, 488, 8008, 8009, 8443
# ── Change File Context ───────────────────────────────────# Change context of a filechcon -t httpd_sys_content_t /srv/mywebsite/index.html
# Change context recursivelychcon -R -t httpd_sys_content_t /srv/mywebsite/
# The CORRECT way: set the policy, then relabel# (chcon is temporary, restorecon will reset it)semanage fcontext -a -t httpd_sys_content_t "/srv/mywebsite(/.*)?"restorecon -Rv /srv/mywebsite/
# ── Restore Default Contexts ──────────────────────────────restorecon -v /etc/nginx/nginx.conf # Single filerestorecon -Rv /var/www/ # Recursive
# ── Full System Relabel (after disabling/re-enabling SELinux) ──# Touch /.autorelabel and reboottouch /.autorelabelreboot# System relabels all files on next boot (takes several minutes)16.5 Reading SELinux Denials (AVC)
Section titled “16.5 Reading SELinux Denials (AVC)”# ── View AVC Denials ──────────────────────────────────────# Raw audit logcat /var/log/audit/audit.log | grep "type=AVC"
# Example AVC denial:# type=AVC msg=audit(1705312800.123:456): avc: denied { read } for# pid=1234 comm="nginx" name="secret.conf"# dev="sda1" ino=5678# scontext=system_u:system_r:httpd_t:s0# tcontext=system_u:object_r:admin_home_t:s0# tclass=file permissive=0
# Breaking down the denial:# { read } = operation attempted# comm="nginx" = which program# name="secret.conf" = which file# scontext=...httpd_t = process type (source)# tcontext=...admin_home_t = file type (target)# tclass=file = object class# permissive=0 = was it blocked (0=blocked, 1=permissive/logged only)
# ── Human-readable with ausearch ──────────────────────────ausearch -m avc -ts today # Today's denialsausearch -m avc -ts recent # Recent denialsausearch -m avc -comm nginx # nginx-specific denials
# ── Best tool: sealert (audit2why) ────────────────────────# Install: yum install setroubleshoot-serversealert -a /var/log/audit/audit.log# Provides:# - Human-readable explanation of why it was denied# - Suggested fix commands# - Whether an AVC boolean exists to fix it
# ── audit2allow: Generate policy from denials ─────────────# Show what policy would allow the denied actions:ausearch -m avc -ts today | audit2allow
# Generate a new policy module:ausearch -m avc -ts today | audit2allow -M mynginx# Creates: mynginx.te (type enforcement) + mynginx.pp (compiled policy)
# Install the new policy module:semodule -i mynginx.pp16.6 SELinux Booleans
Section titled “16.6 SELinux Booleans”Booleans are pre-built policy switches that toggle groups of rules on/off.
# ── List all booleans ─────────────────────────────────────getsebool -a # All booleansgetsebool -a | grep httpd # HTTP-related booleanssemanage boolean -l # With descriptions
# Common booleans for web servers:# httpd_can_network_connect = allow nginx to make outbound connections# httpd_can_network_connect_db = allow nginx to connect to databases# httpd_use_nfs = allow nginx to serve files from NFS# httpd_enable_cgi = allow nginx/apache to execute CGI scripts
# ── Set a Boolean ─────────────────────────────────────────setsebool httpd_can_network_connect on # Temporarysetsebool -P httpd_can_network_connect on # Persistent (-P = permanent)
# ── Common Real-World Boolean Fixes ───────────────────────# Problem: nginx can't connect to upstream app serversetsebool -P httpd_can_network_connect on
# Problem: nginx can't connect to PostgreSQLsetsebool -P httpd_can_network_connect_db on
# Problem: nginx serving files from /home/user/public_htmlsetsebool -P httpd_enable_homedirs on
# Problem: SELinux blocks SMTP on custom port# (Better fix: add port to correct type)semanage port -a -t smtp_port_t -p tcp 252516.7 SELinux Port Management
Section titled “16.7 SELinux Port Management”# ── List port contexts ────────────────────────────────────semanage port -l # All portssemanage port -l | grep http # HTTP ports
# ── Allow service on custom port ─────────────────────────# Example: nginx on port 8080 (already allowed: http_port_t includes 8080)# Example: nginx on port 9000 (not in http_port_t by default)semanage port -a -t http_port_t -p tcp 9000semanage port -l | grep 9000 # Verify
# ── Delete a port from a type ─────────────────────────────semanage port -d -t http_port_t -p tcp 9000
# ── Modify existing port type ─────────────────────────────semanage port -m -t http_port_t -p tcp 900016.8 Writing Custom SELinux Policies
Section titled “16.8 Writing Custom SELinux Policies”# Method 1: audit2allow (recommended for simple cases)# Generate policy from denialsausearch -m avc -ts today | audit2allow -M my_custom_policysemodule -i my_custom_policy.pp
# Method 2: Write a .te file manuallycat > myapp.te << 'EOF'module myapp 1.0;
require { type httpd_t; type myapp_exec_t; type myapp_var_t; class file { read write execute open }; class dir { read search };}
# Allow httpd to execute myapp binaryallow httpd_t myapp_exec_t:file { read execute open };# Allow httpd to read/write myapp data directoryallow httpd_t myapp_var_t:dir { read search };allow httpd_t myapp_var_t:file { read write open };EOF
# Compile and installcheckmodule -M -m -o myapp.mod myapp.tesemodule_package -o myapp.pp -m myapp.modsemodule -i myapp.pp
# Verify module loadedsemodule -l | grep myapp16.9 Production Troubleshooting Workflow
Section titled “16.9 Production Troubleshooting Workflow” SELinux Troubleshooting Decision Tree ──────────────────────────────────────
Application failing? │ ▼ Check: getenforce │ ├── Disabled → SELinux not the issue │ └── Enforcing/Permissive │ ▼ setenforce 0 (Permissive mode) Retry the failing operation │ ├── Still fails → NOT a SELinux issue │ Check file permissions, service config │ └── Now works → SELinux is blocking it │ ▼ ausearch -m avc -ts today sealert -a /var/log/audit/audit.log │ ▼ Is there a boolean that fixes it? getsebool -a | grep <service> │ ├── Yes → setsebool -P boolean_name on │ └── No → Is it a file context issue? │ ├── Yes → semanage fcontext + restorecon │ └── No → audit2allow to create policy REVIEW POLICY before applying! │ ▼ setenforce 1 (back to Enforcing) Verify fix works in Enforcing mode# Troubleshooting one-liner:# 1. Check for recent denialsausearch -m avc -ts recent 2>/dev/null | audit2why
# 2. Set permissive for one domain (not the whole system!)# Only put specific process type in permissive:semanage permissive -a httpd_t # httpd processes permissive onlysemanage permissive -d httpd_t # Remove domain-specific permissive
# This is much better than setenforce 0 for the whole system!16.10 Interview Questions
Section titled “16.10 Interview Questions”Q1: What is the difference between Enforcing and Permissive mode in SELinux?
Answer: In Enforcing mode, SELinux actively blocks policy violations — processes cannot access resources the policy doesn’t allow, and denials are logged. This is the production mode. In Permissive mode, SELinux logs all policy violations but does NOT block them — everything runs as if SELinux were disabled. Permissive is used for debugging new policies to see what would be denied without actually breaking the application. Critical distinction: Permissive is NOT the same as Disabled — the policy is still loaded and evaluated, and the AVC cache is still populated. Never run production servers in Permissive mode.
Q2: An application works fine when SELinux is in Permissive mode but fails in Enforcing mode. How do you fix this without disabling SELinux?
Answer: (1) Check AVC denials:
ausearch -m avc -ts today | audit2whyorsealert -a /var/log/audit/audit.log— this gives human-readable explanations and fix suggestions. (2) Check if there’s an existing boolean:getsebool -a | grep <service_name>. If yes,setsebool -P boolean_name on. (3) If it’s a file context problem:ls -laZ <file>to check context, thensemanage fcontext -a -t correct_type "/path(/.*)?"andrestorecon -Rv /path. (4) If it’s a port problem:semanage port -a -t service_port_t -p tcp <port>. (5) If none of above:audit2allow -M mypolicyto generate a custom policy. Always review generated policies before applying.
Q3: What does this AVC denial mean?
avc: denied { write } for pid=1234 comm="php-fpm" scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:var_t:s0 tclass=file
Answer: PHP-FPM (running with type
httpd_t) is trying to write to a file with typevar_t. The SELinux policy doesn’t allowhttpd_tto write tovar_tfiles. Most likely, a file in/var/has the wrong SELinux type — it should behttpd_sys_rw_content_tor similar for web-writable files. Fix: check the file:ls -laZ /path/to/file, then relabel it:semanage fcontext -a -t httpd_sys_rw_content_t "/path/to/dir(/.*)?"andrestorecon -Rv /path/to/dir.
16.11 Summary
Section titled “16.11 Summary”| Concept | Key Point |
|---|---|
| SELinux type | Every process and file has a type label |
| Type Enforcement | Policy defines which types can access which |
| AVC | Access Vector Cache — logs denials |
| Boolean | Pre-built policy switches |
| audit2allow | Generates policy from AVC denials |
| restorecon | Restores file contexts to policy defaults |
| semanage | Manages persistent SELinux settings |
Prerequisites
Section titled “Prerequisites”Chapter 14 (Security Fundamentals).
Advantages & Disadvantages
Section titled “Advantages & Disadvantages”Advantages: Mandatory Access Control stops 0-day exploits, even root is restricted. Disadvantages: Extremely complex policy language, often disabled by frustrated admins.
Common Mistakes
Section titled “Common Mistakes”- Disabling SELinux entirely (
setenforce 0permanently) instead of fixing the policy. - Running
audit2allow -Mblindly without understanding the security implications. - Forgetting to restorecon after moving files.
Troubleshooting
Section titled “Troubleshooting”| Symptom | Cause | Diagnosis | Fix |
|---|---|---|---|
| Application gets permission denied but standard permissions are correct | SELinux blocking access | grep AVC /var/log/audit/audit.log | Use restorecon or semanage to set correct context, or set an SELinux boolean |
Hands-on Lab
Section titled “Hands-on Lab”Objective: Manage SELinux contexts and booleans.
# 1. Check SELinux statussestatus
# 2. View file contextsls -Z /var/www/html/
# 3. View process contextsps -eZ | grep nginx
# 4. View and change a booleangetsebool -a | grep httpd_can_network_connectsetsebool -P httpd_can_network_connect 1Exercises
Section titled “Exercises”- Move a web page from your home directory to
/var/www/html. Why does the web server get permission denied? Fix it usingrestorecon. - Use
audit2allowto generate a custom policy module for a blocked action, review the generated.tefile, and load it.
Revision Notes
Section titled “Revision Notes”- SELinux is a MAC system that labels files and processes.
- Even root is constrained by SELinux policies.
- Contexts have User:Role:Type:Level. Type enforcement (TE) is the most common.
- Booleans allow toggling policy rules at runtime without recompiling.
Further Reading
Section titled “Further Reading”- SELinux Coloring Book
- Red Hat SELinux Guide
Related Chapters
Section titled “Related Chapters”- Chapter 17 — AppArmor as an alternative MAC
Last Updated: July 2026