Why your app is blocked and why disabling SELinux is a trap
You installed a new service, started it, and got a Permission denied error that made no sense. The user had root. The file permissions looked correct. You searched the error and found a forum post telling you to disable SELinux. You did. The service started. Now you are wondering if your system is safe, or you are about to disable it and need to know the cost. Disabling SELinux is the nuclear option. It removes the safety net that keeps a compromised process from taking over your whole system. You can fix the denial without turning off security.
How SELinux decides what is allowed
SELinux stands for Security-Enhanced Linux. It adds mandatory access controls on top of standard Unix permissions. Standard permissions ask "Who are you and do you own this file?" SELinux asks "What is this process allowed to do?" A web server process might own a file, but SELinux knows it is a web server and blocks it from reading your password file. When you see a denial, SELinux is doing its job. The process tried to access a resource outside its policy. The error is not a bug. It is a boundary.
Fedora logs SELinux denials in a way that is readable. Run journalctl -t setroubleshoot to see one-line summaries of what was blocked. This is better than digging through raw audit logs. The summary tells you the source process, the target file, and the suggested fix. Most sysadmins type journalctl -xeu <unit> muscle-memory style, but the setroubleshoot tag isolates the security messages you need.
Diagnose the denial
Here's how to find the exact reason a process was blocked and get a human-readable explanation. You need the troubleshooting utilities installed. They are not part of the minimal base.
# Install the tools needed to parse SELinux logs
sudo dnf install policycoreutils-python-utils setroubleshoot-server
# WHY: policycoreutils-python-utils provides audit2why and semanage.
# WHY: setroubleshoot-server provides the translation database for readable messages.
# WHY: These packages are required to decode denials into actionable fixes.
Here's how to query the audit log for recent denials and translate them.
# Show recent AVC denials with explanations
sudo ausearch -m avc -ts recent | audit2why
# WHY: ausearch queries the audit log for Access Vector Cache denials.
# WHY: -ts recent limits output to the last few minutes so you don't scroll through months of logs.
# WHY: audit2why translates the raw audit fields into plain text explaining the policy mismatch.
If audit2why returns nothing, the denial might be older or the log was rotated. Check the journal directly.
# Search the journal for SELinux denial messages
journalctl -t setroubleshoot --no-pager
# WHY: The setroubleshoot daemon writes high-level summaries to the journal.
# WHY: --no-pager dumps the output to stdout for easy reading or piping.
# WHY: Look for lines starting with "SELinux is preventing" for the source and target details.
Run restorecon before you blame SELinux. Half the denials come from files that lost their labels during a manual copy.
Fix the policy mismatch
Here's how to restore correct security labels if files were moved or copied without preserving context. When you use cp or mv, the new file often inherits the label of the destination directory or keeps the old label. SELinux expects specific labels for specific paths.
# Restore default labels for a directory tree
sudo restorecon -Rv /var/www/html
# WHY: restorecon reads the file context definitions from the policy and applies the correct label.
# WHY: -R recurses into subdirectories. -v prints every file that gets relabeled.
# WHY: This fixes issues where cp or mv dropped the SELinux context during a manual file move.
Here's how to allow a service to perform a specific action or use a non-standard port without changing the core policy. Booleans are switches built into the policy for common configuration variations.
# Allow httpd to make outbound network connections
sudo setsebool -P httpd_can_network_connect on
# WHY: setsebool toggles a policy boolean that controls a specific capability.
# WHY: -P writes the change to disk so it persists across reboots.
# WHY: Booleans are designed for common configuration variations without editing policy modules.
# Add a port to the allowed set for a service type
sudo semanage port -a -t http_port_t -p tcp 8080
# WHY: semanage modifies the SELinux policy store.
# WHY: -a adds a new port mapping. -t assigns the http_port_t type to the port.
# WHY: This allows the web server to bind to 8080, which is not in the default http_port_t list.
Here's how to generate a custom policy module when no boolean or standard fix exists. This is the safe way to grant permissions. It creates a module you can review and remove later.
# Generate a policy module from recent denials
sudo ausearch -m avc -ts recent | audit2allow -M myfix
# WHY: audit2allow parses the denials and generates a policy module source file.
# WHY: -M creates the module files myfix.te and myfix.pp.
# WHY: This automates the creation of a custom policy based on observed behavior.
# Review the generated source before installing
cat myfix.te
# WHY: The .te file shows exactly what permissions are being granted.
# WHY: Verify the source and target types match your expectations.
# WHY: Never install a module without reviewing the rules it adds.
# Install the generated module
sudo semodule -i myfix.pp
# WHY: semodule loads the compiled policy module into the running kernel.
# WHY: The module persists across reboots once installed.
# WHY: You can remove it later with semodule -r myfix if it causes issues.
Check the service status before you restart. If the unit is failed, the logs tell you why. Restarting blindly just generates more denials.
Verify the fix
Here's how to confirm the service starts and SELinux is no longer blocking it.
# Check service status and recent logs
systemctl status myservice.service
# WHY: status shows the active state and the last few log lines.
# WHY: Look for "active (running)" and no new AVC denials in the log output.
# WHY: systemctl status is the first command to run when debugging a service failure.
# Verify no new denials appeared
sudo ausearch -m avc -ts recent
# WHY: An empty output confirms the process is no longer hitting policy boundaries.
# WHY: If denials persist, the fix was incomplete or the wrong boolean was toggled.
# WHY: Repeat the diagnosis step if the service still fails to start.
Trust the package manager. Manual edits in /usr/lib/ drift and get overwritten. Keep your changes in /etc/.
Common pitfalls and error patterns
The error message rarely mentions SELinux directly. You will see Permission denied in the application log. You might see Cannot bind to port 8080 even though the port is free. The application thinks it is a permission issue. It is a policy issue. If you see audit: type=1400 audit(...): avc: denied { read } for pid=..., that is the raw audit record. Do not parse this by hand. Use audit2why.
The dnf upgrade command will refuse to proceed and print Error: Transaction test error: package python3-3.12.x conflicts with python3-3.13.y if you have manually installed packages that clash with the repository. This is unrelated to SELinux, but users often conflate dependency errors with security denials. Check the package manager output first.
setenforce 0 switches to permissive mode. It logs denials but allows them. It is temporary. It resets on reboot. Use this for testing. SELINUX=disabled in /etc/selinux/config turns off the module entirely. It removes the labels from the kernel. Re-enabling requires a full relabel, which takes time and can break the boot if labels are missing. Edit /etc/selinux/config. Never edit files in /usr/lib/selinux/. Those are owned by the policy package. Package updates will overwrite your changes. Fedora keeps user configuration in /etc/.
If you disable SELinux and then try to re-enable it, you must trigger a relabel. Create the file /.autorelabel and reboot.
# Trigger a full relabel on next boot
sudo touch /.autorelabel
sudo reboot
# WHY: touch creates the marker file that the init system checks during boot.
# WHY: The system will scan the entire filesystem and restore labels before starting services.
# WHY: This process can take 10 to 20 minutes on large disks. Do not interrupt the boot.
A botched upgrade can leave you unable to boot. Run this from a backup VM first if you can.
When to use each tool
Use setenforce 0 when you need to test if SELinux is the cause of a failure without losing audit logs. Use restorecon when files were moved or copied and lost their security context. Use setsebool when a service needs a specific capability that the policy provides as a toggle. Use semanage port when a service must bind to a port that is not in the default allowed list. Use audit2allow to generate a custom policy module when no boolean or standard fix exists. Disable SELinux only when you are running a development environment that cannot tolerate any policy constraints and you accept the security risk.
Disable SELinux only as a last resort. A misconfigured policy is easier to fix than a compromised system.