SELinux: Mandatory Access Control
- Explain what SELinux adds on top of file permissions, and read its three modes
- Decode a security context and find the part that matters for troubleshooting
- Fix the most common denial — a wrong file context — with restorecon and semanage
- Flip SELinux booleans and read the audit log to diagnose a denial
Why SELinux exists
Understand what MAC adds on top of ordinary permissions, and why.
Ordinary Linux permissions are discretionary (DAC): the owner of a file decides who may read or write it. That's fine until a service is compromised — a hacked web server runs as the
apacheuser, and DAC happily letsapacheread anythingapacheis allowed to, including places it has no business touching.SELinux adds mandatory access control (MAC): a system-wide policy, set by the administrator, that the kernel enforces no matter what the file permissions say. Think of it as a building with a security policy, not just door keys. Your key (DAC) might fit a door, but the badge system (SELinux) only opens it if policy says your role is allowed into that room. A hijacked web server has the
httpd_tbadge — policy lets it servehttpd_sys_content_tfiles and nothing else, so it physically cannot read/etc/shadoweven though it tried.Modes: enforcing, permissive, disabled
Read and switch the enforcement mode — and use permissive to debug.
SELinux runs in one of three modes:
- Enforcing — policy is applied: violations are denied and logged.
- Permissive — policy is logged but not enforced (everything is allowed). The debugging mode: see what would be blocked without breaking the service.
- Disabled — SELinux is off entirely (and stops labelling files).
Example — not runnable on your lab box
getenforce; sestatusEnforcing SELinux status: enabled Current mode: enforcing Mode from config file: enforcing Policy from config file: targeted
getenforcegives the one-word current mode;sestatusshows both the current mode and the config-file mode (which applies at next boot) — they can differ if someone ransetenforce.Example — not runnable on your lab box
sudo setenforce 0- 0
- Permissive — log violations but allow them (runtime only).
- 1
- Enforcing — deny + log (runtime only).
Success is silent (Unix's no-news-is-good-news again — verify with getenforce). setenforceflips between enforcing(1) and permissive(0) immediately but only until reboot. The persistent setting lives in/etc/selinux/config(SELINUX=enforcing). You cannot reachdisabledwithsetenforce— that needs the config file + a reboot.Check yourself
Which mode logs SELinux violations but still allows the action — ideal for diagnosing a denial?
Contexts: the label on everything
Read a security context and zero in on the type.
SELinux decisions are made on labels, called contexts. Every file, process, and port has one, written as four colon-separated fields:
user:role:type:level— e.g.system_u:object_r:httpd_sys_content_t:s0For day-to-day troubleshooting, only the type matters (the third field, the one ending in
_t). Policy is essentially a big table of "may a process of type X access a file of type Y?" Match the types and access flows; mismatch them and you get a denial.Example — not runnable on your lab box
ls -Z /var/www/html/index.html; ps -eZ | grep httpd | head -1unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html system_u:system_r:httpd_t:s0 1234 ? 00:00:00 httpd
Add `-Z` to almost any tool to see contexts ( ls -Z,ps -Z,id -Z). Here the file is typehttpd_sys_content_tand the web server process is typehttpd_t— policy allowshttpd_tto readhttpd_sys_content_t, so the page serves. If that file wereuser_home_t(e.g. youmvd it from your home dir),httpd_tcouldn't read it → denial.Check yourself
In the context system_u:object_r:httpd_sys_content_t:s0, which field do you almost always care about for troubleshooting?
Fix the #1 problem: a wrong file context
Repair contexts the right way — restorecon, chcon, and semanage fcontext.
The most common RHCSA SELinux task: a file has the wrong type (you copied or moved it, so it kept its old label) and a service can't reach it. There are three tools, and choosing the right one is the lesson.
Example — not runnable on your lab box
sudo restorecon -Rv /var/www/html/Relabeled /var/www/html/index.html from unconfined_u:object_r:user_home_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0
- -R
- Recurse into the directory.
- -v
- Verbose — print each file whose label it changed.
restoreconresets a file to the default context policy says it should have, based on its path. This fixes the overwhelming majority of denials in one command. It works because the system has a database mapping paths → expected types.Example — not runnable on your lab box
sudo semanage fcontext -a -t httpd_sys_content_t "/srv/website(/.*)?"; sudo restorecon -Rv /srv/websiteRelabeled /srv/website/index.html from unconfined_u:object_r:var_t:s0 to unconfined_u:object_r:httpd_sys_content_t:s0
- -a
- Add a new file-context rule.
- -t
- The type to assign.
- "/srv/website(/.*)?"
- A regex matching the dir and everything under it.
This is the durable pattern for a custom location (e.g. serving from /srv/websiteinstead of/var/www):semanage fcontext -awrites the rule into the default-context database, thenrestoreconapplies it. Now the labels survive reboots and relabels. Remember the two steps —semanagerecords the rule,restoreconmakes it take effect on disk.Booleans: policy on/off switches
Toggle a documented behaviour without writing policy.
Sometimes the labels are right but policy still forbids an action — e.g. "may the web server open outbound network connections?" Rather than make you write policy, SELinux ships booleans: named on/off switches for common, pre-approved exceptions.
Example — not runnable on your lab box
getsebool -a | grep httpd_can_networkhttpd_can_network_connect --> off httpd_can_network_connect_db --> off
getsebool -alists every boolean; grep for your service. Here a web app that proxies or talks to a remote DB is being denied becausehttpd_can_network_connectis off — a classic "works in permissive, fails in enforcing" symptom.Example — not runnable on your lab box
sudo setsebool -P httpd_can_network_connect on- -P
- Persistent — write the change so it survives a reboot.
- (no -P)
- Runtime only — flips now but reverts on reboot.
Silent on success — verify with getsebool httpd_can_network_connect. Flip the switch on. The `-P` is the part everyone forgets: without it the boolean reverts at the next boot and the service mysteriously breaks again. On the exam, almost always use -P.Check yourself
Which flag makes a setsebool change survive a reboot?
Diagnose denials: the audit log
Find out exactly what SELinux blocked, and why.
Every denial is logged as an AVC (Access Vector Cache) message in the audit log. You don't guess — you read what was denied, then fix the matching label, boolean, or port.
Example — not runnable on your lab box
sudo ausearch -m avc -ts recenttype=AVC msg=audit(...): avc: denied { read } for pid=1234 comm="httpd" name="index.html" scontext=system_u:system_r:httpd_t:s0 tcontext=unconfined_u:object_r:user_home_t:s0 tclass=fileRead it like a sentence: process httpd_twas denied read on a file labelleduser_home_t. That's the whole story — the file has the wrong type;restorecon(orsemanage fcontext) fixes it.scontext= the actor,tcontext= the target.Example — not runnable on your lab box
sudo sealert -a /var/log/audit/audit.logSELinux is preventing httpd from read access on the file index.html. ***** Plugin restorecon (94.8 confidence) suggests ***** If you want to fix the label, /var/www/html/index.html default label should be httpd_sys_content_t. Do: /sbin/restorecon -v /var/www/html/index.html
setroubleshoot'ssealertreads the same denials and prints plain-English suggestions (often the exactsetsebool/semanage/restoreconto run). Great for learning — but verify its advice; it sometimes suggests generating a custom policy module when a simple boolean would do.Now do it — read the second lock on your live system
Inspect mode, contexts, and booleans — the reads that begin every SELinux diagnosis
Your sandbox runs real SELinux. This practice is the diagnostic half of the skill — the reads you run the moment you suspect the second lock. For each one, say what you found out loud: the mode, the types you saw, which booleans are off. Being able to NARRATE a system's SELinux state is exactly what the fix half builds on.
getenforce; sestatus | head -4Confirm the mode first, always — a denial diagnosis means nothing until you know whether policy is being enforced or merely logged. On a graded box this is usually Enforcing: the state your fixes must work under (a fix that only works in permissive isn't a fix). ls -Z /etc/passwd /var/www/html 2>/dev/null; id -ZReal contexts on real files: note how the type (_t field) differs by location — passwd_file_t versus httpd content — and read your own login context with id -Z. The -Z habit is the whole diagnostic trick: almost any inspection tool grows a context column when you ask. getsebool -a | grep -E 'httpd|ftpd' | head; sudo restorecon -Rv /var/www/html 2>/dev/nullThe two everyday verbs, rehearsed: getsebool -a | grep for the service's switches (any 'off' near your symptom is a suspect), and a restorecon that no-ops if labels are already correct — proving the command is safe to run on a hunch, which is exactly how you'll use it.
- SELinux is MAC layered on top of DAC: even with correct permissions, the kernel enforces a label-based policy. The type (_t) field is what matters.
- Modes: enforcing (deny+log), permissive (log only — the debug mode), disabled (off, owes a relabel). setenforce is runtime; /etc/selinux/config is persistent.
- Most denials are a wrong file context: restorecon resets to the policy default; semanage fcontext + restorecon makes a custom location permanent; chcon is temporary.
- Booleans toggle pre-approved behaviours — always setsebool -P. Read denials with ausearch -m avc / sealert; custom ports need semanage port.
| Symptom | Tool | Command |
|---|---|---|
| Service blocked, suspect SELinux | permissive test | setenforce 0 (then fix + setenforce 1) |
| Wrong file label | restorecon | restorecon -Rv /path |
| Custom location, must persist | semanage fcontext | semanage fcontext -a -t TYPE "/path(/.*)?" + restorecon |
| Action denied (e.g. network) | boolean | setsebool -P bool on |
| Service on custom port | semanage port | semanage port -a -t TYPE -p tcp PORT |
| What was blocked? | audit log | ausearch -m avc -ts recent |
