Use when you have a written implementation plan to execute in a separate session with review checkpoints
npx skills add netsapiensis/claude-code-skills --skill "rocky-selinux"
Install specific skill from multi-skill repository
# Description
SELinux management on Rocky Linux 8/9 including modes, file contexts, booleans, port labeling, and troubleshooting with audit2why/audit2allow. Use when troubleshooting permission denials, configuring SELinux for services, managing file contexts, or diagnosing AVC denials.
# SKILL.md
name: rocky-selinux
description: SELinux management on Rocky Linux 8/9 including modes, file contexts, booleans, port labeling, and troubleshooting with audit2why/audit2allow. Use when troubleshooting permission denials, configuring SELinux for services, managing file contexts, or diagnosing AVC denials.
Rocky Linux SELinux Management
SELinux policy management, file contexts, booleans, port labeling, and troubleshooting for Rocky Linux 8/9.
Prerequisite: See rocky-foundation for OS detection and safety tier definitions.
Critical Rule
NEVER recommend disabling SELinux. Always troubleshoot and fix SELinux issues properly.
WRONG:
# WRONG -- NEVER do any of these:
setenforce 0 # Disables enforcement
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
echo 0 > /sys/fs/selinux/enforce
CORRECT:
# CORRECT -- Troubleshoot and fix the actual issue
# 1. Check what's being denied
ausearch -m AVC -ts recent # [READ-ONLY]
# 2. Understand why
audit2why < /var/log/audit/audit.log # [READ-ONLY]
# 3. Apply the proper fix (context, boolean, or policy module)
SELinux Status and Modes
# Check current status # [READ-ONLY]
getenforce # Quick: Enforcing/Permissive/Disabled
sestatus # Detailed status
sestatus -v # Verbose with context info
# Temporarily set permissive (for debugging only) # [CONFIRM]
setenforce 0 # Permissive (logs but doesn't block)
setenforce 1 # Enforcing (restore when done debugging)
Permanent mode is set in /etc/selinux/config:
# /etc/selinux/config
SELINUX=enforcing # enforcing | permissive | disabled
SELINUXTYPE=targeted # Always 'targeted' on Rocky
WARNING: Changing from disabled to enforcing requires a full filesystem relabel on next boot:
# Force relabel on next boot # [CONFIRM]
touch /.autorelabel
reboot # [CONFIRM]
# Relabel can take a long time on large filesystems
File Contexts
Viewing Contexts
# View file contexts # [READ-ONLY]
ls -Z /var/www/html/
ls -Zd /opt/myapp/
stat -c '%C' /var/www/html/index.html
# View default context rules # [READ-ONLY]
semanage fcontext -l | grep /var/www
semanage fcontext -l | grep httpd
matchpathcon /var/www/html/index.html # Rocky 8
# On Rocky 9, use:
selabel_lookup /var/www/html/index.html # Or: restorecon -nv /path
Setting File Contexts
CORRECT -- use semanage fcontext + restorecon:
# Add a custom context rule # [CONFIRM]
semanage fcontext -a -t httpd_sys_content_t "/opt/myapp/public(/.*)?"
# Apply the rule to existing files # [CONFIRM]
restorecon -Rv /opt/myapp/public/
# Verify # [READ-ONLY]
ls -Z /opt/myapp/public/
WRONG -- using chcon:
# WRONG: chcon changes are lost on restorecon or relabel
chcon -R -t httpd_sys_content_t /opt/myapp/public/
# Next restorecon or relabel will revert this!
# CORRECT: semanage fcontext persists permanently
semanage fcontext -a -t httpd_sys_content_t "/opt/myapp/public(/.*)?"
restorecon -Rv /opt/myapp/public/
Common File Context Types
| Context Type | Purpose |
|---|---|
httpd_sys_content_t |
Web content (read-only by httpd) |
httpd_sys_rw_content_t |
Web content (read-write by httpd) |
httpd_log_t |
Web server logs |
httpd_config_t |
Web server configuration |
mysqld_db_t |
MariaDB/MySQL data files |
var_log_t |
Log files in /var/log |
etc_t |
Configuration files |
user_home_t |
User home directory content |
ssh_home_t |
SSH keys (~/.ssh) |
cert_t |
TLS/SSL certificates |
container_file_t |
Container-accessible files |
Manage Custom Context Rules
# List custom rules # [READ-ONLY]
semanage fcontext -l -C # -C shows only custom (local) rules
# Modify existing rule # [CONFIRM]
semanage fcontext -m -t httpd_sys_rw_content_t "/opt/myapp/uploads(/.*)?"
# Delete custom rule # [CONFIRM]
semanage fcontext -d "/opt/myapp/public(/.*)?"
# Restore default contexts for a path # [CONFIRM]
restorecon -Rv /var/www/html/
Booleans
SELinux booleans toggle pre-defined policy rules on/off.
# List all booleans # [READ-ONLY]
getsebool -a
getsebool -a | grep httpd
# Check specific boolean # [READ-ONLY]
getsebool httpd_can_network_connect
# Set boolean temporarily (lost on reboot) # [CONFIRM]
setsebool httpd_can_network_connect on
# Set boolean permanently # [CONFIRM]
setsebool -P httpd_can_network_connect on
Common Booleans by Service
Web server (httpd/nginx):
# Allow httpd to make network connections (e.g., reverse proxy) # [CONFIRM]
setsebool -P httpd_can_network_connect on
# Allow httpd to connect to databases # [CONFIRM]
setsebool -P httpd_can_network_connect_db on
# Allow httpd to send email # [CONFIRM]
setsebool -P httpd_can_sendmail on
# Allow httpd to read user home directories # [CONFIRM]
setsebool -P httpd_enable_homedirs on
# Allow httpd to execute CGI scripts # [CONFIRM]
setsebool -P httpd_enable_cgi on
# Allow httpd to use NFS-mounted content # [CONFIRM]
setsebool -P httpd_use_nfs on
SSH:
# Allow SSH to use non-standard port # [CONFIRM]
# (Also need semanage port -- see Port Labeling section)
setsebool -P ssh_sysadm_login on
Samba/NFS:
setsebool -P samba_enable_home_dirs on # [CONFIRM]
setsebool -P use_nfs_home_dirs on # [CONFIRM]
Port Labeling
SELinux restricts which ports services can bind to.
# List port labels # [READ-ONLY]
semanage port -l | grep http
semanage port -l | grep ssh
semanage port -l | grep -w 8080
# Allow httpd on custom port # [CONFIRM]
semanage port -a -t http_port_t -p tcp 8080
# Allow SSH on custom port # [CONFIRM]
semanage port -a -t ssh_port_t -p tcp 2222
# Allow OpenSearch ports # [CONFIRM]
semanage port -a -t http_port_t -p tcp 9200
semanage port -a -t http_port_t -p tcp 9300
# Remove custom port label # [CONFIRM]
semanage port -d -t http_port_t -p tcp 8080
# List custom port rules # [READ-ONLY]
semanage port -l -C
WRONG -- only changing the service config without SELinux:
# WRONG: Changed nginx to port 8080 but forgot SELinux
# nginx.conf: listen 8080;
# Result: nginx fails to bind, AVC denial in audit log
# CORRECT: Update SELinux port label first, then change config
semanage port -a -t http_port_t -p tcp 8080 # [CONFIRM]
# Then update nginx.conf and restart
Troubleshooting SELinux Denials
Step-by-Step Troubleshooting
Step 1: Identify the denial
# Recent AVC denials # [READ-ONLY]
ausearch -m AVC -ts recent
ausearch -m AVC --start today
ausearch -m AVC -c httpd # Filter by command
# Or from audit.log directly # [READ-ONLY]
grep "type=AVC" /var/log/audit/audit.log | tail -20
# SELinux denial messages in journal # [READ-ONLY]
journalctl -t setroubleshoot --since "1 hour ago"
Step 2: Understand the denial
# Use audit2why for human-readable explanation # [READ-ONLY]
ausearch -m AVC -ts recent | audit2why
# Or from the full log # [READ-ONLY]
audit2why < /var/log/audit/audit.log
# sealert for detailed analysis (if setroubleshoot installed) # [READ-ONLY]
sealert -a /var/log/audit/audit.log
Step 3: Apply the fix
The fix depends on what audit2why tells you:
| audit2why says | Fix |
|---|---|
| "was caused by missing file context" | semanage fcontext + restorecon |
| "setsebool" | Set the recommended boolean |
| "was caused by a missing type" | May need a custom policy module |
| "was caused by incorrect file context" | restorecon -Rv /path |
Step 4: Verify the fix
# Clear the denial by restarting the service # [CONFIRM]
systemctl restart httpd
# Check no new denials # [READ-ONLY]
ausearch -m AVC -ts recent
Creating Custom Policy Modules (Last Resort)
Only when file contexts and booleans don't solve the issue:
# Generate a policy module from denials # [READ-ONLY] (generation only)
ausearch -m AVC -ts recent | audit2allow -M mypolicy
# Review the generated policy # [READ-ONLY]
cat mypolicy.te
# Install the policy module # [CONFIRM]
semodule -i mypolicy.pp
# List installed custom modules # [READ-ONLY]
semodule -l | grep mypolicy
# Remove a custom module # [CONFIRM]
semodule -r mypolicy
WARNING: Never blindly pipe audit2allow output into semodule. Always review the .te file first to understand what permissions you're granting.
Process Domain Troubleshooting
# Check what domain a process is running in # [READ-ONLY]
ps -eZ | grep nginx
ps -eZ | grep httpd
# Check if a process is confined # [READ-ONLY]
ps -eZ | grep unconfined_t # These are not confined
# View transitions # [READ-ONLY]
sesearch -T -s init_t -t httpd_exec_t # Rocky 8
sesearch --type_trans -s init_t -t httpd_exec_t # Rocky 9
SELinux and Common Services
Apache/Nginx
# Custom web root
semanage fcontext -a -t httpd_sys_content_t "/opt/www(/.*)?" # [CONFIRM]
restorecon -Rv /opt/www/ # [CONFIRM]
# Writable directory (uploads)
semanage fcontext -a -t httpd_sys_rw_content_t "/opt/www/uploads(/.*)?" # [CONFIRM]
restorecon -Rv /opt/www/uploads/ # [CONFIRM]
# Reverse proxy
setsebool -P httpd_can_network_connect on # [CONFIRM]
MariaDB/MySQL
# Custom data directory
semanage fcontext -a -t mysqld_db_t "/data/mysql(/.*)?" # [CONFIRM]
restorecon -Rv /data/mysql/ # [CONFIRM]
# Custom log location
semanage fcontext -a -t mysqld_log_t "/var/log/mariadb(/.*)?" # [CONFIRM]
restorecon -Rv /var/log/mariadb/ # [CONFIRM]
SSH
# Non-standard SSH port
semanage port -a -t ssh_port_t -p tcp 2222 # [CONFIRM]
Container (Podman)
# Allow containers to access host path
semanage fcontext -a -t container_file_t "/srv/containers(/.*)?" # [CONFIRM]
restorecon -Rv /srv/containers/ # [CONFIRM]
# Or use the :Z/:z volume flag in podman (auto-labels)
podman run -v /srv/data:/data:Z myimage # [CONFIRM]
Version Differences
| Feature | Rocky 8 | Rocky 9 |
|---|---|---|
matchpathcon |
Available | Deprecated (use restorecon -nv) |
sesearch syntax |
-T flag |
--type_trans |
setools-console |
Package name | Same |
| Default policy | targeted | targeted |
| Policy version | Older | Newer (more rules) |
| MLS/MCS | Available | Available |
Checklist: SELinux for New Service Deployment
- [ ] Service is running in correct SELinux domain (
ps -eZ | grep <service>) - [ ] Data directories have correct file contexts (
ls -Z) - [ ] Custom paths registered with
semanage fcontext - [ ] Non-standard ports registered with
semanage port - [ ] Required booleans enabled (
getsebool -a | grep <service>) - [ ] No AVC denials after testing (
ausearch -m AVC -ts recent) - [ ] Custom rules documented for future reference
When to Use This Skill
- Any "Permission denied" error that isn't a standard Unix permission issue
- Deploying a new service or changing service paths
- Moving service data to non-standard directories
- Changing service ports
- AVC denial messages in logs
- After system relabel or SELinux policy updates
- Configuring containers with host volumes
Related Skills
- rocky-foundation -- OS detection, safety tiers
- rocky-webstack -- Web server SELinux contexts
- rocky-security-hardening -- SELinux as part of security posture
- rocky-core-system -- Service management (services must run in correct domains)
- rocky-opensearch -- OpenSearch-specific SELinux contexts
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.