One of the reasons for the tremendous success of the Linux OS on embedded, mobile devices, and servers is the relatively high level of security of the kernel, associated services, and applications. However, of the Linux kernel architecture, you cannot find a square that specifically addresses security. So where does the Linux security subsystem hide and what does it consist of?
Background of Linux Security Modules and SELinux
Security Enhanced Linux is a set of rules and access mechanisms based on mandatory and role-based access models, intended to protect Linux systems from potential threats and rectify the shortcomings of Discretionary Access Control (DAC) — the traditional Unix security system. The project originated within the National Security Agency (NSA) of the USA, primarily developed by contractors from Secure Computing Corporation and MITRE, along with several research laboratories.

Linux Security Modules
Linus Torvalds made several comments on the new developments by the NSA, so that they could be included in the main branch of the Linux kernel. He described a common environment, with a set of interceptors to manage object operations and a set of protective fields in kernel data structures to store relevant attributes. This environment can then be used by loadable kernel modules to implement any desired security model. LSM was fully integrated into the Linux kernel v2.6 in 2003.
The LSM framework includes protective fields in data structures and function interception calls at critical points in the kernel code to manage them and enforce access control. It also adds features for registering security modules. The interface /sys/kernel/security/lsm contains a list of active modules in the system. LSM hooks are stored in lists that are called in the order specified by CONFIG_LSM. Detailed documentation on hooks is included in the header file include/linux/lsm_hooks.h.
The LSM subsystem enabled the complete integration of SELinux in the same version of the stable Linux kernel v2.6. Almost immediately, SELinux became the de facto standard for a secure Linux environment and was included in the most popular distributions: RedHat Enterprise Linux, Fedora, Debian, and Ubuntu.
SELinux Glossary
- Identity The SELinux user is not the same as the familiar Unix/Linux user ID; they can coexist on the same system but are fundamentally different. Each standard Linux account can correspond to one or several in SELinux. The SELinux identity is part of the overall security context that defines which domains can be accessed and which cannot.
- Domains In SELinux, a domain is the execution context of a subject, i.e., a process. The domain directly determines the access that the process has. A domain is basically a list of what processes can do or the actions a process can perform with different types. Examples of domains include sysadm_t for system administration and user_t, which is the typical unprivileged user domain. The init initialization system runs in the init_t domain, while the named process runs in the named_t domain.
- Roles Roles act as the intermediary between domains and SELinux users. Roles determine which domains a user can belong to and what types of objects they can access. This access control mechanism helps prevent privilege escalation attacks. Roles are part of the Role-Based Access Control (RBAC) security model used in SELinux.
- Types An attribute of the Type Enforcement list assigned to an object that defines who can access it. It is similar to a domain definition, except that a domain applies to a process, while a type applies to objects like directories, files, sockets, etc.
- Subjects and Objects Processes are subjects and run in a specific context, or security domain. Operating system resources: files, directories, sockets, etc., are objects that are associated with a specific type, or in other words — a level of confidentiality.
- SELinux Policies To protect the system, SELinux employs various policies. The SELinux policy defines users' access to roles, roles' access to domains, and domains' access to types. Initially, a user is authorized for a role, then the role is authorized for access to domains. Finally, a domain may only access specific types of objects.
LSM and SELinux Architecture
Despite its name, LSM is not technically loadable modules in Linux. However, similar to SELinux, it is directly integrated into the kernel. Any modifications to the LSM source code require a new kernel compilation. The appropriate option must be enabled in the kernel settings; otherwise, the LSM code will not be activated upon booting. Even then, it can only be enabled via the OS bootloader.

LSM Verification Stack
LSM is equipped with hooks in essential kernel functions that may be relevant for checks. One of the key features of LSM is that it is structured as a stack. This means that standard checks are still performed, and each LSM layer only adds additional controls and oversight. Consequently, retraction of prohibitions is not possible. This is illustrated in the figure: if routine DAC checks result in a denial, the process won't even reach the LSM hooks.
SELinux adopted the security architecture of Flask from the Fluke research operating system, particularly the principle of least privilege. The essence of this concept, as implied by their names, is to grant a user or process only those rights necessary to perform intended actions. This principle is implemented through mandatory access type enforcement, thus access control in SELinux is based on a domain => type model.
Thanks to mandatory access type enforcement, SELinux offers significantly greater access control capabilities compared to the traditional DAC model used in Unix/Linux systems. For instance, one can restrict the number of network ports that an FTP server will use, allow writing and modifying files in a specific folder, but not their deletion.
The main components of SELinux are:
- Policy Enforcement Server — The primary mechanism for organizing access control.
- Database of the system's security policies.
- Interaction with the LSM event interceptor.
- Selinuxfs — A pseudo-filesystem, similar to /proc, mounted at /sys/fs/selinux. It is dynamically populated by the Linux kernel at runtime and contains files with information about the SELinux status.
- Access Vector Cache — An auxiliary mechanism for performance enhancement.

Operation Scheme of SELinux
All of this works as follows.
- A subject, in SELinux terms, performs a permitted action on an object after DAC verification, as shown in the upper image. This operation request is sent to the LSM event interceptor.
- From there, the request, along with the security context of the subject and object, is passed to the SELinux Abstraction and Hook Logic module, responsible for interacting with LSM.
- The instance making the decision on the subject's access to the object is the Policy Enforcement Server, which receives data from SELinux AnHL.
- To make a decision about access or denial, the Policy Enforcement Server consults the caching subsystem of the most frequently used rules called Access Vector Cache (AVC).
- If the decision for the corresponding rule is not found in the cache, the request is forwarded to the security policy database.
- The result of the search from the database and AVC is returned to the Policy Enforcement Server.
- If the found policy aligns with the requested action, the operation is permitted. Otherwise, the operation is denied.
Managing SELinux settings
SELinux operates in one of three modes:
- Enforcing — Strict enforcement of security policies.
- Permissive — Violations of restrictions are allowed, with corresponding logging.
- Disabled — Security policies do not apply.
You can check which mode SELinux is in with the following command.
[admin@server ~]$ getenforce
PermissiveChanging the mode until reboot, e.g., setting it to enforcing, or 1. The permissive parameter corresponds to the numeric code 0.
[admin@server ~]$ setenfoce enforcing
[admin@server ~]$ setenfoce 1 # the same
You can also change the mode by editing the file:
[admin@server ~]$ cat /etc/selinux/config # This file controls the state of SELinux on the system.
# SELINUX= can take one of these three values:
# enforcing - SELinux security policy is enforced.
# permissive - SELinux prints warnings instead of enforcing.
# disabled - No SELinux policy is loaded.
SELINUX=enforcing
# SELINUXTYPE= can take one of three values:
# targeted - Targeted processes are protected,
# minimum - Modification of targeted policy. Only selected processes are protected.
# mls - Multi Level Security protection.
SELINUXTYPE=targete
The difference from setenfoce is that upon system boot, the SELinux mode will be set according to the value of the SELINUX parameter in the configuration file. Additionally, changes between enforcing disabled take effect only through editing the /etc/selinux/config file and after a reboot.
View a brief status report:
[admin@server ~]$ sestatus SELinux status: enabled
SELinuxfs mount: /sys/fs/selinux
SELinux root directory: /etc/selinux
Loaded policy name: targeted
Current mode: permissive
Mode from config file: enforcing
Policy MLS status: enabled
Policy deny_unknown status: allowed
Max kernel policy version: 31
To view SELinux attributes, some built-in utilities use the -Z parameter.
[admin@server ~]$ ls -lZ /var/log/httpd/
-rw-r--r--. root root system_u:object_r:httpd_log_t:s0 access_log
-rw-r--r--. root root system_u:object_r:httpd_log_t:s0 access_log-20200920
-rw-r--r--. root root system_u:object_r:httpd_log_t:s0 access_log-20200927
-rw-r--r--. root root system_u:object_r:httpd_log_t:s0 access_log-20201004
-rw-r--r--. root root system_u:object_r:httpd_log_t:s0 access_log-20201011
[admin@server ~]$ ps -u apache -Z
LABEL PID TTY TIME CMD
system_u:system_r:httpd_t:s0 2914 ? 00:00:04 httpd
system_u:system_r:httpd_t:s0 2915 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 2916 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 2917 ? 00:00:00 httpd
...
system_u:system_r:httpd_t:s0 2918 ? 00:00:00 httpdCompared to the standard output of ls -l, there are several additional fields in the following format:
:::
The last field denotes something like a security label and consists of a combination of two elements:
- s0 — significance, also written as lowlevel-highlevel
- c0, c1… c1023 — category.
Changing access configurations
Use semodule to load SELinux modules, add and remove them.
[admin@server ~]$ semodule -l | wc -l # list of all modules
408
[admin@server ~]$ semodule -e abrt # enable - activate the module
[admin@server ~]$ semodule -d accountsd # disable - turn off the module
[admin@server ~]$ semodule -r avahi # remove - delete the moduleThe first command semanage login links the SELinux user with the operating system user, while the second outputs a list. Finally, the last command with the -r option removes the link mapping SELinux users to OS accounts. An explanation of the syntax for MLS/MCS Range can be found in the previous section.
[admin@server ~]$ semanage login -a -s user_u karol
[admin@server ~]$ semanage login -l Login Name SELinux User MLS/MCS Range Service
__default__ unconfined_u s0-s0:c0.c1023 *
root unconfined_u s0-s0:c0.c1023 *
system_u system_u s0-s0:c0.c1023 *
[admin@server ~]$ semanage login -d karol
The command semanage user is used to manage mappings between SELinux users and roles.
[admin@server ~]$ semanage user -l
Labeling MLS/ MLS/
SELinux User Prefix MCS Level MCS Range SELinux Roles
guest_u user s0 s0 guest_r
staff_u staff s0 s0-s0:c0.c1023 staff_r sysadm_r
...
user_u user s0 s0 user_r
xguest_u user s0 s0 xguest_r
[admin@server ~]$ semanage user -a -R 'staff_r user_r'
[admin@server ~]$ semanage user -d test_uCommand options:
- -a add a custom role mapping entry;
- -l list user and role mappings;
- -d delete a custom role mapping entry;
- -R list roles attached to the user;
Files, ports, and boolean values
Each SELinux module provides a set of file labeling rules, but you can also add your own rules if necessary. For example, we want to give the web server access rights to the folder /srv/www.
[admin@server ~]$ semanage fcontext -a -t httpd_sys_content_t "/srv/www(\/.*)?
[admin@server ~]$ restorecon -R /srv/www/The first command registers new labeling rules, while the second resets, or more accurately sets, the file types according to the current rules.
Similarly, TCP/UDP ports are marked in such a way that only the corresponding services can listen to them. For instance, to allow the web server to listen on port 8080, you need to execute the command.
[admin@server ~]$ semanage port -m -t http_port_t -p tcp 8080A significant number of SELinux modules have parameters that can take Boolean values. The complete list of such parameters can be viewed with getsebool -a. Boolean values can be modified using setsebool.
[admin@server ~]$ getsebool httpd_enable_cgi
httpd_enable_cgi --> on
[admin@server ~]$ setsebool -P httpd_enable_cgi off
[admin@server ~]$ getsebool httpd_enable_cgi
httpd_enable_homedirs --> off
Practicing accessing the PgAdmin web interface.
Let's consider a practical example: we installed pgAdmin 4-web on RHEL 7.6 for managing PostgreSQL databases. We went through a small of configuring pg_hba.conf, postgresql.conf, and config_local.py, set permissions on folders, and installed missing Python modules via pip. Everything is ready; let's run it and receive 500 Internal Server error.

We start with the usual suspects, checking /var/log/httpd/error_log. There are some interesting entries.
[timestamp] [core:notice] [pid 23689] SELinux policy enabled; httpd running as context system_u:system_r:httpd_t:s0
...
[timestamp] [wsgi:error] [pid 23690] [Errno 13] Permission denied: '/var/lib/pgadmin'
[timestamp] [wsgi:error] [pid 23690]
[timestamp] [wsgi:error] [pid 23690] HINT: You may need to manually set the permissions on
[timestamp] [wsgi:error] [pid 23690] /var/lib/pgadmin to allow apache to write to it.
At this point, most Linux administrators will feel a strong temptation to run setenforce 0 and call it a day. To confess, I did that the first time as well. This is certainly a solution, but far from the best.
Despite the cumbersome structures, SELinux can be user-friendly. Just install the setroubleshoot package and check the system log.
[admin@server ~]$ yum install setroubleshoot
[admin@server ~]$ journalctl -b -0
[admin@server ~]$ service restart auditd
Note that the auditd service needs to be restarted this way, not using systemctl, despite the presence of systemd in the OS. The system log will indicate not only the blocking fact, but also the reason and how to overcome the restriction..

Execute these commands:
[admin@server ~]$ setsebool -P httpd_can_network_connect 1
[admin@server ~]$ setsebool -P httpd_can_network_connect_db 1
Check access to the pgAdmin 4-web page; everything works.
Source: habr.com
