- SELinux adds mandatory access control to the Linux kernel through tags and policies that go beyond traditional DAC permissions.
- Security contexts (user, role, type, level) and type-based policies allow for very granular access control over processes, files, and ports.
- Tools such as getenforce, chcon, semanage, semodule and booleans facilitate the practical management of SELinux in production.
- When properly configured, SELinux mitigates zero-day exploits and misconfigurations by strictly limiting what each service or application can do.

SELinux may sound like technology reserved for kernel geeks, but it's actually one of the pieces of the most powerful security we have today in LinuxIf you manage servers, Docker container securityWhether it's cloud infrastructure or even slightly sensitive desktop computers, understanding how SELinux works makes the difference between a system that is simply "well configured" and a system that is difficult to take down even with zero-day vulnerabilities.
Despite its reputation for being "complicated," SELinux offers a very logical model: it defines what each process can do and what objects it can communicate with, and If anything deviates from that script, the kernel cuts it off abruptly.Instead of blindly trusting that root will behave or that your daemons won't have bugs, SELinux enforces mandatory access control that is imposed even on the superuser, using very detailed labels and policies.
What is SELinux and what problem does it solve?
Security-Enhanced Linux (SELinux) is a LSM-based Linux kernel security module (Linux Security Modules). It was originally developed by the NSA in collaboration with Red Hat and other partners, and since kernel version 2.6, it has been an official part of the kernel. It is not a separate application, but rather a kernel extension that adds a mandatory access control (MAC) system and role-based access control (RBAC).
Unlike the classic Unix discretionary access control (DAC) —the one of owner, group, others and rwx permissionsIn SELinux, access decisions are not based on the file owner's choice, but on a global policy established by the security administrator. The DAC is still present and takes precedence: if the DAC denies, SELinux doesn't intervene; but even if the DAC allows, SELinux can still block the operation according to its policy.
The SELinux architecture clearly separates the pieces: on one hand, the kernel code that makes security decisionsOn the one hand, there are the policy modules, and on the other, the policy modules that describe what is allowed and what is not. This separation allows for adjusting the rules without recompiling the kernel and streamlines the number of components that can affect system security.
SELinux has been adopted by default in distributions such as Fedora, Red Hat Enterprise Linux, CentOS, and Scientific Linux, and is also heavily integrated into systems like Android, where it is used for confine system processes and apps to very specific domains. In the BSD and GNU/Linux world there are alternatives such as AppArmor, TOMOYO or TrustedBSD (in macOS/FreeBSD), but SELinux stands out for the level of granularity it offers over all system objects.
From DAC to MAC: why the classic model is no longer enough
In a traditional Unix system, the Domain-Object model is managed through DAC: each file or resource has an owner and its permissions, and Any process running as that user can do whatever it wants with those resources.This means that if a daemon runs as root, any exploitable bug can open the door to controlling half the system with its root context.
Typical examples: databases whose data files should only be manipulated through the DBMS, but which in reality They are readable and modifiable by processes with UID rootor critical daemons running with excessive privileges. A programming error, a buffer overflow, or poor input validation can turn the service into a highway to the entire system.
SELinux adds a layer of Mandatory Access Control (MAC) above DAC. "Mandatory" means that access control is centrally defined by the administrator through policies, and neither users nor processes can relax these rules on their own. The operating system enforces these policies by evaluating each relevant kernel operation before allowing it.
The kernel, using LSM hooks, queries SELinux on every sensitive system call (opening files, creating sockets, mounting file systems, communicating via IPC, etc.). At each decision point, SELinux evaluates the operation based on the charged politics and the context of subject and object securityIf the policy does not explicitly grant permission, the action is denied, regardless of whether the process is root or not.
Operating modes: enforcing, permissive, and disabled
SELinux can operate in three clearly differentiated operating states, which are important to understand to avoid going crazy in production:
- EnforcingSELinux is enabled and fully enforces the policy. All actions not permitted by the rules are blocked and logged.
- permissiveSELinux is active, loads the policy and labels the filesystem, but does not block It only records the transactions as if they were denied. This is ideal for debugging and adjusting policies.
- DisabledSELinux is disabled. No policies are applied and no tagging is performed. The system relies on the classic DAC model.
For quick and temporary changes between enforcing and permissive, the command is used setenforcewhere the mode is indicated as 0 (permissive) or 1 (enforcing). It is important to know that this change is volatile: After the next restart, the mode will revert to the settings..
If you need a permanent change, you need to edit the file. /etc/selinux/config (or /etc/sysconfig/selinux in some distributions) and adjust the value of the directive SELINUX=disabled|permissive|enforcingThe changes will be applied on the next boot and, in many cases, will involve relabeling the file system.
To check active mode, commands such as getenforce o sestatusThe first simply returns Enforcing, Permissive, or Disabled; the second provides a more complete summary of SELinux status, loaded policies, and active modules.
Security contexts and labeling system
The heart of SELinux is its system of security labels or contextsEach file, process, network port, socket, device, etc., has an associated context that describes how it can be used. This context is composed of several fields that together form the view that SELinux has of that object.
The general format of a context is user_u:role_r:type_t:levelwith some nuances depending on the policy (especially if MLS/MCS is used). Each field has a specific purpose: the SELinux user, the role, the type (also called domain when referring to processes) and the sensitivity level or category.
In actual practice, the most critical element is the type (the third field), since The vast majority of political rules are formulated as relationships between typesFor example, allowing httpd_t processes to access files labeled as httpd_sys_content_t, or allowing a specific domain to communicate with sockets labeled as http_port_t.
These contexts are stored as extended file system attributesTherefore, it is essential to use filesystems that support xattrs (such as ext4, XFS, etc.). In the case of processes, the current context and other related contexts are exposed through the pseudo-filesystem. /proc/<pid>/attr/ in files such as current, exec, fscreate, prev, sockcreate or keycreate.
Typical examples of contexts in a system with targeted policy would be:
- system_u:object_r:httpd_sys_content_t:s0 for web content served by Apache or Nginx.
- system_u:object_r:home_user_t:s0 for user home directories.
- system_u:system_r:httpd_t:s0 for the execution domain of the web server itself.
Breaking down the components of the SELinux context
When you find yourself in a context like this unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023It may seem like a hieroglyphic, but each piece corresponds to a specific concept within SELinux and its reference policy.
El SELinux user (by convention with suffix) _uThis is not the same as the Unix user in /etc/passwd. SELinux maintains its own user database and maps them to Linux users. An SELinux user can group several Unix users, because the idea is for the MAC layer to be independent of the DAC.
El SELinux role (with suffix _rThis defines what roles a user or domain can adopt. If used strictly, it's called full RBAC. Most file objects use the object_r role, while roles like system_r, user_r, staff_r, or sysadm_r are applied to processes depending on the security context they must assume.
El SELinux type or domain (suffix _tIn practice, the type is the crucial element. The type describes the class of object or the execution domain of a process. The policy specifies which interactions are allowed between types: which domains can read, write, execute, or communicate with which other types of objects.
El level and categories They are used when multi-level security (MLS) or multi-category security (MCS) policies are enabled. Sensitivities are hierarchical (e.g., s0, s1, etc.), while categories are not (c0, c1, c2, ...). In highly critical environments—e.g., certain government organizations—they are used to ensure that It can only be read downwards and written either downwards or upwards.and to isolate data according to very specific compartments.
SELinux policies: targeted, strict, MLS/MCS and modularity
The security that SELinux applies is determined by the charged politicsA policy is simply a large set of rules that describe which domains can do what on which types, as well as other elements (transitions, tagging rules, etc.). Distributions usually package standard policies so you don't have to write everything from scratch.
The most common policy is the so-called targetedIn this mode, only certain processes—primarily network services and high-risk daemons such as Apache, Nginx, DNS, proxies, SNMP, syslog, etc.—run within confined domains. All other user processes run in "unconfined" domains, where standard Linux security is essentially enforced, along with logging of certain actions.
There is also politics strictIn this model, virtually all processes are confined under some policy. It is much more secure, but it can also be considerably more painful to maintain if the model is not well understood, because Any mismatch in labeling or rules can disrupt normal workflows.
There are policies for high-security environments MLS/MCS (Multi-Level / Multi-Category Security)These systems leverage sensitivities and categories to impose even finer controls. They are typical of very rigid military or administrative environments and are rarely used outside of specific contexts due to their high operational complexity.
Modern policies are distributed in a modularInstead of a single monolithic policy file, separate modules are used for specific services, greatly simplifying management and updates. The modules are managed with tools such as semodule y week modulewhich allow you to install, remove, enable or disable parts of the policy without recompiling everything each time.
How SELinux decides: subjects, objects, classes, and permissions
When a process (the subject) attempts to access a resource (the object), SELinux conceptually formulates the question: "Can a domain of type X perform operation Y on an object of type Z belonging to class C?"The answer is sought in politics through defined rules.
In Type Enforcement (TE)-based policies, which are used by most distributions and Android, each object belongs to a class (file, dir, fifo_file, tcp_socket, process, etc.) and the policy defines what permissions are possible for each class: read, write, execute, bind, connect, getattr, open, and a long etcetera.
TE rules are expressed very directly. A basic example would be:
allow httpd_t http_port_t:tcp_socket name_bind;
With this rule, the policy indicates that processes in the httpd_t domain can perform the name_bind operation on TCP sockets labeled http_port_t. The focus is on object types and classes, not in specific pathsThis prevents surprises when moving files or changing directory structures.
In Android, for example, SELinux attributes are used to group types under more general tags such as appdomainThis allows a single rule to apply to multiple domains (untrusted_app, isolated_app, etc.) without repeating definitions. Macros like rw_file_perms are also used to group several common file permissions and reduce errors caused by oversights.
Internal statuses, AVC and denial registry
When an access request occurs, SELinux first queries the Access Vector Cache (AVC)A cache is used to store recent access decisions to speed up the process. If the decision is already in the cache, it is used directly; otherwise, the security server (the internal part of SELinux in the kernel) is queried, which evaluates the operation based on the policy and the subject and object context.
If there is no rule that explicitly permits the requested action, the default decision is to denyThis "deny by default" philosophy is one of the pillars of SELinux and what gives it its robustness against permissive configuration errors.
When a denial occurs in enforcing mode, the kernel logs a message of type avc: denied in the system logs. Depending on the distribution, it may appear in /var/log/audit/audit.log, /var/log/messages or be captured by the auditd daemon. These messages include the process context (scontext), the object context (tcontext), the class, the requested operation, and other data very useful for debugging.
In permissive mode, the operation is allowed but also The event avc: denied is generated, marked with permissive=1. This is pure gold for building and adjusting policies, because it allows you to see what would break the system if you put enforcing on it while still operating normally.
Integrating SELinux into distributions and Android
In the Linux server and desktop ecosystem, SELinux is enabled by default in Fedora, RHEL, CentOS and derivativesDebian and Ubuntu provide full support in their kernels and packages, although activation is usually optional and requires installing packages such as selinux-basics, selinux-policy-default, and auditd, followed by global re-tagging with fixfiles relabel.
Android initially incorporated SELinux in version 4.3 in permissive mode, then switched to using it partially in 4.4 (only for critical domains such as installd, netd, vold, zygote) and Since Android 5.0 it is fully integratedAndroid's policy focuses on isolating apps, system services, and sensitive processes using types and attributes, with the goal of minimizing the impact of a compromise on any of those components.
Android uses concepts like the untrusted_app type for common application processes, appdomain attributes to group app domains, and MLS/MCS categories to isolate data between apps and between physical users. All of this combines to... a compromised application cannot leave its sandbox even if you obtain very broad user permissions.
Important: Android simplifies the SELinux model by ignoring users, roles, and advanced sensitivities. There is only one SELinux user (u), two basic roles (r for subjects and object_r for objects), and the sensitivity is always s0. Categories are what make the difference for data isolation.
Comparison with AppArmor and other LSMs
In many discussions, the comparison between inevitably arises. SELinux and AppArmorSince both are Linux security modules and offer MAC, their approaches are quite different, and it's worth understanding this before choosing one or the other for your environment.
SELinux defines a object-centered policy and their types: every object in the system (files, processes, sockets, ports, devices, IPCs, etc.) is assigned a label. Access decisions are made based on subject and object contexts, without depending on the exact file path. This makes the system more stable in the face of directory structure changes or alternative file system views (chroot, containers, bind mounts, etc.).
AppArmor, for its part, applies a task-focused policy and path-based. It defines profiles for each program, indicating which file paths, ports, etc., it can access and with what permissions. It is more intuitive to configure and is usually more user-friendly for administrators who don't want to become experts in SELinux, but Its control is somewhat less fine. and more dependent on the file system structure.
Both share the principle of denying by default, but they apply it differently: AppArmor does so only on the tasks it covers with profiles, while SELinux, when in strict mode, extends it to the entire system and all tagged objects. The consequence is that SELinux typically offers a deepest level of confinement, at the price of a more extensive and complex policy.
Practical tools for managing SELinux
Working with SELinux relies on a series of command-line tools that simplify the management of mode, tagging, booleans, and policy modules. Although it may seem like an overwhelming arsenal at first, A few utilities cover most everyday tasks.
To view the security context of files and processes, you can use the option -Z in common commands like ls, ps, or id. For example, ls -Z In addition to DAC permissions, it will show the SELinux context of each file, allowing you to quickly check if the tagging is as expected.
The command chcon The `change context` command allows you to manually modify the entire context of a file or only specific parts (role, type, range) using options such as `-r`, `-to`, and `-l`. It's useful for spot corrections, but it's worth remembering that Your changes may be lost if the labeling is reapplied according to the policy using tools such as restorecon or fixfiles.
The tool weekly It's the Swiss Army knife of runtime policy management. With various subcommands, you can manage persistent file contexts (fcontext), login mappings between Linux and SELinux users (login), SELinux users and their roles (user), tagged ports (port), booleans, and even policy modules. All of this without needing to recompile the entire policy from source.
To review and change the state of booleans—small switches that enable or disable blocks of rules within the policy—the following are used getsebool y setsebool, in addition to his own boolean weekA typical boolean is httpd_enable_homedirs, which when active allows the web server to access users' home directories (useful for ~user/public_html/).
Finally, commands like fixfiles They allow for a complete relabeling of the file system according to defined rules, and semodule It handles installing, listing, enabling, or disabling policy modules (.pp) packaged and distributed by the reference policy or by the administrator.
Creation and adjustment of customized policies
When an application doesn't have its own SELinux module or you need more specific confinement, it's time to get down to business and create custom policiesIt may sound harsh, but the workflow is quite well defined if you follow certain steps thoughtfully.
The first step is to ensure that the relevant objects (executables, data directories, sockets, etc.) are tagged with appropriate types. This can be achieved by defining file context rules with semanage fcontext and apply them with restorecon, using regular expressions to cover entire directory trees.
Afterwards, the system is usually put into permissive mode for that machine (or, in some cases, for a specific domain) and the application is allowed to run normally while SELinux logs all the avc denials: denied that would have occurred. These logs, usually analyzed with tools like audit2allowThey are used to extract candidate rules.
Reference policies are typically structured in three files per application: a .te file containing TE rules (allow, type, domain_type, etc.), a .fc file containing file context rules, and a .if file containing public interfaces that other modules can reuse. All of this is compiled into .pp modules using make and loaded with semodule.
It is crucial not to blindly trust everything that audit2allow proposes: tends to be more permissive than strictly necessaryIdeally, you should manually review the suggested rules, create new types where appropriate to separate sensitive data from irrelevant data, and, when a denied operation is not critical, consider using dontaudit rules to stop recording noise without granting permissions.
In environments such as SUSE Linux Micro or Android, additional tools are provided (for example, Udica to generate container policies from JSON descriptions) that automate part of the process by quickly adapting the policy to specific containers without having to learn the entire policy language from scratch.
This entire ecosystem of contexts, modular policies, booleans, and management tools makes SELinux an extremely robust and flexible security platformWith some initial investment in learning, it allows you to very precisely limit what each service or application can do, drastically hardening the system's attack surface against exploits, malware, and human error.
Table of Contents
- What is SELinux and what problem does it solve?
- From DAC to MAC: why the classic model is no longer enough
- Operating modes: enforcing, permissive, and disabled
- Security contexts and labeling system
- Breaking down the components of the SELinux context
- SELinux policies: targeted, strict, MLS/MCS and modularity
- How SELinux decides: subjects, objects, classes, and permissions
- Internal statuses, AVC and denial registry
- Integrating SELinux into distributions and Android
- Comparison with AppArmor and other LSMs
- Practical tools for managing SELinux
- Creation and adjustment of customized policies