SELinux, part one.
reading denials and fixing file contexts.
httpd serves files from /srv/webcontent but returns 403 on every request. Disabling SELinux fixes it, but you are not going to do that. Diagnose the denial and fix the file context properly.
webadmin stood up the web tier on server1, pointed httpd at /srv/webcontent, the same SGID directory from the shared-folder ticket back on day three, and every request comes back the same way: 403 Forbidden. Not some of them. All of them. The files are there, httpd is listening, and the permissions are fine. The one thing that makes it work is setenforce 0, which is exactly the thing you are not going to leave in place.
That last detail is the whole diagnosis. If turning SELinux off fixes it, SELinux is doing its job and refusing httpd access to files carrying the wrong label. Anything you create under /srv lands with a generic default_t type, and httpd is only allowed to read httpd_sys_content_t. So this is not a permissions bug or an httpd bug. It is a label, and the real work is relabeling those files in a way a later restorecon will not silently undo.
Confirm the ground you are standing on
getenforce # Enforcing systemctl is-active httpd # active ls -Z /srv/webcontent 2>/dev/null
The three modes and the one field that matters
man selinux man restorecon
There are three modes. Enforcing blocks and logs. Permissive blocks nothing but still logs, which is why it is the mode you diagnose in. Disabled is off and needs a reboot to change, so leave it alone. Every file carries a context of four colon-separated fields, and on this exam the only one you touch is the third, the type.
system_u:object_r:httpd_sys_content_t:s0
^^^^^^^^ ^^^^^^^^^^^^^^^^^^^
role type <- the field that decides access
httpd_sys_content_t httpd may read it
httpd_sys_rw_content_t httpd may write it
default_t what /srv gets, and httpd cannot read itWatch the 403 happen
Point httpd at the directory and fetch it. The permissions are wide open (Require all granted), so when this still returns 403, permissions are ruled out and the label is the only suspect left.
sudo tee /etc/httpd/conf.d/server1.conf << 'EOF'
DocumentRoot "/srv/webcontent"
<Directory "/srv/webcontent">
Require all granted
</Directory>
EOF
sudo systemctl restart httpd
curl -I http://localhost/
# HTTP/1.1 403 Forbidden
# Server: Apache/2.4.62 (Red Hat Enterprise Linux)Read the denial, then read the labels
The denial is written to the audit log in plain sight. It names httpd as the source, the file it wanted, and the type it choked on. Then put your file’s label next to the default web root’s label and the mismatch is right there.
sudo ausearch -m AVC -ts recent | tail -3
# type=AVC msg=audit(...): avc: denied { getattr } for pid=1893
# comm="httpd" path="/srv/webcontent/index.html"
# scontext=system_u:system_r:httpd_t:s0
# tcontext=unconfined_u:object_r:default_t:s0 tclass=file
ls -Z /srv/webcontent/index.html
# unconfined_u:object_r:default_t:s0 <- httpd is refused this
ls -Z /var/www/html/
# system_u:object_r:httpd_sys_content_t:s0 <- this is the targetThere it is: tcontext=…:default_t:s0 in the denial, and default_t on the file. httpd wanted httpd_sys_content_t and got the /srv default instead.
One flip to prove it, then flip back
You do not have to guess. Permissive stops blocking but keeps logging, so if the 403 turns into a 200 the instant you flip it, SELinux was the only thing in the way. Then go straight back to enforcing, because leaving it in permissive is the same cop-out as disabling it.
sudo setenforce 0 curl -I http://localhost/ # HTTP/1.1 200 OK <- same files, same httpd, only SELinux changed sudo setenforce 1
The fix that looks right and then rots
The obvious move is chcon, which stamps a new label straight onto the file. It works immediately, which is the trap. chcon only changes the file, not the policy, so the file’s “correct” label according to SELinux is still default_t. The next restorecon that runs, and one runs on relabels, reboots, and plenty of admin tasks, resets it and the 403 comes back. Watch it happen:
sudo chcon -R -t httpd_sys_content_t /srv/webcontent curl -I http://localhost/ # 200 OK... for now sudo restorecon -Rv /srv/webcontent # Relabeled /srv/webcontent/index.html from # unconfined_u:object_r:httpd_sys_content_t:s0 to ...:default_t:s0 curl -I http://localhost/ # 403 Forbidden again
The fix that survives
The durable move changes the policy, not the file. semanage fcontext -a records a rule that says “anything at this path should be httpd_sys_content_t,” and then restorecon enforces that rule. Now restorecon is on your side: because the policy agrees with the label, every future run keeps it.
sudo dnf install -y policycoreutils-python-utils sudo semanage fcontext -a -t httpd_sys_content_t "/srv/webcontent(/.*)?" sudo restorecon -Rv /srv/webcontent # Relabeled /srv/webcontent/index.html from ...:default_t:s0 to # ...:httpd_sys_content_t:s0 curl -I http://localhost/ # 200 OK, and this time it stays sudo restorecon -Rv /srv/webcontent # run it again: no change, it holds
(/.*)? on the end is the piece people drop, and it is a regex meaning “this directory or anything under it.” Leave it off and you label the directory but not the index.html inside it, so the page still 403s and you spend ten minutes confused.Exam questions
Write the command first.
Q1. Show the current SELinux mode and the mode configured for boot.
Q2. Temporarily switch SELinux to permissive and verify without rebooting.
Q3. A web service cannot read files in /webdir due to SELinux. Fix the context permanently so it survives restorecon and reboots.
Answers.
# A1 getenforce; sestatus; cat /etc/selinux/config # A2 sudo setenforce 0; getenforce # A3 sudo semanage fcontext -a -t httpd_sys_content_t "/webdir(/.*)?" sudo restorecon -Rv /webdir ls -Z /webdir
Final checklist: confirm everything works
If every check passes, the ticket is done.
# 1. 403 reproduced, then confirmed as SELinux via permissive # 2. semanage fcontext + restorecon used, not just chcon sudo semanage fcontext -l | grep webcontent # 3. label survives a fresh restorecon sudo restorecon -Rv /srv/webcontent; ls -Z /srv/webcontent/index.html # 4. enforcing restored and httpd serves 200 OK getenforce; curl -I http://localhost/ # 5. all three exam commands written from scratch # 6. tracker entry checked off
Reply to webadmin: fixed with semanage fcontext -a -t httpd_sys_content_t ‘/srv/webcontent(/.*)?’ then restorecon -R. Permanent, SELinux back to enforcing, curl returns 200.