Understanding SELinux: Why Mandatory Access Control is Essential for Securing Linux Web Stacks


2 views

While password protection and standard Linux permissions (user/group/other with rwx bits) form the first layer of defense, they suffer from critical flaws:

  • Privilege escalation risks: Any process running as root has unlimited access
  • Inheritance problems: Child processes inherit parent's permissions
  • Binary decisions: Either full access or no access at all

Consider Apache running as www-data. If compromised, the attacker gains all www-data privileges:


# Traditional Linux permissions allow:
attacker@server:/var/www/html$ rm -rf /  # If www-data has write access

SELinux implements Mandatory Access Control (MAC) where:

  • Every process and object gets a security context
  • Policies define allowed interactions
  • Rules are enforced regardless of user privileges

Example security contexts:


# View Apache's context:
ps -eZ | grep httpd
system_u:system_r:httpd_t:s0

# View web directory context:
ls -Z /var/www/html
system_u:object_r:httpd_sys_content_t:s0 index.html

For your specific stack (SSH+Apache+MySQL+Memcached):

1. Containing Web Server Breaches

Even with a vulnerable WordPress plugin, SELinux prevents:


# Attempted malicious actions would fail:
httpd_t trying to connect to mysql.sock (type mysqld_var_run_t)
httpd_t trying to write to /etc/shadow (type shadow_t)
httpd_t trying to connect to memcached (type memcache_port_t)

2. Database Protection

MySQL runs under mysqld_t domain. SELinux prevents:


# Blocked actions:
mysqld_t trying to read sshd config files (type sshd_config_t)
mysqld_t trying to execute /bin/bash (type shell_exec_t)

3. SSH Hardening

sshd runs under sshd_t domain with strict policies:


# Prevented attacks:
sshd_t trying to access httpd scripts (type httpd_script_exec_t)
sshd_t trying to modify crontabs (type system_cron_spool_t)

Creating custom policies for a web application:


# 1. Generate policy module from denial logs
grep "avc: denied" /var/log/audit/audit.log | audit2allow -M myapp

# 2. Review and customize the generated .te file
# Example: Allow httpd to connect to custom app port
module myapp 1.0;
require {
    type httpd_t;
    type mysqld_port_t;
    class tcp_socket name_connect;
}
allow httpd_t mysqld_port_t:tcp_socket name_connect;

# 3. Compile and load policy
make -f /usr/share/selinux/devel/Makefile myapp.pp
semodule -i myapp.pp

Key tools for SELinux administration:


# Check current mode
getenforce

# View denials
ausearch -m avc -ts recent

# Temporarily change context
chcon -t httpd_sys_content_t /path/to/file

# Fix filesystem labels
restorecon -Rv /var/www

Remember: SELinux isn't about making systems unhackable - it's about limiting blast radius when breaches occur. In modern attack landscapes where perimeter defenses regularly fail, MAC systems like SELinux provide the crucial containment layer.


While standard Linux permissions (user/group/other with rwx bits) and password protection provide basic security, they follow the Discretionary Access Control (DAC) model where processes inherit the privileges of the user who runs them. This creates dangerous scenarios:


# Example of vulnerable DAC scenario:
$ sudo -u apache /bin/bash
# Now the entire Apache process has full shell access

Attackers frequently exploit this by finding vulnerabilities in services (Apache, MySQL, etc.) to gain the service account's privileges - often resulting in full system compromise.

SELinux implements Mandatory Access Control (MAC) where policies define exactly what actions processes can perform, regardless of user privileges. Key components:

  • Labels: Every process/file has security context (user:role:type:level)
  • Policies: Rules governing allowed interactions between contexts
  • Domain Transition: Strict control over privilege escalation

# View SELinux contexts:
$ ls -Z /var/www/html
system_u:object_r:httpd_sys_content_t:s0 index.html

$ ps -eZ | grep httpd
system_u:system_r:httpd_t:s0   1234 ? httpd

Scenario 1: Web Application Exploit


# Without SELinux:
1. Attacker uploads PHP shell via vulnerable web app
2. PHP executes as apache user
3. apache user can read /etc/shadow, modify website files

# With SELinux:
1. PHP script attempts to read /etc/shadow
2. SELinux denies with AVC denial:
type=AVC msg=audit(1625091234.123:456): avc: denied { read } for pid=1234 comm="php" 
name="shadow" dev="sda1" ino=12345 scontext=system_u:system_r:httpd_t:s0 
tcontext=system_u:object_r:shadow_t:s0 tclass=file

Scenario 2: Database Compromise


# Without SELinux:
1. SQL injection in web app
2. Attacker gains mysql shell access
3. Can read arbitrary files via LOAD_FILE()

# With SELinux:
mysqld_t can only access files labeled mysqld_db_t or mysqld_log_t

Apache HTTPD Configuration:


# Ensure proper labeling:
$ semanage fcontext -a -t httpd_sys_content_t "/var/www/html(/.*)?"
$ restorecon -Rv /var/www/html

# Allow non-standard ports:
$ semanage port -a -t http_port_t -p tcp 8080

MySQL Protection:


# Prevent file read operations:
setsebool -P mysql_connect_any_file off

# Verify context for data files:
$ ls -Z /var/lib/mysql
system_u:object_r:mysqld_db_t:s0 ibdata1

When things break (they will):


# View denials:
$ ausearch -m avc -ts recent

# Generate human-readable reports:
$ sealert -a /var/log/audit/audit.log

# Temporarily allow an operation for testing:
$ audit2allow -a -M mypolicy
$ semodule -i mypolicy.pp

The key advantage isn't just preventing known vulnerabilities, but containing damage when the inevitable zero-day occurs. In modern server environments with complex interactions between services, SELinux provides the crucial security layer that traditional Unix permissions simply cannot offer.