Introduction to SELinux concepts and management
Operating System and Software Versions
- Operating System: – Linux distribution agnostic
- Root access on a working Linux installation with a valid SElinux policy
- policycoreutils package: it provides getsebool, setsebool, restorecon utilities
- coreutils package: provides chcon utility
- policycoreutils-python package: provides semanage command
- policycoreutils-newrole: provides the newrole program
- setools-console: provides seinfo command
- # – requires given linux commands to be executed with root privileges either directly as a root user or by use of
- $ – requires given linux commands to be executed as a regular non-privileged user
SELinux (Security Enhanced Linux) is an implementation of a Mandatory Access Control permission system (MAC) in the Linux kernel. This type of access control differs from Discretionary Access Control systems (DAC) like ACLs and standard unix ugo/rwx permissions, in how the access to a resource is provided. In the case of MAC is not the owner of a resource the one who decides who and how can access it: this access is based on the relationships between domains and labels, dictated by a policy and enforced at the kernel level. Its important to say that SELinux enforced rules and standard system permissions are not mutually exclusive, and the former are implemented after the latter.
Possible SELinux status
There are three possible status of SELinux: disabled, permissive and enforcing. In the first case SELinux is completely off: it doesnt have any effect on the running system. When in permissive mode SELinux is active: it does log the policy violations, but it does nothing to block them. Finally, when in enforcing mode, SELinux actually enforces its policy.
There are many ways you can check SELinux status on your system. The first one is using the command called getenforce. This command just reports in what of the three status mentioned above SELinux is. To have a more verbose output you can use the sestatus utility. This is the output of the command on my system (CentOS 7):
SELinux status: enabled SELinuxfs mount: /sys/fs/selinux SELinux root directory: /etc/selinux Loaded policy name: targeted Current mode: enforcing Mode from config file: enforcing Policy MLS status: enabled Policy deny_unknown status: allowed Max kernel policy version: 28
Some useful information are provided: first of all the
SELinuxfs mountpoint, in this case /sys/fs/selinux.
SELinuxfs is a pseudo filesystem, just like /proc: it is populated at runtime by the Linux kernel and contains files useful to document SELinux status. The
SELinux root directory is, instead, the path used to keep SELinux configuration files, the main one being /etc/selinux/config (a symbolic link to this file is present also at /etc/sysconfig/selinux). Changing this file directly is the most straightforward way to change selinux status and mode. Lets take a brief look at its content:
$ 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 two values: # targeted - Targeted processes are protected, # minimum - Modification of targeted policy. Only selected processes are protected. # mls - Multi Level Security protection. SELINUXTYPE=targeted
The file is very well commented: by changing the values of SELINUX and SELINUXTYPE variables, we can set respectively the SELinux status and the SELinux mode. The possible modes are: targeted (the default), minimum and mls. The targeted mode is the default: when this mode is active all targeted processes are protected. The minimum mode is a subset of the first one, in which only specific processes are protected. Finally the mls policy its the most sophisticated one, based on the concept of security classification: from unclassified to top secret: it uses the Bell-La Padula model, developed for the US Department of Defense.
Changing SELinux status
To change the SELinux status at runtime you can use the
setenforce command. Its syntax is really simple: you specify the status you want to put SELinux in, choosing between Enforcing or Permissive or providing a boolean value referred to the enforcing status. What you cannot do with this command is to disable SELinux completely. To accomplish this (not recommended) and make other persistent changes, you must edit the main configuration file, as seen above. Changes made to this file are applied after a reboot.
How does SELInux work?
Basically SELinux works on the concept of entities: subjects, objects and actions. A subject is an application or a process (an http server for example), an object is a resource on the system, like a file, a socket, or a port. Finally an action is what that specific subject can perform on the object. A subject runs under a certain domain, which, for example, in the case of the httpd daemon is
httpd_t. This is easy verifiable by checking a running process with the ps command: all we need to do is to add the -Z switch (-Z switch is often associated with SELinux on the commands that support it, like ls for example):
$ ps -auxZ | grep httpd
The above command gives the following result (output truncated):
system_u:system_r:httpd_t:s0 apache 2340 0.0 0.2 221940 2956 ? S 14:20 0:00 /usr/sbin/httpd -DFOREGROUND
Running under the httpd_t domain, the httpd service (subject) can only access (action) resources (objects) within the associated SELinux types. A very simple way to verify this is by checking the /var/www directory. The httpd daemon must be able to access it, so lets check what type this directory has. We can do it by using the ls command with the -Z switch:
$ ls -dZ /var/www
The commands gives us this result:
The output shows us the complete SELinux context, and the /var/www directory being labeled with the ttpd_sys_content_t type. This makes perfectly sense: the targeted SELinux policy allows a process running under the httpd_t domain to access (in read only mode) all the files labeled with the httpd_sys_content_t type, no matter what DAC permissions are set on the file. If the process will attempt any action not expected by the policy, SELinux will log the error, and, if in enforcing mode, block the action itself.
We saw above how a representation of a complete SELinux context appears to be structured:
Lets analyze this structure by taking in consideration the first three parts (the fourth is referred to the MLS mode). The first section is about the SELinux users: every SELinux user has a different set of restrictions and is authorized
to play only a specific set of SELinux roles which give access to specific SELinux domains, which, in turn, are able to access only relate SELinux types.
Selinux users can play selinux roles can go to SELinux domains have access to SELinux types
To have a clear idea of the available SELinux users, we can run:
# semanage user -l
This command gives us a clear overall view of the users – roles relationships:
SELinux User Prefix MCS Level MCS Range SELinux Roles guest_u user s0 s0 guest_r root user s0 s0-s0:c0.c1023 staff_r sysadm_r system_r unconfined_r staff_u user s0 s0-s0:c0.c1023 staff_r sysadm_r system_r unconfined_r sysadm_u user s0 s0-s0:c0.c1023 sysadm_r system_u user s0 s0-s0:c0.c1023 system_r unconfined_r unconfined_u user s0 s0-s0:c0.c1023 system_r unconfined_r user_u user s0 s0 user_r xguest_u user s0 s0 xguest_r
Lets briefly see what some of the described SELinux users are authorized to do:
- guest_u: This type of user has no access to networking, no script execution privileges in /home, nor can make use of sudo or su commands to gain higher privileges. It can only use the guest_r role
- staff_u: The system users mapped to this SELinux user have access to GUI, to networking, and to the use of the sudo command to gain privileges. It can switch between the stuff_r, sysadm_r , system_r and unconfined_r roles
- sysadmin_u: Same as above, plus can use also the su command. It can only play the sysadm_r role
- system_u: This is the user assigned to system services, no system users should be mapped to it
- unconfined_u: This type of user has no restrictions. It has both unconfined_r and system_r roles associated with it
- xguest_u: This SELinux user has access to GUI and to the network, but only via the Firefox browser. It has not execution rights for files under /home and has only the xguest_r role associated with it
As you can see, SELinux user are identifiable, in the context, having the _u suffix. It should be clear that they are a totally different thing from system users. There exists a map between the two, and its possible to see it by running
semanage login -l command:
# semanage -l login
Which gives us the following output:
Login Name SELinux User MLS/MCS Range Service __default__ unconfined_u s0-s0:c0.c1023 * root unconfined_u s0-s0:c0.c1023 *
The system user root is mapped to the unconfined_u SELinux user, therefore has no restrictions. No other users are explicitly mapped, so they are, by default, associated to the unconfined_u SELinux user.
Changing SELinux User
At this point you may ask how its possible to set a map between a system user and a SELinux one. We accomplish this task by using semanage login command. In the following example I change the default mapping, associating the dummy user on my system to the guest_u SELinux user:
# semanage login -a -s guest_u dummy
The -a switch is short for –add and its used to add a record, while the -s one (short for –seuser) specifies the SELinux user the system user should be mapped to. Lets now run again semanage login -l to see if something changed:
Login Name SELinux User MLS/MCS Range Service __default__ unconfined_u s0-s0:c0.c1023 * dummy guest_u s0 * root unconfined_u s0-s0:c0.c1023 * system_u system_u s0-s0:c0.c1023 *
As expected the system dummy user is now associated with the guest_u SELinux user which, as said before, has no access to the network. Lets verify it in the most simple way: we try to ping google and see what the result is:
[dummy@linuxconfig ~]$ ping google.com ping: socket: Permission denied
As expected, the dummy user is not allowed to use the network, so the ping command fails. To delete the mapping we use the -d switch (short for –delete):
# semanage login -d -s guest_u dummy
Not having a specific mapping, the dummy user will fallback to the unconfined_u SELinux user. Since the latter has no restrictions, if we try again the above command, it should now be successful:
[dummy@linuxconfig ~]$ ping google.com PING google.com (126.96.36.199) 56(84) bytes of data. 64 bytes from mil04s29-in-f14.1e100.net (188.8.131.52): icmp_seq=1 ttl=52 time=29.2 ms 
Keep in mind that changes in the mapping between users and SELinux users will be effective only after a new login.
The second part in a SELinux context is about roles. As you can see from the output of
semanage user -l above, each SELinux user can play a specified set of SELinux roles: when there are multiple roles for a SELinux user, the user can also switch between them using the
newrole command, using the following syntax:
$ newrole -r newrole
To check what domains a specific role can access, you should run the
seinfo command. This is provided by the
setools-console package. For example to check what domains are accessible from the stuff_r role, we run:
# seinfo -rstuff_r -x
$ seinfo -rstaff_r -x (output truncated) staff_r Dominated Roles: staff_r Types: abrt_helper_t alsa_home_t antivirus_home_t httpd_user_content_t httpd_user_htaccess_t [...]
Domains and types
The third part of a SELinux context is about domains and types, and is identifiable by having the _t suffix in the context representation. We refer to it as type if we are talking about an object, or as domain if we are talking about a process. Lets take a look.
I have created a simple .html file inside the default apache VirtualHost on my CentOS 7 machine: as you can see the file inherited the SELinux context of the directory it was created in:
-rw-r--r--. root root unconfined_u:object_r:httpd_sys_content_t:s0 test.html
httpd_sys_content_t, the file can be read by the httpd process, as confirmed by navigating to it in the browser.
Now lets try to change the file type and see the effect this change has. To manipulate the SELinux context we use the
# chcon -t user_home_t /var/www/html/test.html
We changed the SELinux type of the file to
user_home_t: this is the type used by the files located in the users
home directories by default. Running ls -Z on the file gives us the confirmation:
If we now try to reach the file from the browser, as expected.
chcon command can be used not only to change the type of the file, but also the user and the role part of the selinux context. When using it to change a directory context it can also run recursively with the -R switch, and can assign a context also by reference: in this case we dont specify the parts of the context to be changed directly, but we provide the reference to the file or directory the context should conform to. For example, lets make the test.html file above, acquire the context of the /var/www/html directory:
# chcon --reference /var/www/html /var/www/html/test.html && ls -Z /var/www/html/test.html
We can see from the output of the commands above, that now the context of the file has changed again, and its now the same as the one of the /var/www/html directory:
Notice that the changes made with chcon command, will survive a reboot but not a relabeling of the files: in that case the files will be set in accordance to the SELinux original policy, and the changes will be lost. So how can we make the change persistent? We must add new rule to the SELinux policy using semanage command.
Lets say we want to add a rule dictating that all the files created in the directory /home/egdoc/test should have, by default the
httpd_sys_content_t type. Here is the command we should run:
semanage fcontext -a -t httpd_sys_content_t /home/egdoc/test(/.*)?
First we invoke the semanage command specifying
fcontext for modifying file contexts, then we add the
-a switch to add a record and the
-t one, to specify we want to change the type part of the context to the one immediately following.
Finally, we provide the directory path together with a regular expression that means: /home/egdoc/test path followed by the / character, followed by any number of any character, the entire expression being match 0 or 1 time. This regular expression will match all the file names.
We now run the
restorecon command with the
-R (recursive) option on the directory, to apply the policy. Since now the rule we added above is part of the policy itself, all the files contained in the directory, and also the newly created ones, will have the context we specified in the rule.
SELinux boolean settings
Selinux booleans settings can change SELinux behavior, and are managed by the use of boolean values. We can interact with them by the use of two commands:
setsebool, the first one being used to query the state of an option and second one to change it.
If we pass the option we want to check to getsebool, it will give us just the state of that option, if we provide it with the
-a switch it will instead show us all available settings and their respective boolean state. For example if we want to check the status of options related to httpd we could run:
$ getsebool -a | grep httpd
Here is a very short excerpt of the output:
[firstname.lastname@example.org ~]$ getsebool -a | grep httpd httpd_anon_write --> off httpd_builtin_scripting --> on [...]
Lets now try to change the state of the httpd_anon_write option, and activate it. As mentioned above we use setsebool for the task:
# setsebool httpd_anon_write 1
If we now check the value of the option, it should have been activated:
[email@example.com ~]$ getsebool -a | grep httpd_anon_write httpd_anon_write --> on
All went as expected. However, the changes made this way will not survive a reboot. To accomplish this task we must use the same command, but adding the
-P switch: when using it, the changes will be written to the policy and they will persist.
There are many things one should consider when using SELinux, and fine-tuning it to obtain a specific behavior, while maintaining the less possible permissions can be a time-consuming task. Nonetheless is not a good idea, in myopinion, to turn it completely off. Keep experimenting until you are satisfied with the results and you reach the wanted setup:
you will gain both in security and knowledge.