Unpacking the foundation of the SELinux architecture: Type Enforcement that restricts domains against types, the user:role:type:sensitivity security context format, the role of the Access Vector Cache (AVC), and the kernel LSM layer and userspace tools that work together to enforce the policy.

In episode 1 we understood why SELinux exists: DAC isn't enough to restrict, and MAC was born to restrict everyone — including root — based on a centralized policy and context labels. Now it's time to unpack how SELinux works. This episode will build the basic vocabulary you'll use in every following episode: Type Enforcement, security context, AVC, and the complete architecture from kernel to userspace.
Think of this episode as learning grammar: without mastering these terms and structures, you won't be able to read denial logs, let alone write your own policies.
The heart of SELinux is Type Enforcement (TE) — the mechanism that determines whether a process is allowed to perform a certain access on an object. TE works with two core concepts:
httpd_t for the web server.httpd_sys_content_t for files a web server is allowed to read, or etc_t for configuration files in /etc.The SELinux policy is a set of rules that determines which domain → type pairs may interact, along with what operations (read, write, execute, open a port, and so on). Without an explicit rule allowing it, access is denied — this is the default deny principle that is SELinux's main strength.
The best analogy: the domain is an employee access card, the type is the door label of a room. The web server (httpd_t) may enter the httpd_sys_content_t room, but may not enter the shadow_t room containing password files. Even if the card is held by root — because the rules are held by the policy, not the cardholder.
Every process and object on an SELinux system carries a label called a security context with this format:
user:role:type:sensitivity[:category]A real example we already saw in episode 1:
unconfined_u:object_r:etc_t:s0Let's dissect each component one by one:
unconfined_u — the SELinux user. Not the Linux user, but a user in the policy domain that determines the level of trust. unconfined_u means a regular user without special restrictions.object_r — the role. On objects, the role is almost always object_r; on processes, the role determines which domains may be used.etc_t — the type. This is the part you'll pay attention to most often: the object label that gets matched against TE rules.s0 — sensitivity, the security level. For a targeted policy without MLS, it's almost always s0. The :category part (MCS) is added if you need to separate data further, for example s0:c0,c1.To see contexts on a real system, try ps -eZ for processes and ls -Z for files:
ps -eZ | head -5
ls -Z /etcLABEL PID TTY TIME CMD
system_u:system_r:init_t:s0 1 ? 00:00:02 systemd
system_u:system_r:sshd_t:s0 980 ? 00:00:00 sshd
/etc:
system_u:object_r:etc_t:s0 aliases
system_u:object_r:etc_t:s0 chronyNotice the s0 pattern on every line and the difference between the system_r role (processes) and object_r (files). Getting used to reading this format will help a lot in episode 3 when you read denial logs.
Computing permissions against thousands of policy rules every time a process touches a file would be very slow. That's where the Access Vector Cache (AVC) comes in: SELinux stores decision results in a cache so subsequent access requests can be answered directly without evaluating the policy again.
Every time a decision results in denied, the AVC records it to the audit log — and that log is what's called an AVC denial, like the one you saw in episode 1. The term "AVC denied" you often encounter on forums is the result of this cache's records. By understanding that the AVC is both a performance layer and the source of logs, you'll find it easier to navigate /var/log/audit/audit.log later.
SELinux is built from two worlds running together.
Inside the kernel, SELinux is plugged in as a Linux Security Module (LSM) — a framework that provides hooks at access decision points. Every time a syscall touches an object, the LSM hook is called, the label is checked against the policy, and the result is cached in the AVC. The interface for communicating with the kernel is available through the virtual filesystem selinuxfs, mounted at /sys/fs/selinux.
Outside the kernel, the userspace layer provides the tools and libraries you'll use every day:
| Component | Role |
|---|---|
policycoreutils | Core tools: restorecon, setsebool, chcon |
libselinux | C library for applications to interact with SELinux |
libsepol | Compiler and parser of binary policies |
setools | Analysis tools: sesearch, seinfo for reading policies |
semanage | Runtime configuration management: fcontext, port, user, boolean |
The workflow: the admin changes configuration through userspace tools → the policy is compiled to binary format by libsepol → loaded into the kernel → the LSM hooks enforce it based on labels and the AVC. One large system where every layer can be observed.
SELinux policies come in several variants, and the two most discussed are:
targeted — the default policy on RHEL/Fedora/Rocky. It only protects targeted processes (daemons like httpd_t, sshd_t, named_t); everything else runs in the unconfined_t domain with almost no restrictions. The most balanced choice between security and convenience.strict — all processes are strictly restricted, with no unconfined_t domain. Far more secure in theory, but very heavy to operate day-to-day — which is why it's rarely used in production.Other variants worth knowing by name: minimum (a smaller derivative of targeted) and mls (adds Multi-Level Security with hierarchical sensitivities). Whenever you see SELINUXTYPE=targeted in /etc/selinux/config, that's the policy currently running.
Note
For this series, all examples and practices assume the targeted policy — the default on almost all Red Hat-family installations. The Type Enforcement, context, and denial concepts you learn apply equally across all policy variants; what differs is only how many processes are protected.
In this episode 2, we've built the basic SELinux vocabulary: Type Enforcement that restricts domains (subjects) against types (objects), the security context format user:role:type:sensitivity[:category], the role of the AVC as a decision cache and the source of denial logs, the kernel LSM + userspace architecture working together, and the difference between the targeted and strict policies.
The essentials to take with you:
user:role:type:sensitivity[:category] — example unconfined_u:object_r:etc_t:s0.libselinux, libsepol, setools, semanage) as the controllers.With this vocabulary in hand, you're ready to practice. In the next episode 3, we'll discuss modes, status & basic troubleshooting — the differences between enforcing, permissive, and disabled, how to switch between modes safely, reading getenforce and sestatus, and diagnosing your first denials from /var/log/audit/audit.log using ausearch. Stay motivated, because starting this episode you'll be standing at the terminal a lot!