RHCSA study materials
RHCSA study resources

SELinux Denials on a Web Application Moved to Port 9090

RHCSA · Put your knowledge to work in context.

All case studies

SELinux Denials on a Web Application Moved to Port 9090

A RHEL 9 web server runs Apache httpd in enforcing mode. The inventory application's content lives in /srv/inventory/www, uploads are written by httpd into /srv/inventory/data (mode 2775, group apache), and httpd was configured to Listen 9090. The application also opens TCP connections to PostgreSQL at 10.20.0.9:5432. The content was copied in with cp -a from /root/project and the port was registered with semanage port -a -t http_port_t -p tcp 9090. In enforcing mode every page returns 403, uploads fail with permission denied in the httpd error log, and the database connection is refused; with setenforce 0 all three work. ausearch -m avc -ts today is not empty. ss -ltnp shows httpd on 0.0.0.0:9090 and /etc/selinux/config still says enforcing.

1. The three symptoms have three different causes. Identify each, name the type or boolean involved, and give the fix.

Read the worked answer

Read the audit log first - it names the wanted type; do not guess.

ausearch -m avc -ts today | audit2why
ls -Zd /srv/inventory/www /srv/inventory/data
  1. 403 on the content. cp -a preserved the source labels, so the files are still user_home_t; httpd_t is not allowed to read that type.
semanage fcontext -a -t httpd_sys_content_t '/srv/inventory/www(/.*)?'
restorecon -Rv /srv/inventory/www
  1. Upload denied. The directory exists but its type is not writable by httpd_t. Give it a read-write web type:
semanage fcontext -a -t httpd_sys_rw_content_t '/srv/inventory/data(/.*)?'
restorecon -Rv /srv/inventory/data
  1. Database connection refused. SELinux blocks httpd from making outbound network connections by default:
setsebool -P httpd_can_network_connect on

-P is essential; without it the change is lost at reboot.

Judgement: symptoms 1 and 2 look identical at the Unix permission level (403 + permission denied) but need different types - which is why the audit record, not the error log, decides the fix. Retest after each change so you know which fix closed which denial.

2. Is the `semanage port` command for 9090 correct and sufficient? What would you check, and what would you do if it had failed with 'Port already defined'?

Read the worked answer

It is the right shape of fix - a non-standard port has to be labelled with a type httpd_t is permitted to bind, and http_port_t is that type. Two things must be verified rather than assumed:

semanage port -l | grep -w 9090            # exactly one entry, type http_port_t
ss -ltnpZ | grep 9090                      # listener + process context
getsebool httpd_can_network_bind           # only needed if a boolean is blocking the bind

If the command had failed with ValueError: Port 9090 already defined, the port is already assigned to some other type. -a only adds; use the modify form:

semanage port -l | grep 9090
semanage port -m -t http_port_t -p tcp 9090

And do not stop at the port: the 403 in the previous task proves that a correctly labelled port plus unreadable content still yields a broken site. The port fix only removes the name_bind denial; it says nothing about file labels or outbound connections. Also confirm that semanage port -a was run as root on the same host - a change made on a test VM and forgotten is a classic false assumption.

3. A colleague suggests `setenforce 0` permanently. Argue against it and give the verification workflow that proves the fixes are real and will survive a full filesystem relabel.

Read the worked answer

setenforce 0 in /etc/selinux/config turns off the mandatory access control that is protecting every other service on the box, and it is not a fix: it converts the three denials into three silent policy violations that reappear the moment anyone re-enables enforcing. It also destroys the evidence you need - audit records stop being generated, so the next admin has nothing to work from. The targeted fixes already exist and cost three commands; nothing about them is risky.

The real weakness people hit is using chcon, which writes a label directly and is wiped by the next relabel. Use semanage fcontext (persistent policy) plus restorecon (apply it).

Verification workflow:

# 1. policy entries exist, not just live labels
semanage fcontext -l | grep /srv/inventory
# 2. apply and confirm
restorecon -Rv /srv/inventory ; ls -Zd /srv/inventory/www /srv/inventory/data
# 3. prove the labels survive a relabel without actually doing one
touch /.autorelabel   # (only if you intend to reboot); otherwise:
restorecon -Rn /srv/inventory     # -n dry run: no changes needed means the policy matches
# 4. no new denials during a real test
ausearch -m avc -ts recent ; curl -sI http://127.0.0.1:9090/ ; # then an upload test
setenforce 1 ; getenforce

Accept the change only when getenforce says Enforcing, the functional tests pass, and ausearch -m avc -ts recent is empty of refusals for httpd_t.