CoursesLinux hardeningMandatory access control

SELinux in enforcing, calmly

Read denials, write targeted modules, never disable.

Advanced16 min · lesson 12 of 16

Standard Linux permissions are discretionary — the owner of a file decides who can access it. Mandatory Access Control (MAC) adds a second, non-negotiable layer the kernel enforces regardless of owner or even root: a policy that says exactly which processes may touch which resources. SELinux is the MAC system on RHEL-family distributions, and it is the difference between "a compromised web server can do whatever the web-server user can do" and "a compromised web server can only do what the web-server policy allows, which is almost nothing beyond serving its files."

THE RIGHT REFLEX: READ THE DENIAL
1Access blocked
SELinux logs an AVC denial, no silent failure
2Read the AVC
ausearch -m avc | audit2why
3Legitimate access?
yes -> narrow module; no -> intrusion stopped
4Grant only that access
audit2allow -M / setsebool; stay Enforcing
A single AVC is SELinux working; the fix is a targeted module or boolean, never setenforce 0.
terminal
$ getenforce
Enforcing # the goal state — policy is enforced
$ sestatus # full status: mode, policy, etc.
# SELinux labels every process and file with a "context"; policy is rules between labels:
$ ls -Z /var/www/html/index.html
unconfined_u:object_r:httpd_sys_content_t:s0 index.html # the httpd_sys_content_t type

How SELinux thinks: labels and types

SELinux labels everything — every process and every file gets a context, and the important part is the type. The policy is a set of rules about which process types may access which object types: the httpd process (httpd_t) may read httpd_sys_content_t files, and that is it. It cannot read your SSH keys or /etc/shadow because no rule permits httpd_t to touch those types. So even a fully compromised web server is boxed into the web server’s declared behavior — the confinement holds even against code the attacker fully controls.

The right reflex: read denials, do not disable

When SELinux blocks something, it logs an AVC (Access Vector Cache) denial rather than failing silently, and the correct response is to read that denial and decide whether the access is legitimate. If it is, generate a small targeted policy module that permits exactly that one access; if it is not, SELinux just stopped an intrusion. The tools make this routine: ausearch finds the denials, audit2allow explains and can generate the module. What you never do is reach for setenforce 0.

terminal
$ sudo ausearch -m avc -ts recent # recent SELinux denials
$ sudo ausearch -m avc -ts recent | audit2why # plain-English reason for each
# if the access is legitimate, generate a targeted module (grant ONLY this):
$ sudo ausearch -m avc -ts recent | audit2allow -M my-httpd-fix
$ sudo semodule -i my-httpd-fix.pp
# common booleans toggle whole behaviors without a custom module:
$ sudo setsebool -P httpd_can_network_connect_db on
Never "fix" SELinux by disabling it
The universal wrong answer to an SELinux denial is setenforce 0 or SELINUX=disabled — it makes the immediate problem go away and removes an entire defensive layer for the whole host, permanently. A single denial is SELinux doing its job; the fix is a two-line targeted policy module or a boolean, not turning off confinement for every service. Keep it Enforcing, learn to read AVCs, and treat "disable SELinux" in any runbook as a bug to fix.