Docs / Security / How to Implement Mandatory Access Control with SELinux

How to Implement Mandatory Access Control with SELinux

By Admin · Mar 15, 2026 · Updated Apr 23, 2026 · 250 views · 3 min read

SELinux (Security-Enhanced Linux) adds a mandatory access control (MAC) layer on top of standard Linux permissions. Even if an attacker compromises a service running as root, SELinux can prevent them from accessing files or network resources outside the service policy.

SELinux vs Standard Permissions

# Standard (DAC): If nginx runs as root, it can read ANY file
# SELinux (MAC): Even as root, nginx can only access files labeled for nginx

# SELinux adds labels (contexts) to every file, process, and port
# Example: ls -Z /var/www/html/
# unconfined_u:object_r:httpd_sys_content_t:s0 index.html
#                       ^^^^^^^^^^^^^^^^^
#                       SELinux type label

SELinux Modes

# Check current mode
getenforce
# Enforcing — Policies are enforced (production mode)
# Permissive — Policies logged but not enforced (testing mode)
# Disabled — SELinux is off

# Temporarily change mode
sudo setenforce 0   # Permissive (for testing)
sudo setenforce 1   # Enforcing

# Permanently set mode in /etc/selinux/config
# SELINUX=enforcing

Common SELinux Commands

# Check SELinux context of files
ls -Z /var/www/html/

# Check SELinux context of processes
ps auxZ | grep nginx

# Check SELinux context of ports
semanage port -l | grep http

# View SELinux booleans (on/off switches for common policies)
getsebool -a | grep httpd

Fixing Common SELinux Issues

# Problem: Nginx cannot serve files from a custom directory
# Solution: Set the correct SELinux context
sudo semanage fcontext -a -t httpd_sys_content_t "/custom/web(/.*)?"
sudo restorecon -Rv /custom/web

# Problem: Nginx cannot connect to a backend on a non-standard port
# Solution: Allow httpd to connect to the network
sudo setsebool -P httpd_can_network_connect 1

# Problem: Nginx cannot bind to a non-standard port
# Solution: Add the port to the http_port_t type
sudo semanage port -a -t http_port_t -p tcp 8080

# Problem: Application cannot write to a directory
sudo semanage fcontext -a -t httpd_sys_rw_content_t "/var/www/app/storage(/.*)?"
sudo restorecon -Rv /var/www/app/storage

Troubleshooting with audit2why

# When SELinux blocks something, it logs to /var/log/audit/audit.log
# Use audit2why to understand why:
sudo ausearch -m AVC -ts recent | audit2why

# Generate a policy module to allow the blocked action
sudo ausearch -m AVC -ts recent | audit2allow -M myapp
sudo semodule -i myapp.pp

# WARNING: Only do this if you understand what is being allowed

SELinux on Ubuntu (AppArmor Alternative)

# Ubuntu uses AppArmor by default instead of SELinux
# AppArmor provides similar MAC but with path-based rules

# Check AppArmor status
sudo aa-status

# View a profile
cat /etc/apparmor.d/usr.sbin.nginx

# Put a profile in complain mode (like SELinux permissive)
sudo aa-complain /usr/sbin/nginx

# Enforce a profile
sudo aa-enforce /usr/sbin/nginx

Best Practices

  1. Never disable SELinux in production — use permissive mode for troubleshooting
  2. Use audit2why before creating custom policies
  3. Use booleans before writing custom modules
  4. Test in permissive mode, then switch to enforcing
  5. Keep SELinux policies updated with your OS

Was this article helpful?