How to Fix SELinux Permission Denied Errors
Published On: 20 June 2026
Objective
This guide shows you how to diagnose and fix SELinux permission denied errors step by step. By the end, you will know how to:
- Read SELinux denial logs and understand what they mean
- Identify the exact cause of a denial (wrong context, boolean, or port)
- Apply the correct fix without disabling SELinux
- Verify the fix works in enforcing mode
You configured a service. It should work. Instead you get a permission denied error even though the file permissions look correct. SELinux is blocking it silently in the background. This is one of the most common problems on RHEL systems and one of the most misunderstood. Most people disable SELinux to make the error go away. That is the wrong fix. Here is the right one.
First: Confirm SELinux Is Actually the Problem
Before touching SELinux, confirm it is the cause. Temporarily set SELinux to permissive mode. In permissive mode, SELinux logs denials but does not enforce them.
setenforce 0
Now test your service or action again. Two possible outcomes:
- It works in permissive mode: SELinux is the cause. Proceed with this guide.
- It still fails in permissive mode: SELinux is not the cause. Check standard file permissions, firewall rules, or service misconfiguration instead.
Once confirmed, set SELinux back to enforcing before making any fixes:
setenforce 1
Never leave SELinux in permissive mode on a production system. Fix the actual issue instead.
Step 1: Find the Denial in the Audit Log
Every SELinux denial is logged in /var/log/audit/audit.log. Use ausearch to find recent AVC (Access Vector Cache) denials:
# Show denials from the last 10 minutes
ausearch -m avc -ts recent
# Show all denials from today
ausearch -m avc -ts today
A typical denial looks like this:
type=AVC msg=audit(1234567890.123:456): avc: denied { read } for
pid=1234 comm="httpd" name="index.html"
scontext=system_u:system_r:httpd_t:s0
tcontext=unconfined_u:object_r:user_home_t:s0
tclass=file permissive=0
How to read this output:
comm="httpd": the process that was denied (Apache web server)- This tells you which service is having the problem
denied { read }: the action that was blocked- Other common actions: write, execute, open, getattr
scontext=httpd_t: the SELinux context of the process making the request- This is the subject: who is trying to do something
tcontext=user_home_t: the SELinux context of the target file- This is the object: what is being accessed
- Apache expects to read files labeled
httpd_sys_content_t, notuser_home_t
permissive=0: SELinux is in enforcing mode and actively blocked this action
The problem in this example is clear: the file has the wrong label. Apache is trying to read it but the context does not match what Apache is allowed to access.
Step 2: Get a Human-Readable Explanation
The sealert tool translates audit log entries into plain language and suggests fixes. Install it if it is not already present:
dnf install setroubleshoot-server -y
Then run it against the audit log:
sealert -a /var/log/audit/audit.log
What sealert gives you:
- A plain-language description of what was denied and why
- A confidence-rated list of suggested fixes (the top suggestion is usually correct)
- The exact commands to run to apply each fix
- An explanation of what each fix does
Read the suggestions carefully. Understand what a fix does before running it. The top suggestion is usually right but not always.
Step 3: Identify Which Fix Applies
SELinux permission denied errors have 3 common causes. Each has a different fix.
Cause 1: Wrong File Context (most common)
The file or directory has the wrong SELinux label. This happens when:
- You create files in a non-standard location
- You copy files from another directory without preserving context
- You move content that had a different original context
- A filesystem relabel reverted a context change made with
chcon
Check the current context of a file:
ls -lZ /var/www/html/index.html
If the context shows anything other than httpd_sys_content_t for a web file, it needs relabeling.
Fix A: Restore the default context (for standard locations)
# Restore context based on built-in file location rules
restorecon -Rv /var/www/html/
# Verify the context is now correct
ls -lZ /var/www/html/index.html
Fix B: Set a custom context that persists after relabel (for non-standard locations)
# Step 1: Add a persistent policy rule for the custom directory
semanage fcontext -a -t httpd_sys_content_t "/webdata(/.*)?"
# Step 2: Apply the rule to existing files
restorecon -Rv /webdata
# Step 3: Verify
ls -lZ /webdata
Important distinction:
- Use Fix A when files are in a standard location like
/var/www/html - Use Fix B when files are in a custom location like
/webdataor/srv/web- Using only
restoreconwithoutsemanage fcontexton a non-standard path will work until the next system relabel, then revert - Always use both commands together for a permanent fix
- Using only
Cause 2: SELinux Boolean Is Disabled
Booleans are on/off switches that control what a service is permitted to do beyond its default policy. Many behaviors are disabled by default for security reasons.
List all booleans related to a service:
getsebool -a | grep httpd
Common booleans you will encounter:
- Apache (httpd):
httpd_can_network_connect: allow Apache to make outbound network connectionshttpd_can_network_connect_db: allow Apache to connect to databaseshttpd_use_nfs: allow Apache to serve files from NFS mountshttpd_use_cifs: allow Apache to serve files from Samba/CIFS mountshttpd_enable_homedirs: allow Apache to access user home directories
- FTP (ftpd):
ftpd_anon_write: allow anonymous FTP write accessftpd_use_passive_mode: allow passive mode FTP connections
- Samba:
samba_enable_home_dirs: allow Samba to share home directoriessamba_export_all_rw: allow Samba read/write access to all directories
Fix: Enable the boolean persistently
# The -P flag makes the change persist across reboots
setsebool -P httpd_can_network_connect on
# Verify the boolean is now on
getsebool httpd_can_network_connect
Without -P, the boolean resets after reboot. Always use -P for permanent fixes.
Cause 3: Non-Standard Port
If a service is configured to listen on a port that SELinux does not associate with that service type, it will be blocked from binding to that port.
Check which ports SELinux currently allows for a service:
# List all ports allowed for HTTP
semanage port -l | grep http
# List all allowed ports for SSH
semanage port -l | grep ssh
If your Apache is configured to run on port 8090 and that port is not listed under http_port_t, SELinux will block it from starting.
Fix: Add the port to the SELinux allowed list
# Add port 8090 as an allowed HTTP port
semanage port -a -t http_port_t -p tcp 8090
# Verify the port now appears in the list
semanage port -l | grep http
Things to check before applying this fix:
- Confirm the port is actually intentional and not a misconfiguration
- Make sure the port is not already assigned to another service type
- Run
semanage port -l | grep 8090to check - If it belongs to another type, use
-m(modify) instead of-a(add)
- Run
- Also open the port in firewalld if external access is needed
Step 4: Test and Verify
After applying a fix, always test in enforcing mode. Never test in permissive mode and assume the fix worked.
# Confirm enforcing mode is active
getenforce
# Restart the affected service
systemctl restart httpd
# Check service status
systemctl status httpd
# Check for any new denials
ausearch -m avc -ts recent
How to interpret the results:
- No new AVC denials and service runs correctly: the fix worked
- New AVC denials appear: there is a second issue. Read the new denial and repeat the process.
- Service fails but no AVC denials: SELinux is no longer the cause. Check service logs with
journalctl -xe
Quick Reference: SELinux Troubleshooting Commands
- Check SELinux mode:
getenforce: shows Enforcing, Permissive, or Disabledsestatus: shows full SELinux status including policy and mode
- Find denials:
ausearch -m avc -ts recent: last 10 minutesausearch -m avc -ts today: everything todaysealert -a /var/log/audit/audit.log: human-readable with fix suggestions
- File context:
ls -lZ /path/to/file: check current contextrestorecon -Rv /path/: restore default contextsemanage fcontext -a -t type_t "/path(/.*)?": add persistent custom context rulesemanage fcontext -l | grep /path: check existing context rules for a path
- Booleans:
getsebool -a | grep service: list all booleans for a servicesetsebool -P boolean_name on: enable boolean persistentlysetsebool -P boolean_name off: disable boolean persistently
- Ports:
semanage port -l | grep service: list allowed ports for a servicesemanage port -a -t port_type -p tcp PORT: add a new allowed portsemanage port -m -t port_type -p tcp PORT: modify an existing port assignment
Full command reference for copy-paste use:
# Check mode
getenforce
sestatus
# Find denials
ausearch -m avc -ts recent
sealert -a /var/log/audit/audit.log
# File context
ls -lZ /path/to/file
restorecon -Rv /path/to/directory
semanage fcontext -a -t type_t "/path(/.*)?"
restorecon -Rv /path
# Booleans
getsebool -a | grep service_name
setsebool -P boolean_name on
# Ports
semanage port -l | grep service
semanage port -a -t port_type -p tcp PORT
What Not to Do
- Do not disable SELinux
- Setting
SELINUX=disabledin/etc/selinux/configremoves a critical security layer - It is never the right fix for a production system
- On the RHCSA exam, any task that requires SELinux enforcing mode will be marked wrong if you disabled it
- Setting
- Do not use permissive mode as a permanent solution
- Permissive mode is a diagnostic tool only
- Find the denial, apply the proper fix, return to enforcing
- A system running in permissive mode is not protected by SELinux
- Do not use
chconfor permanent fixeschconchanges the context of a file directly but the change is wiped during a filesystem relabel- Use
semanage fcontextfollowed byrestoreconfor permanent fixes chconis only useful for temporary testing to confirm a context change will fix the issue
SELinux and the RHCSA Exam
SELinux is one of the heaviest topics on the RHCSA and the one that fails the most candidates. What the exam tests:
- Diagnosing a denial using
ausearchand identifying the cause - Applying persistent context changes with
semanage fcontextandrestorecon - Enabling booleans persistently with
setsebool -P - Adding non-standard ports to the SELinux allowed list
- Completing all of the above with SELinux in enforcing mode throughout
Candidates who only know how to turn SELinux off will fail those tasks. The exam expects you to fix the actual issue, not work around it.
If you want to practice SELinux troubleshooting on a real RHEL 9 environment, LinuxCert.Guru has dedicated SELinux labs covering basics, policy management, and troubleshooting with auto-graded tasks and instant feedback.
Conclusion
SELinux permission denied errors always have a specific cause. The fix is never to disable SELinux. Here is the complete process:
- Confirm SELinux is the cause by testing in permissive mode, then returning to enforcing
- Find the denial using
ausearch -m avc -ts recent - Get a plain-language explanation with
sealert -a /var/log/audit/audit.log - Identify the cause:
- Wrong file context: fix with
semanage fcontextandrestorecon - Disabled boolean: fix with
setsebool -P - Blocked port: fix with
semanage port -a
- Wrong file context: fix with
- Verify in enforcing mode by restarting the service and checking for new denials
Follow this process every time and SELinux stops being a source of frustration and becomes something you can troubleshoot in under 5 minutes.
Practice SELinux troubleshooting on real hands-on lab at LinuxCert.Guru → https://linuxcert.guru