Intermediate · SELinux and SSH · topic 32 of 57 · 30 min
Troubleshooting SELinux denials
Explanation
Is it SELinux at all?
Before blaming SELinux, check the usual suspects: is the service running, are normal permissions right, is the firewall open, does the app log say anything? If all that is fine and you still get "Permission denied", look for denials. A quick test, only for a few minutes on a lab or with approval: setenforce 0; if the problem disappears, it was SELinux. Then setenforce 1 and fix it properly.
Reading an AVC denial
# ausearch -m AVC -ts recent
----
time->Mon Oct 19 14:02:11 2026
type=AVC msg=audit(1729326731.402:812): avc: denied { read } for pid=1301 comm="httpd" name="index.html" dev="vdb1" ino=131 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file permissive=0| Part | Meaning here |
|---|---|
| denied { read } | The action that was blocked |
| comm="httpd" | The program that tried |
| name="index.html" | The file (or path=, port= for other objects) |
| scontext=...httpd_t | Source type: the process's domain |
| tcontext=...default_t | Target type: the file's label |
| tclass=file | Kind of object: file, dir, tcp_socket... |
| permissive=0 | It was really blocked (1 would mean only logged) |
Here the fix is clear: httpd_t should read web content, and the file is default_t. That is a labelling problem: semanage fcontext + restorecon.
Let sealert explain
# dnf install -y setroubleshoot-server
# journalctl -t setroubleshoot --since "10 min ago"
Oct 19 14:02:14 web01 setroubleshoot[5120]: SELinux is preventing /usr/sbin/httpd from read access on the file index.html. For complete SELinux messages run: sealert -l 7d8a3c1e-...
# sealert -l 7d8a3c1e-...
SELinux is preventing /usr/sbin/httpd from read access on the file index.html.
***** Plugin restorecon (92.2 confidence) suggests ************************
If you want to fix the label.
/web/index.html default label should be httpd_sys_content_t.
Then you can run restorecon.
Do
# /sbin/restorecon -v /web/index.html
...
# sealert -a /var/log/audit/audit.log # analyse all denials in the logsealert ranks suggestions by confidence. The top one is usually right, but read them all with judgement. If it suggests a boolean, check what the boolean allows before turning it on.
Choosing the fix
| Denial looks like | Usual fix |
|---|---|
| Service reading/writing files with a wrong type (default_t, user_home_t) | semanage fcontext -a -t <right_type> + restorecon |
| Service doing optional things (connect out, read home dirs, use NFS) | setsebool -P <boolean> on |
| name_bind on a port, or "could not bind" | semanage port -a -t <service>_port_t -p tcp <port> |
| Nothing above fits (custom software) | Ask the vendor; as a last resort build a small local policy module (audit2allow), reviewed carefully |
A few more tools
- audit2why < /var/log/audit/audit.log: explains why each denial happened (missing boolean, wrong label, and so on).
- Denials that are expected and harmless may be hidden by "dontaudit" rules. semodule -DB disables them temporarily so you can see everything; semodule -B turns them back on.
- If the audit service is not running, denials go to the journal and dmesg instead.
Common mistakes
- Searching the whole log with grep and reading old, unrelated denials. Reproduce the problem, then use ausearch -ts recent.
- Fixing with setenforce 0 and leaving it.
- Using audit2allow first instead of last.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Troubleshooting SELinux denials · 1/6
First check the service, permissions and firewall. Then look for SELinux denials.
Prefer a video by a teacher? Find free videos on “selinux troubleshooting ausearch sealert audit log tutorial” on YouTube ↗
In real life: A reverse proxy that returned 502 at a SaaS company
A Pune SaaS company put nginx in front of its Node.js API on port 3000. Every request got "502 Bad Gateway". The nginx error log said "connect() to 127.0.0.1:3000 failed (13: Permission denied)". ausearch showed a name_connect denial from httpd_t (nginx runs in that domain) to a port labelled for something else. sealert suggested the boolean httpd_can_network_connect, which the team turned on with -P. SELinux stayed enforcing.
# ausearch -m AVC -ts recent | grep -o 'denied.*tclass=[a-z_]*'
denied { name_connect } for pid=2210 comm="nginx" dest=3000 scontext=system_u:system_r:httpd_t:s0 tcontext=system_u:object_r:ntop_port_t:s0 tclass=tcp_socket
# setsebool -P httpd_can_network_connect onPractice (0/4 done)
Where are SELinux denials logged when auditd is running?
Write parse_avc(line) for an AVC denial line. Return a dict with: action (the word inside { }), comm (without quotes), stype (the third field of scontext), ttype (the third field of tcontext) and tclass. Example line: type=AVC msg=audit(1729326731.402:812): avc: denied { read } for pid=1301 comm="httpd" name="index.html" scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:default_t:s0 tclass=file permissive=0
A denial shows: denied { name_bind } ... comm="httpd" src=8282 ... tcontext=...unreserved_port_t ... tclass=tcp_socket. Best fix?
Create a denial on purpose and fix it using only the tools from this lesson: put index.html in your home directory, mv it into /var/www/html, request it with curl, then find and fix the denial.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
Which command shows SELinux denials from the last few minutes?
Answer: ausearch -m AVC -ts recent
ausearch filters audit records by type and time.
In an AVC line, what is scontext?
Answer: The label of the process that tried the action
Source context = the process.
Which package gives you sealert?
Answer: setroubleshoot-server
setroubleshoot-server provides sealert and the journal summaries.
When is audit2allow the right tool?
Answer: Only after label, boolean and port fixes are ruled out, with the rules reviewed
It can allow too much if used blindly.
Which command shows booleans with descriptions?Related
Answer: semanage boolean -l
semanage boolean -l includes descriptions.
Can setenforce disable SELinux?Related
Answer: No, it only switches between enforcing and permissive
Disabling needs a boot-time change.
A file moved with mv from /home/asha to /var/www/html gives 403. Why?Related
Answer: mv keeps the old user_home_t label
Moved files keep their context; run restorecon.
From Contexts and file labels: ls -Z, semanage fcontext and restorecon
Without /var/log/journal, where does journald keep logs?Related
Answer: /run/log/journal, lost at reboot
With Storage=auto and no /var/log/journal, logs stay in /run (RAM).
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- 1 more topic in “SELinux and SSH” before its exam: SSH: keys, ssh-copy-id and hardening sshd.
- Module 6 exam: SELinux and SSH · 13 questions · 25 min, without help. 5 topics of this module still to finish first.
- Then the next module, Automation basics: scheduling, archives and shell scripts. 57 topics left in the course before the final exam.