Skip to content
ohsudo, Home
Linux

SELinux "avc: denied": read the denial, then fix the label, the boolean or the port

Fix SELinux "avc: denied" on RHEL, Rocky and AlmaLinux: read the AVC line, then fix file labels with restorecon and semanage, booleans with setsebool, or ports.

Intermediate4 min readApplies to RHEL 8/9/10 · Rocky Linux · AlmaLinux · Fedora · CentOS Stream

by OhSudo ··

type=AVC msg=audit(1762941600.123:456): avc: denied { read } for pid=1234 comm="nginx" name="index.html" dev="dm-0" ino=67890 scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=file permissive=0

The service gets Permission denied or 403 Forbidden, even though the file permissions are fine, and this line is in the audit log. That’s SELinux: a second set of rules on RHEL and its relatives, on top of the usual permissions. Each process and each file has a label, and the policy says which labels may touch which.

Reading this one line usually tells you the fix. Turning SELinux off is never needed.

  1. nginx (httpd_t)wants to read index.html
  2. File permissionsallow it(works)
  3. SELinux labeluser_home_t: denied(breaks here)
SELinux is a second set of rules on top of the usual permissions: each process and each file has a label, and the policy says which labels may meet.

TL;DR

Terminal window
sudo ausearch -m AVC -ts recent
sudo ls -Z /srv/www/index.html
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/www(/.*)?'
sudo restorecon -Rv /srv/www

That’s for a web server reading files from a new folder. Sections 3 to 5 cover the other two common cases.

1. Read the denial

Find recent denials:

Terminal window
sudo ausearch -m AVC -ts recent

Each part of the line answers a question:

Part Here Means
{ read } read What it tried to do
comm="nginx" nginx Which program
name="index.html" index.html Which file, port or object
scontext=...:httpd_t:... httpd_t The program’s label, or domain
tcontext=...:user_home_t:... user_home_t The target’s label
tclass=file file What kind of object

So, nginx (httpd_t) may not read a file labeled user_home_t. The file came from someone’s home folder, and was moved, keeping its old label.

2. Ask for an explanation

Terminal window
sudo ausearch -m AVC -ts recent | audit2why

audit2why says which kind of problem this is: a wrong label, or a boolean that’s off, and names the boolean. On a machine with setroubleshoot-server installed, sealert gives a fuller report, with commands:

Terminal window
sudo sealert -a /var/log/audit/audit.log

Both come from the policycoreutils-python-utils and setroubleshoot-server packages.

3. A wrong file label

This is the most common case. See the label:

Terminal window
ls -Z /srv/www/index.html
unconfined_u:object_r:user_home_t:s0 /srv/www/index.html

mv keeps a file’s label, and cp gives it the label of where it lands. That’s why a moved file fails, and a copied one works. Reset labels to what the policy expects for that place:

Terminal window
sudo restorecon -Rv /var/www/html

For a folder the policy doesn’t know about, like /srv/www, tell it first, then apply:

Terminal window
sudo semanage fcontext -a -t httpd_sys_content_t '/srv/www(/.*)?'
sudo restorecon -Rv /srv/www

Use httpd_sys_rw_content_t for folders the web app must write to, such as uploads. Avoid chcon: its changes are lost the next time anything runs restorecon, while semanage fcontext is permanent.

4. A boolean that’s off

Booleans are switches for common needs. A web server proxying to an app, which is the cause of many 502 errors on RHEL, needs this:

Terminal window
getsebool httpd_can_network_connect
sudo setsebool -P httpd_can_network_connect on

-P keeps it across reboots. Others you’ll meet: httpd_can_network_connect_db for a web app reaching a database, httpd_enable_homedirs for ~/public_html, and httpd_use_nfs for content on NFS. List them all with getsebool -a.

5. A port that’s not allowed

Each service may only listen on ports labeled for it. Nginx on port 8081 fails to start, and the denial says name_bind with src=8081. See the labeled ports, then add yours:

Terminal window
sudo semanage port -l | grep -w http_port_t
sudo semanage port -a -t http_port_t -p tcp 8081

The same goes for SSH on another port, with ssh_port_t. The Port Lookup shows what each port is usually for.

6. Test, without turning it off

To check whether SELinux is really the cause, switch it to permissive for a moment: it logs, and doesn’t block.

Terminal window
getenforce
sudo setenforce 0
# test
sudo setenforce 1

If it works in permissive mode, the fix is one of the above. Turn enforcing back on right away. It resets to the config file’s setting on reboot anyway.

If none of the cases fit, audit2allow can write a small custom policy from the denials. Read what it proposes: it allows exactly what was denied, which may be more than you want.

Warning

Don’t set SELINUX=disabled, as many guides suggest. It removes a layer that contains a compromised service, and turning it back on later means relabeling the whole disk at boot. Fix the label, boolean or port, which takes one command.

Shell, we have a problem!

Similar, but different: a plain Permission denied with no AVC line is ordinary permissions: see Nginx 403 Forbidden. Job for service failed often hides an AVC denial, so check ausearch when a service won’t start. No route to host on RHEL is usually firewalld, not SELinux.

Found this useful? Share it.

Found a mistake? Let us know at contact@ohsudo.com.

Tools

Try it yourself

Articles

Related articles