SELinux labels, a localhost backend port, and an over-broad firewall rule
An Oracle Linux 8 web server runs Apache with SELinux in enforcing mode. The application was moved from /var/www/html to /srv/app/public, and uploads go to /srv/app/uploads. Files were restored from a tar archive with tar -xpf, and ls -Z now shows unconfined_u:object_r:default_t on most of /srv/app, while old files under /var/www are system_u:object_r:httpd_sys_content_t. Apache returns 403 for static pages, and the app log shows 'Permission denied' when writing uploads. The audit log shows avc: denied { write } ... scontext=httpd_t ... tcontext=default_t and avc: denied { name_connect } ... dest=9200. The app also listens on 8443, which must be reachable only from 10.20.0.0/16. Firewalld is running; the public zone has http and https open, and the admin added 8443/tcp with --add-port, which exposes it to everyone. A colleague suggests setenforce 0 to confirm SELinux is the cause and then leave it permissive.
1. Explain the correct SELinux labeling fix for /srv/app, and why chcon is not the right long-term tool.
Read the worked answer
Labeling. default_t is not a web content type, so httpd_t cannot read the static files and cannot write the upload directory; the 403 and the write denial share that root cause. The persistent fix is policy, not chcon. Install policycoreutils-python-utils for semanage, then define semanage fcontext -a -t httpd_sys_content_t '/srv/app/public(/.*)?' and semanage fcontext -a -t httpd_sys_rw_content_t '/srv/app/uploads(/.*)?'. Also ensure the parent path is traversable by httpd_t; if /srv/app itself is default_t, label it or an appropriate parent so directory search succeeds. Apply with restorecon -Rv /srv/app, then verify with ls -Z and matchpathcon -V /srv/app/public/index.html. chcon changes only the inode and is lost on the next restorecon or filesystem relabel, so it would work until the next backup restore and then fail again. Test with curl -I from the server and one upload; do not use setenforce 0 as the fix. If a denial remains, use ausearch -m avc -ts recent and audit2why, not a blanket permissive mode.
2. The app connects to 127.0.0.1:9200. Decide between enabling httpd_can_network_connect and labeling port 9200, and justify the choice.
Read the worked answer
Port labeling versus boolean. httpd_can_network_connect allows httpd_t to connect to arbitrary network ports, which is broader than this application needs and weakens the containment SELinux provides. The local Elasticsearch service is on TCP 9200, so first check semanage port -l | grep 9200. If the port is unlabeled or labeled unreserved_port_t, add it to a type httpd_t is allowed to reach: semanage port -a -t http_port_t -p tcp 9200 (use -m if it already exists). This permits the connection without opening every outbound port to httpd. Then retry the app and check ausearch -m avc -ts recent. If 9200 is actually a database protocol rather than HTTP, the correct type might be a database port type and a narrower boolean such as httpd_can_network_connect_db; do not choose a boolean by name alone. Only if the service must use a port that cannot be relabeled, or a policy constraint blocks the port type, should you fall back to the boolean. Document the choice and run sealert -a /var/log/audit/audit.log to catch related denials. The key judgement is least privilege: label the specific port before enabling a broad boolean.
3. Fix the firewalld rule so 8443 is restricted to 10.20.0.0/16 and persists, and describe how you would verify without breaking existing 80/443 access.
Read the worked answer
Firewall. The --add-port=8443/tcp rule is correct only for a public service; it cannot express a source restriction. Remove it from the permanent configuration and add a rich rule: firewall-cmd --permanent --zone=public --remove-port=8443/tcp, then firewall-cmd --permanent --zone=public --add-rich-rule='rule family=ipv4 source address=10.20.0.0/16 port port=8443 protocol=tcp accept', then firewall-cmd --reload. Use --list-all and --list-rich-rules to verify, and test from an allowed 10.20.x host and a denied host; do not test only from localhost. If 8443 was added at runtime only, --reload will drop it, so check both runtime and permanent. Apache also has to bind 8443 under SELinux; if the port is not labeled http_port_t, add it with semanage port -a -t http_port_t -p tcp 8443. Keep the 80/443 rules intact and verify with curl and ss -ltnp. Avoid adding the source to the public zone as a shortcut, because that would trust the whole source for every service. The final state should be: public zone allows 80/443 from all, 8443 only from 10.20.0.0/16, and SELinux enforcing.