- SELinux adds a mandatory access control layer based on contexts and types to strengthen security. Linux beyond traditional permits.
- Daily administration focuses on modes (enforcing, permissive), proper labeling, booleans, and tools such as semanage, semodule, and restorecon.
- The logs in /var/log/audit/audit.log, along with audit2why and audit2allow, allow you to diagnose problems and, if necessary, create specific local policies.
- The development of custom modules and the use of modular policies make it easy to adapt SELinux to your own applications without sacrificing a fine security model.
If you manage Linux systems and are concerned about security, sooner or later you're going to come across SELinux. Although it sometimes has a reputation for "breaking things," it's actually a a very powerful protective layer that limits what each process can do in the system, even after it has passed the traditional permissions of Unix.
Throughout this guide we will see, in considerable detail and in the clearest language possible, how SELinux works internally, what its components are contexts, policies, modes of operation and basic management toolsand how to start using it wisely without dying in the attempt, both in Red Hat-type distributions and in Debian or SUSE.
What is SELinux and how does it fit into Linux security?
SELinux (Security-Enhanced Linux) is a Mandatory access control (MAC) that is integrated into the Linux kernel through the LSM (Linux Security Modules) interface. While classic discretionary access control (DAC) is based on users, groups, and read/write/execute permissions, SELinux adds an extra layer that answers the question: "Can this subject perform this action on this object?".
With DAC, a file owner has considerable freedom to change permissions, and in practice, a process that compromises an account can gain access to far more resources than it should. SELinux, on the other hand, enforces a centralized policy: Even if the traditional permissions system allows access, if the SELinux policy does not authorize it, it is blocked.SELinux rules are evaluated after DAC rules, so if DAC already denies something, SELinux doesn't even come into play.
In practice, the system kernel consults SELinux before authorizing each relevant system call to decide whether the process involved is authorized to perform the operation on the specific resource according to the loaded policyThat policy is a large set of rules that describe which processes can interact with which files, directories, sockets, ports, and other objects.
SELinux answers this authorization question using a key concept: the security contextBoth processes and objects (files, directories, ports, etc.) have an associated special label, the SELinux context, which abstracts low-level details and focuses on their security properties. This avoids typical ambiguities, for example, when the same file is accessible through different paths due to bind mounts.
SELinux contexts: user, role, type, and level
An SELinux context is usually represented as a string with several fields: SELinux user, SELinux role, security type and levelAlthough all fields are relevant, in practice the most important part for most policies is the type, because the most common access rules are defined between types.
SELinux types are easily recognized because they end in _tFor example, the Apache web server process typically runs with the type httpd_t, the files that the web server publishes in /var/www/html/ They are usually labeled with httpd_sys_content_t, the temporary files of /tmp y /var/tmp/ usually tmp_t, and the ports that web servers can use are marked as http_port_t. The policy defines which combinations of types are allowed or prohibited..
Thanks to this, even if Apache is compromised, SELinux can prevent that process from running. httpd_t access, for example, MariaDB data. If there is no rule that allows httpd_t access objects labeled as mysqld_db_t (typical of /data/mysql/), that access will be denied even if the Unix permissions system would allow it.
The context of a process depends not only on the executable, but also on the SELinux user, role, and domain it currently has. The domain is, in fact, the type associated with the process (for example, ssh_t for the daemon SSH), And It is the domain that determines what that process can and cannot do.Roles control which domains a user can adopt and how they can switch between them.
When a user logs on, they are assigned a default SELinux context that sets their SELinux identity, the initial role, and the domain of the processes it launchesIf you want to change your role (and, usually, your associated domain), you must use the command newrole -r rol_r -t dominio_twhich will require a password to prevent programs from automatically changing roles. Such transitions will only be possible if the policy explicitly allows it.
SELinux states and operating modes
SELinux can be globally enabled or disabled, and when enabled it operates in one of several modes. It is important to distinguish between state (enabled or disabled) and way (how policies are applied when enabled).
When SELinux is enabled in the kernel, it can operate in these three main modes:
- Enforcing (application mode)SELinux strictly enforces the loaded policy. If an action violates a rule, the operation is blocked and a message is logged in the audit logs. safest mode and the recommended one once the system is properly configured.
- Permissive (permissive mode)The policy is evaluated, but violations are not blocked; they are simply recorded. /var/log/audit/audit.logThis mode is ideal for testing, debugging, and preparing for migration to enforcing without breaking services.
- DisabledSELinux is completely turned off; it neither enforces rules nor logs incidents. In this state there is no additional SELinux protection.
In Red Hat or similar systems, the current mode can be checked with getenforce, which returns Enforcing, Permissive or DisabledTo dynamically switch between enforcing and permissive without restarting, use setenforce 0 (permissive) or setenforce 1 (enforcing), provided that it is not completely disabled in the configuration file.
To configure persistent behavior after a reboot, edit the file /etc/selinux/configensuring that the line SELINUX = enforcing, SELINUX=permissive o SELINUX = disabled reflect what we want. If it changes to disabledSELinux stops loading when the system starts and cannot be changed on its own. setenforce.
In Debian environments, in addition to these modes, parameters can be passed to the kernel such as selinux=1 security=selinux to enable it, audit=1 to force the registration of denied transactions and enforcing=1 to start directly by applying the policy. These parameters are added to the GRUB kernel line, usually by modifying GRUB_CMDLINE_LINUX en /etc/default/grub and running update-grub.
SELinux basic installation and activation
In most modern distributions, the kernel comes with built-in SELinux support, and user tools are available in dedicated packages. If you need to adjust kernel settings, see [link to relevant documentation]. How to search for configurations in `make menuconfig`However, the way to enable and configure it changes slightly depending on the distribution family, although the underlying idea is always the same: Install the policies, label the file system, and boot the kernel with SELinux enabled..
In Debian, for example, the command apt install selinux-basics selinux-policy-default auditd It automatically installs the components necessary to use SELinux. The package selinux-policy-default It includes a standard modular policy, which by default It only restricts certain exposed services and leaves users in a kind of unrestricted domain (module unconfined). If you want a policy more like the old "strict" one, you have to disable that module.
After installing a policy, it is mandatory to label all system files, that is, assign them an appropriate SELinux type according to the labeling rulesIn Debian this can be done with fixfiles relabelThis process scans the system and applies labels according to the installed policy. This process can take a considerable amount of time, especially on machines with large amounts of data.
There is a script, selinux-activatewhich automates much of this process in Debian: adjusts parameters of BootPrepare the labeling and forces a relay on the next restartpreventing the creation of unlabeled files while SELinux is not yet fully up and running.
In Red Hat and derivative environments, when SELinux is enabled for the first time, something similar is usually done: it is adjusted /etc/selinux/config a SELINUX=permissiveThe empty file is created /.autorelabel and the system restarts (for example, with init 6The file /.autorelabel tells the system that it must Re-label the entire file system on the next bootOnce completed, you can switch to enforcing mode both in the configuration file and with setenforce 1.
SELinux policies: types, modules and administration
The SELinux policy is essentially a large set of rules that determine how processes and objects can interact within the system. However, it is not maintained as a single monolithic file, but rather in a structured way. modular, through policy packages that can be activated or deactivated depending on the services installed.
In Debian, for example, the available policy modules are located in /usr/share/selinux/default/, and the active configuration is stored in /etc/selinux/default/Although these files are readable, they should not be edited manually: You should always use the tools designed to manage them.as the semodule y semanage.
The command semodule is used for manage installed policy modulesAmong its basic options are:
- semodule -i module.pp.bz2: installs a policy module (compressed with bzip2) in the current configuration.
- semodule -r module_name: removes an already installed module.
- semodule -l: lists the modules present, with their versions.
- semodule -e module_name y semodule -d module_name: selectively activate or deactivate modules without uninstalling them.
Default, semodule Apply the changes to the policy selected by the variable. SELINUXTYPE en /etc/selinux/configIt is possible to work on another type of policy using the option -s. The changes are immediate unless the -n option is usedwhich avoids overloading the political system in the heat of the moment.
On the other side is semanageThe Swiss Army knife for fine-tuning high-level SELinux configuration: it maps system users to SELinux identities, manages file and port labeling rules, modifies booleans, etc. Each data type managed by semanage It has its own manual page, like semanage-login(8) o semanage-user(8), and the syntax of the suborders is quite consistent: -a to add, -d to delete, -m to modify, -l to list, and -t to specify a type or domain.
With semanage login -l The current mappings between usernames and SELinux users are obtained. Users not explicitly listed inherit the default identity, represented as __default__. For example, weekly login -a -s user_u user assigns the SELinux identity user_u to that Unix user, and semanage login -d user reverses that allocation.
The command semanage user -l It shows the associations between SELinux identities (such as user_u, staff_u, sysadm_u) and the roles they are allowed. When creating a new SELinux identity, you must specify the roles it can assume and a tagging prefix (user, staff o sysadm) which determines the type of personal files, for example staff_home_dir_t for users with prefix staffA typical order would be semanage user -a -R 'staff_r user_r' -P staff test_u, which creates an SELinux user test_u with certain roles and a specific labeling scheme.
Labeling of files, directories, and ports
Once the policy is installed, every relevant file, directory, device, socket, or port on the system must have a SELinux type consistent with its functionThis association between paths/ports and types is known as file context rules or tagging rules.
For files and directories, SELinux tags are stored as extended file system attributesThey can be easily seen with ls -Z, for example ls -Z /etc/httpdFor processes and ports, the kernel maintains the context, and it can be inspected with commands , the ps auxZ | grep httpd for processes or netstat -anpZ | grep httpd to view labeled ports.
If we want to know what context is applied by default to the files under /var/www/html, we can use ls -Z /var/www/html o semanage fcontext -l | grep '/var/www'It's common to find something like httpd_sys_content_t for content served by Apache. In contrast, a home directory like /home/dan/html may have something like user_home_twhich will prevent the web server from accessing it even if DAC allows it.
To allow the web server to read content from /home/dan/html In line with SELinux, the policy would need to be told that the type should be applied to those files. httpd_sys_content_tThis is done by registering a labeling rule with semanage fcontext -a -t httpd_sys_content_t '/home/dan/html(/.*)?' and then executing restorecon -Rv /home/dan/html to apply the new context to existing files.
The boss '/home/dan/html(/.*)?' It is a regular expression that indicates that it applies to both the directory and all its recursive content. restorecon is responsible for synchronizing the actual file system tags with the rules stored in the policy.This is also used after major changes or backup restorations.
Similarly, TCP/UDP ports also have an SELinux type. The web server, for example, uses ports labeled as http_port_tIf we want Apache to listen on port 8080, in addition to configuring it on the web server itself, we must register the port with semanage port -m -t http_port_t -p tcp 8080 so that SELinux considers it a valid port for that service.
SELinux Booleans: Behavior Switches
In addition to static rules, many SELinux policies expose Boolean options that allow you to activate or deactivate certain parts of the behavior without recompiling or loading new modules. They are very useful for adjusting permissions to real needs without getting bogged down in custom policies.
We can see all available options with getsebool -aThis lists the name of each boolean and its current value (on/off). For more detail, including a description, you can use semanage boolean -land filter by keywords with grep to locate what interests us, for example ftp o httpd.
Imagine we want an FTP server to have read and write permissions in users' home directories, and although everything seems correctly configured at the system level, the files are not visible. Checking with semanage boolean -l | grep ftp we can find a boolean called ftp_home_dir with description “Allow ftp to read & write file in user home directory” and value off. Activate it with setsebool ftp_home_dir on This allows the FTP server to access those directories in accordance with the policy.
Another typical example is the Boolean httpd_enable_homedirswhich allows the web server to access personal directories to serve sites like ~/public_html/We can check its value with getsebool httpd_enable_homedirsand if it appears as off, enable it permanently with setsebool -P httpd_enable_homedirs on. The option -P It ensures that the change is saved persistently and survives reboots.
In general, before writing complex policies, it's worth carefully reviewing the booleans related to the service you're adjusting. Often, A simple on/off option solves what appears to be a serious permissions problem..
Diagnosis: logs, audit2why and audit2allow
When SELinux denies an operation, it generates an access control event (AVC) that is logged in /var/log/audit/audit.log (if the service auditd is active and audit=1 (it's configured). For daily administration, learning to read these messages is essential to understanding Why has something been blocked?.
A typical AVC message might look like this: avc: denied { read write } for pid=1876 comm="syslogd" name="xconsole" dev=tmpfs ino=5510 scontext=system_u:system_r:syslogd_t:s0 tcontext=system_u:object_r:device_t:s0 tclass=fifo_file permissive=1It indicates which permissions have been denied, which process is involved (PID, command and source context) and against which object (name, type and class of object). With that information, it is possible to draft a rule that expressly authorizes that operation, for example allow syslogd_t device_t:fifo_file { read write }.
Analyzing the log manually is cumbersome, so there are specific tools like ausearch to search for events of interest and, above all, audit2why y audit2allow to interpret what is happening. With audit2why < /var/log/audit/audit.log We can obtain a summary of the causes of the denials, which greatly helps in identifying incorrect configurations or missing booleans.
If, despite adjusting booleans and contexts, we continue to see repeated errors for the same service, we can consider creating a specific local policy for that caseFor example, to compile rules that allow currently denied access to Apache, we can do something like grep httpd_t /var/log/audit/audit.log | audit2allow -M http_policy, which generates a local policy module called http_policy.pp.
Similarly, we could create a policy for Samba with grep smbd_t /var/log/audit/audit.log | audit2allow -M smb_policyOnce generated, they are loaded into the active policy with semodule -i http_policy.pp y semodule -i smb_policy.pp. This allows the rules generated from the records to be integrated into SELinux. and, in principle, that denials related to those cases will cease to appear.
Even so, it's advisable to manually review the rules generated by audit2allow, because it tends to be generous and may grant more permissions than are actually necessary. A good practice is create more specific types and limit permissions to those dedicated typesInstead of opening up too much access to general types, when certain denials are not critical, it may be better to use rules. dontaudit so that they don't create noise in the records instead of allowing them.
Development of custom policy modules
In complex environments or internally developed applications, it's common for there not to be a specific SELinux module that covers them. In such cases, it is recommended Develop custom modules that expand the reference policy, instead of forcing everything into generic policies or booleans.
To do this, packages such as selinux-policy-dev y selinux-policy-doc, which provide both the documentation of the standard rules (for example in /usr/share/doc/selinux-policy-doc/html/) as example files that serve as templates. It is common to copy base files such as Makefile.example, example.fc, example.if y example.te to a working directory to adapt them.
The file .te (type enforcement) is the heart of the module: It declares the types, domains, and allow/dontaudit rules. The file .fc (file contexts) defines how files related to the module are labeled, using paths and regular expressions. Finally, the file .if defines public interfaces that other modules can invoke to interact with ours, something like high-level reusable functions.
In a typical example, in the file .te a domain could be declared myapp_t, the executable type myapp_exec_t, a type for the logs myapp_log_t and another for temporary files myapp_tmp_tMacros would be used as domain_type(), domain_entry_file(), logging_log_file() o files_tmp_file() to properly structure the policy, followed by specific allow rules that authorize, for example, reading and appending to log files and managing temporary files.
In the File .if interfaces would be defined as myapp_domtrans (to control who can launch the application and enter your domain) or myapp_read_log (to allow other domains to read its records). Each interface must declare the types it needs with gen_require and generate a valid block of rules that, if expanded, could be seamlessly integrated into a .te.
Once the three files (interfaces, contexts, and rules) have been adjusted, they are renamed with the actual name of the application (for example, mi_programa.te, mi_programa.fc, mi_programa.if) and the module is compiled with something like make NAME=develwhich will produce a policy package mi_programa.pp. This package can be charged immediately with semodule -i mi_programa.ppAnd if there are multiple modules in the directory, make will generate all the corresponding .pp.
The most delicate part of module development is determining the minimum set of rules necessary for the application to function without breaking the security model. A common technique is to properly label all objects used by the application, set SELinux to permissive mode, and Run the application to generate denials in the recordswhich are then analyzed with auditing tools to refine the policy.
Finding the balance between functionality and security is a iterative workBut when implemented correctly, it offers very granular protection that prevents a vulnerability in an application from becoming a total system compromise.
SELinux has gone from being "that nuisance that breaks services" to a powerful and flexible tool that, when properly configured, provides a a layer of defense of enormous value against attacks and configuration errorsUnderstanding its basic concepts—contexts, types, modes, booleans, labeling, and modules—is the first step to taking advantage of it without fear.
Passionate writer about the world of bytes and technology in general. I love sharing my knowledge through writing, and that's what I'll do on this blog, show you all the most interesting things about gadgets, software, hardware, tech trends, and more. My goal is to help you navigate the digital world in a simple and entertaining way.
