Reading and understanding the policy from the inside: the structure of Type Enforcement rules such as allow and neverallow, attributes and roles, reading the policy with sesearch and seinfo, and managing policy modules with semodule in the .pp and modern CIL formats.

In episode 4 we learned to label the filesystem correctly. But labels are only half the story — labels only mean something if there are rules connecting them. This episode takes you to the heart of SELinux: the policy. We'll read Type Enforcement rules, understand the structure of allow and neverallow, get to know attributes and roles, and then manage policy modules with semodule.
By the end of this episode, you'll no longer see SELinux as a black box — you'll be able to ask the policy directly "what is this domain allowed to do?" and get a readable answer.
The most fundamental TE rule looks like:
allow <domain> <type> : <class> { <permissions> };A real example you'll encounter in the policy:
allow httpd_t httpd_sys_content_t : file { read getattr open };Meaning: processes in the httpd_t domain are allowed to perform the read, getattr, and open operations on objects of the file class labeled httpd_sys_content_t. That's the language whose results we've seen as "allowed" or "denied" in episode 3.
The two most important types of rules:
allow — grants permission. Without an allow rule, access is automatically denied (default deny).neverallow — an absolute prohibition that is checked at policy compile time. If another module tries to allow something that neverallow forbids, the policy compilation fails entirely — the system won't load the policy. This is the last line of defense that prevents admins (or bugs) from opening accesses that should stay closed.neverallow is why you sometimes can't just "add an allow" with audit2allow: if that rule violates a neverallow, the policy refuses to compile. Understanding this saves you from frustration when developing your own policies.
Besides domains and types, the policy knows two grouping concepts:
file_type attribute covers all file types; an allow rule targeting an attribute automatically applies to all its member types. This keeps the policy concise — one rule for hundreds of types.system_r is the role for system processes, while the role on objects is always object_r. The rule connecting a role to a domain is called a role transition.The tools for dissecting the policy come from the setools-console package (installed in episode 0):
sesearch --allow --source httpd_tallow httpd_t httpd_sys_content_t : file { ioctl read getattr lock open };
allow httpd_t httpd_sys_content_t : dir { ioctl read getattr lock open search };
allow httpd_t tmp_t : file { read write getattr setattr };sesearch is the policy search engine: search by source domain (--source), target type (--target), or class (--class). Practical options you'll use often:
| Option | Function |
|---|---|
sesearch --allow -s <domain> | All permissions granted by a domain |
sesearch --allow -t <type> | All permissions targeting a type |
sesearch --neverallow | All absolute prohibitions |
seinfo -t -x httpd_t | Details of a type (member attributes, etc.) |
While seinfo is the policy directory: it lists object types, attributes, roles, and users. An example of its use to view all existing types:
seinfo -tA recommended workflow when debugging: find the offending domain from the AVC denial (episode 3), then ask sesearch what that domain is allowed to do. Often the answer to "why was it denied" is that the expected rule simply doesn't exist.
The SELinux policy is composed of modules — units that can be installed, disabled, and removed independently. The management tool is semodule:
sudo semodule -l
sudo semodule -i myapp.pp
sudo semodule -d myapp
sudo semodule -r myapp| Option | Function |
|---|---|
-l | List installed modules |
-i | Install a new module (load into the active policy) |
-d | Disable a module |
-r | Remove a module |
Modules come in two package formats:
.pp — the old binary format, the result of traditional policy compilation. Still widely circulated and supported for compatibility..cil — the CIL (Common Intermediate Language) format, the modern format introduced by the SELinux userspace and the standard in userspace 3.11. CIL is a mid-level language that's more expressive, readable, and the basis for writing new policies.(allow httpd_t httpd_sys_content_t (file (read getattr open)))Notice: the same TE rule, rewritten in CIL syntax with parentheses. This is the modern face of policy development — and we'll go deeper into it in the later episodes about audit2allow and policy development.
Tip
Before installing a downloaded or self-built module, first inspect what's in it with sesearch. A single .pp or .cil module can contain many rules — including ones you're not aware of. Reading before installing is a security habit that distinguishes a conscious sysadmin from someone just copy-pasting.
In this episode 5, we've penetrated the heart of the SELinux policy: the structure of TE rules (allow, neverallow, attributes, and roles), reading the policy with sesearch and seinfo, and managing modules with semodule in the .pp and .cil formats.
The essentials to take with you:
allow rule grants permission; without a rule, access is denied (default deny).neverallow is enforced at compile time — violating it means the policy fails to load.sesearch for searching rules; seinfo for viewing policy structure.semodule manages modules; .pp is the old format, .cil (CIL) is the modern format in userspace 3.11.With the ability to read and manage policies, you've moved beyond just "copying commands". In the next episode 6, we'll discuss booleans & tunables — the on/off switches inside the policy that let you change protection behavior without rewriting rules, including setsebool, getsebool, and the built-in boolean list for various services. Stay motivated, because this skill is the bridge to flexible SELinux management in production!