SELinux policies for RHEL web servers
Configuring SELinux policies for web applications on RHEL is about giving each process a clearly defined role, not bypassing Linux permissions. Australian hosting providers commonly run customer portals on Apache or Nginx in Sydney, Melbourne and Perth, where SELinux’s Mandatory Access Control (MAC) model governs access alongside ordinary file permissions.
The targeted policy shipped with RHEL provides a strong baseline, but custom document roots, non-standard ports and outbound API calls can trigger access vector denials. The practical approach is to inspect labels, record a baseline, make the smallest persistent change, and verify the application under enforcing mode.
Understand what SELinux already controls
Check the current state with getenforce and sestatus: the former reports Enforcing, Permissive or Disabled, while the latter shows the loaded policy and overall boolean status. RHEL’s targeted policy confines major daemons, commonly using httpd_t for both Apache and Nginx. SELinux uses the Linux Security Modules (LSM) framework to enforce these decisions.
A security context combines a SELinux user, role and type, with a category such as s0 on modern systems. The type determines which labels match a service. Inspect files with ls -Z and processes with ps -eZ; chown changes ownership, not the SELinux label.
Keep enforcement active while troubleshooting. Permissive mode records denied actions, but it does not protect the system and should not become the normal operating state.
Set up a repeatable test environment
Use a RHEL 9 virtual machine or a CentOS Stream host before touching production. Install the tooling with sudo dnf install policycoreutils policycoreutils-python-utils setools-console audit, then confirm that semanage, restorecon, sealert and audit2allow are available. The Linux commands reference can support the surrounding command-line work.
Capture existing contexts, port mappings and booleans before editing. Commands such as ls -Z /srv/app, semanage port -l and semanage boolean -l create a useful baseline. Make one change per test cycle, record its time in AEST or AEDT, and reproduce the failed request; this fits the audit expectations of many Australian IT teams.
Label application files for least privilege
Custom content needs a persistent file-context rule rather than a one-off chcon. Check the path and ordinary Linux permissions with namei -l /srv/app/current/public; SELinux works alongside those DAC permissions rather than replacing them.
For a static tree, use sudo semanage fcontext -a -t httpd_sys_content_t '/srv/app(/.*)?', then apply it with sudo restorecon -Rv /srv/app. The semanage rule stores the intended type in local policy, while restorecon changes the filesystem label. Confirm the result with ls -Z and test the actual page.
Do not label an entire tree writable. Reserve httpd_sys_rw_content_t for directories where the service genuinely writes, and httpd_log_t for custom logs where appropriate. Keep keys, credentials and private configuration outside the document root, with restrictive ownership and modes.
Map ports, booleans and service identity
Ports carry separate SELinux labels. Check allocations with sudo semanage port -l | grep http, then map a custom web listener using sudo semanage port -a -t http_port_t -p tcp 8080. If the port already exists, adjust its current type instead of creating a conflicting rule.
Outbound connections are governed by booleans. For example, a PHP application calling an external API may need sudo setsebool -P httpd_can_network_connect 1; using -P makes the change persistent. Review semanage boolean -l | grep httpd and enable only the capability the application actually requires.
Service identity matters too: a daemon running as nginx may need the correct login mapping and init context rather than a label copied from Apache. The Linux and Unix tutorials cover adjacent service-management patterns that complement this policy work.
Diagnose denials without weakening protection
When an app is ‘playing up’ in Brisbane, start with the denial rather than a firewall rule. Query recent access-vector denials with sudo ausearch -m AVC -ts recent, and generate a readable explanation using sudo sealert -a /var/log/audit/audit.log. These records separate SELinux failures from ordinary permission or routing errors.
Check the denial’s source, target, permission and type. If httpd_t cannot write to a directory labelled httpd_sys_content_t, decide whether the write is an approved requirement; granting it by changing the whole tree to writable would be excessive. Refuse access when it is not part of the application’s design.
audit2allow can propose rules, but review every permission before applying it. A permissive-domain test can reveal missing access without making the whole host permissive: use sudo semanage permissive -a httpd only as a controlled diagnostic, then remove it with sudo semanage permissive -d httpd.
Make changes safely and keep a usable record
Treat every policy change like a production change: capture the baseline, explain the type or boolean, test the real request path and record the rollback. The Australian Signals Directorate’s voluntary Cyber Security Baseline gives local IT teams a useful model for deliberate configuration and auditable change. Keep vendor advice distinct from host evidence; the Flat Elements website can provide an external web-project reference, while RHEL documentation remains authoritative for SELinux behaviour.
A short operational checklist should cover:
- Keep the targeted policy enabled, using permissive mode only for a controlled diagnostic.
- Prefer
semanage fcontextandrestoreconoverchcon, which relabelling can overwrite. - Add one narrowly scoped port mapping or boolean, then test the application end to end.
- Save denial output, the selected rule and rollback steps with the change record.
Compare audit timestamps with Australian daylight-saving boundaries: AEST and AEDT can make an event appear later than expected, so confirm the host clock before chasing the wrong incident. The practical takeaway is to preserve confinement—label the smallest required path, allow the exact network action and prove the result under enforcing mode.