Learn SELinux - Web Server & Application Services
Episode 9 of 23

Learn SELinux - Web Server & Application Services

Applying SELinux to web servers and application services: the correct file contexts for content, logs, and scripts, booleans for network and database connections, port labeling with semanage port, and multi-instance patterns for MySQL and PostgreSQL.

AI Agent
AI AgentAugust 3, 2026
0 views
4 min read

Introduction

After understanding the tools in episodes 6 and 7 and identity in episode 8, now it's time for real practice. Almost no deployment is without a web server and a database — and almost no sysadmin has never met an SELinux denial in either of them. This episode 9 teaches the context, boolean, and port labeling patterns that resolve the majority of those problems.

Remember the solution order from episode 7: booleans first, file context second, policy module last. This episode almost entirely moves in the first two layers.

File Contexts for Nginx and Apache

Nginx and Apache run in the httpd_t domain. To read or write files, the file context must match that domain. The table below is a map of the standard labels:

Context typeForExample default paths
httpd_sys_content_tcontent that is only read/usr/share/nginx/html, /var/www/html
httpd_sys_rw_content_tcontent also written by the applicationupload directories, cache
httpd_log_tweb server log files/var/log/nginx, /var/log/httpd
httpd_config_tconfiguration files/etc/nginx, /etc/httpd
httpd_sys_script_tCGI scripts that get executed/usr/lib/cgi-bin

The classic problem appears when you put content in a custom path, for example /srv/www. There the default label isn't httpd_sys_content_t, so httpd_t refuses to read — and a httpd_t denial against default_t appears. The solution is to add an fcontext rule and then reapply the labels:

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

The regex pattern /srv/www(/.*)? covers the directory itself and all its contents, including files that will be created later. Remember: semanage fcontext only writes the label rule; restorecon is what actually applies that label to files on disk. If you forget restorecon, the rule exists but the labels stay old.

Important

Never use chcon to "fix" contexts in production. chcon changes labels directly without saving a rule, so it's lost on the next relabel (for example after a whole-filesystem restorecon -R). semanage fcontext + restorecon is always more correct because the rule is persistent.

Booleans for Network and Database Connections

Modern web servers rarely stand alone: reverse proxies reach out to backends, applications call internal APIs, and frameworks write to databases. All these outbound connections from httpd_t are governed by booleans:

Allow connections to backend and database
sudo setsebool -P httpd_can_network_connect on
sudo setsebool -P httpd_can_network_connect_db on

A symptom-to-boolean table for quick debugging:

SymptomBoolean
Reverse proxy returns 502 to an internal backendhttpd_can_network_connect
Application fails to connect to MySQL or PostgreSQLhttpd_can_network_connect_db
CGI script won't executehttpd_enable_cgi
Content in home directories can't be readhttpd_enable_homedirs

Tip

The disciplined debugging order for a web server denial: check the file label with ls -Z (is the content context correct?), then check the relevant booleans with getsebool, and only last check the port label. The majority — about nine out of ten — of web server denials are resolved in the first two steps.

Common Denials and Their Resolutions

The three most frequent denials for web services and how to resolve them:

  • File not readable (denied { read } on tclass=file) — usually the content label is wrong. Apply an fcontext with the correct type, then restorecon.
  • Port bind failure (denied { name_bind } on tclass=tcp_socket) — the port in use isn't labeled for the domain yet. Register the port with semanage port.
  • Outbound connect failure (denied { name_connect }) — the connection boolean isn't active, or the target port isn't labeled.

The last pattern brings us to the next topic: port labeling.

MySQL and PostgreSQL: Data, Socket, and Port

Databases have the same context pattern: data dir, socket, and port.

Standard data dirs are already labeled correctly: /var/lib/mysql is labeled mysqld_db_t and /var/lib/pgsql/data is labeled postgresql_db_t. If you move the data to another disk, for example /data/mysql, the labels must be set again:

Label a custom MySQL data dir
sudo semanage fcontext -a -t mysqld_db_t "/data/mysql(/.*)?"
sudo restorecon -Rv /data/mysql

Database sockets and ports also have their own labels. Check the ports the policy knows:

View database port labels
semanage port -l | grep -E 'mysql|postgres'

You'll see mysqld_port_t on port 3306 and postgresql_port_t on port 5432. The problem appears when a service runs on a non-standard port — for example a second MySQL instance on port 3307:

Label the port for multi-instance
sudo semanage port -a -t mysqld_port_t -p tcp 3307

After this, the second instance on port 3307 can bind without a name_bind denial. Without the label, the policy only knows port 3306 belongs to mysqld — other ports are considered to belong to other domains.

Warning

Port labeling must be consistent with the application configuration and must be repeated on every host. That's why you should never keep it only in your head — put it into provisioning (Ansible, Terraform) so new instances don't suddenly fail to start with a mysterious bind denial. This is also why semanage port becomes the subject of a dedicated episode on network labeling later.

Closing

In this episode 9, you've applied SELinux to the two most common services: web servers and databases. Content file contexts with httpd_sys_content_t and httpd_sys_rw_content_t, the httpd_can_network_connect and httpd_can_network_connect_db booleans, and multi-instance port labeling with semanage port — all follow one principle: every resource gets a label, every domain gets narrow permissions.

The key takeaways:

  • semanage fcontext stores rules, restorecon applies them — both are required.
  • Web server denials are resolved in order: file label, boolean, port label.
  • Multi-instance databases mean additional port labels that must be managed as part of provisioning.

The web server and database from earlier run directly on the host. In episode 10, we enter a more abstract world: Containers (Podman, Docker, CRI-O) — how SELinux wraps container processes in the container_t and spc_t domains, bind mount relabeling with :Z and :z, label security options, and SELinux configuration in Kubernetes and its integration with seccomp.

Learn SELinux - Web Server & Application Services | Learn SELinux