Signal9Signal9 — home
online
all lessons

SELinux: Mandatory Access Control

Intermediate40 min7 steps
What you'll learn
  • 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
  1. 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 apache user, and DAC happily lets apache read anything apache is 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_t badge — policy lets it serve httpd_sys_content_t files and nothing else, so it physically cannot read /etc/shadow even though it tried.

  2. 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; sestatus
    Enforcing
    SELinux status:                 enabled
    Current mode:                   enforcing
    Mode from config file:          enforcing
    Policy from config file:        targeted
    getenforce gives the one-word current mode; sestatus shows both the current mode and the config-file mode (which applies at next boot) — they can differ if someone ran setenforce.

    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). setenforce flips between enforcing(1) and permissive(0) immediately but only until reboot. The persistent setting lives in /etc/selinux/config (SELINUX=enforcing). You cannot reach disabled with setenforce — that needs the config file + a reboot.

    Check yourself

    Which mode logs SELinux violations but still allows the action — ideal for diagnosing a denial?

  3. 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:s0

    For 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 -1
    unconfined_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 type httpd_sys_content_t and the web server process is type httpd_t — policy allows httpd_t to read httpd_sys_content_t, so the page serves. If that file were user_home_t (e.g. you mvd it from your home dir), httpd_t couldn'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?

  4. 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.
    restorecon resets 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/website
    Relabeled /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/website instead of /var/www): semanage fcontext -a writes the rule into the default-context database, then restorecon applies it. Now the labels survive reboots and relabels. Remember the two steps — semanage records the rule, restorecon makes it take effect on disk.
  5. 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_network
    httpd_can_network_connect --> off
    httpd_can_network_connect_db --> off
    getsebool -a lists every boolean; grep for your service. Here a web app that proxies or talks to a remote DB is being denied because httpd_can_network_connect is 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?

  6. 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 recent
    type=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=file
    Read it like a sentence: process httpd_t was denied read on a file labelled user_home_t. That's the whole story — the file has the wrong type; restorecon (or semanage fcontext) fixes it. scontext = the actor, tcontext = the target.

    Example — not runnable on your lab box

    sudo sealert -a /var/log/audit/audit.log
    SELinux 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's sealert reads the same denials and prints plain-English suggestions (often the exact setsebool/semanage/restorecon to run). Great for learning — but verify its advice; it sometimes suggests generating a custom policy module when a simple boolean would do.
  7. 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 -4
    Confirm 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 -Z
    Real 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/null
    The 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.
Recap
  • 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 → fix
SymptomToolCommand
Service blocked, suspect SELinuxpermissive testsetenforce 0 (then fix + setenforce 1)
Wrong file labelrestoreconrestorecon -Rv /path
Custom location, must persistsemanage fcontextsemanage fcontext -a -t TYPE "/path(/.*)?" + restorecon
Action denied (e.g. network)booleansetsebool -P bool on
Service on custom portsemanage portsemanage port -a -t TYPE -p tcp PORT
What was blocked?audit logausearch -m avc -ts recent