Need to know about Files and Processes in SELinux?
This guide will help you.
The purpose of SELinux is to secure how processes access files in a Linux environment. Without SELinux, a process or application like the Apache daemon will run under the context of the user that started it.
So if your system is compromised by a rogue application that's running under the root user, the app can do whatever it wants because root has all-encompassing rights on every file.
A Linux kernel security module, SELinux brings heightened security for Linux systems.
A file security context is a type and a process security context is a domain.
Here at Ibmi Media, as part of our Server Management Services, we regularly help our Customers to perform Linux related queries.
In this context, we shall look into Files and Processes in SELinux on CentOS 7.
Here, we run the commands as the root user.
Initially, we create four user accounts to demonstrate SELinux capabilities.
1. regularuser
2. switcheduser
3. guestuser
4. restricteduser
As the root user, we run the following command to add the regularuser account:
# useradd -c “Regular User” regularuser
Then we change its password:
# passwd regularuser
In the output we give the new password:
Changing password for user regularuser.
New password:
Retype new password:
passwd: all authentication tokens updated successfully.
Similarly, we create the other accounts too:
# useradd -c “Switched User” switcheduser
# passwd switcheduser
# useradd -c “Guest User” guestuser
# passwd guestuser
# useradd -c “Restricted Role User” restricteduser
# passwd restricteduser
SELinux secure how processes access files in a Linux environment. Without it, if the system compromises under the root user, the app can do whatever it wants because the root has all-encompassing rights on every file.
With SELinux, a process or application will have only the rights it needs to function and NOTHING more.
The SELinux policy for the application will determine what types of files it needs access to and what processes it can transition to.
A policy is basically a set of rules that maps processes and users to their rights.
Initially, we will understand SELinux file contexts.
If we perform a regular ls -l command against the /etc directory:
# ls -l /etc/*.conf
Our output will be similar to:
…
-rw-r–r–. 1 root root 19 Aug 19 21:42 /etc/locale.conf
-rw-r–r–. 1 root root 662 Jul 31 2013 /etc/logrotate.conf
-rw-r–r–. 1 root root 5171 Jun 10 07:35 /etc/man_db.conf
-rw-r–r–. 1 root root 936 Jun 10 05:59 /etc/mke2fs.conf
…
Here, we will add the -Z flag:
# ls -Z /etc/*.conf
This gives an extra column of information after the user and group ownership:
…
-rw-r–r–. root root system_u:object_r:locale_t:s0 /etc/locale.conf
-rw-r–r–. root root system_u:object_r:etc_t:s0 /etc/logrotate.conf
-rw-r–r–. root root system_u:object_r:etc_t:s0 /etc/man_db.conf
-rw-r–r–. root root system_u:object_r:etc_t:s0 /etc/mke2fs.conf
…
Then let us take a closer look at one of the security contexts.
-rw-r–r–. root root system_u:object_r:etc_t:s0 /etc/logrotate.conf
Here, the security context is:
system_u:object_r:etc_t:s0
We can see four parts and each separates by a colon (:). The first part is the SELinux user context for the file. We can see that it is system_u.
The second part specifies the SELinux role, which is object_r.
The most important part is the third one, etc_t. This is the part that defines what type the file or directory belongs to.
As another example, let us check the type contexts for user home directories:
# ls -Z /home
Usually, home directories have a different context type: user_home_dir_t:
drwx——. guestuser guestuser unconfined_u:object_r:user_home_dir_t:s0 guestuser
drwx——. root root system_u:object_r:lost_found_t:s0 lost+found
drwx——. regularuser regularuser unconfined_u:object_r:user_home_dir_t:s0 regularuser
drwx——. restricteduser restricteduser unconfined_u:object_r:user_home_dir_t:s0 restricteduser
drwx——. switcheduser switcheduser unconfined_u:object_r:user_home_dir_t:s0 switcheduser
drwx——. sysadmin sysadmin unconfined_u:object_r:user_home_dir_t:s0 sysadmin
The fourth part of the security context is s0.
Basically, this is another way of enforcing the SELinux security policy and shows the sensitivity of the resource (s0).
Initially, we start the Apache and SFTP services:
# service httpd start
# service vsftpd start
We can run the ps command with a few flags:
# ps -efZ | grep 'httpd\|vsftpd'
Generally, the -Z flag is for displaying SELinux contexts.
The output gives us the user running the process, the process ID, and the parent process ID:
system_u:system_r:httpd_t:s0 root 7126 1 0 16:50 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 7127 7126 0 16:50 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 7128 7126 0 16:50 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 7129 7126 0 16:50 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 7130 7126 0 16:50 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:httpd_t:s0 apache 7131 7126 0 16:50 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
system_u:system_r:ftpd_t:s0-s0:c0.c1023 root 7209 1 0 16:54 ? 00:00:00 /usr/sbin/vsftpd /etc/vsftpd/vsftpd.conf
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 root 7252 2636 0 16:57 pts/0 00:00:00 grep –color=auto httpd\|vsftpd
Here, the security context is this part:
system_u:system_r:httpd_t:s0
It has four parts: user, role, domain, and sensitivity. The user, role and sensitivity work like the same contexts for files.
However, the domain is unique to processes.
For instance, above we saw that a few processes run within the httpd_t domain, while one runs within the ftpd_t domain.
The domain gives the process a context to run within. It tells the process what it can do and what it cannot do.
This ensures that each process domain can act on only certain types of files and nothing more.
With this model, the worst a hijacker can do is damage the files it has access to.
For example, the vsftp daemon will not have access to files like Sendmail or samba.
This restriction enforces as the SELinux policy loads into memory, and thus the access control becomes mandatory.
In order to run, a process have to access its files and perform some actions on them (open, read, modify, or execute). Each process has access to certain types of resources only (files, directories, ports, etc.).
Generally, the access rules follow a standard allow statement structure:
allow <domain> <type>:<class> { <permissions> };
A generic allow statement means:
i. If a process is of a certain domain
ii. And the resource object it is trying to access is of a certain class and type
iii. Then allow the access
iv. Else deny access
For instance, let us consider the security contexts of the httpd daemon running on the CentOS 7 system:
system_u:system_r:httpd_t:s0 7126 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 7127 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 7128 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 7129 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 7130 ? 00:00:00 httpd
system_u:system_r:httpd_t:s0 7131 ? 00:00:00 httpd
The default home directory for the webserver is /var/www/html.
Hence, let us create a file within that directory and check its context:
# touch /var/www/html/index.html
# ls -Z /var/www/html/*
The file context for our web content will be httpd_sys_content_t:
-rw-r–r–. root root unconfined_u:object_r:httpd_sys_content_t:s0 /var/www/html/index.html
We use the sesearch command to check the type of access for the httpd daemon:
# sesearch –allow –source httpd_t –target httpd_sys_content_t –class file
Here, the source domain is httpd_t, the same domain Apache is running in.
The output will be similar to:
Found 4 semantic av rules:
allow httpd_t httpd_sys_content_t : file { ioctl read getattr lock open } ;
allow httpd_t httpd_content_type : file { ioctl read getattr lock open } ;
allow httpd_t httpd_content_type : file { ioctl read getattr lock open } ;
allow httpd_t httpdcontent : file { ioctl read write create getattr setattr lock append unlink link rename execute open } ;
Notice the first line:
allow httpd_t httpd_sys_content_t : file { ioctl read getattr lock open } ;
It says that the httpd daemon has I/O control, read, get attribute, lock, and open access to files of the httpd_sys_content type.
In this case, our index.html file has the same type.
Let us first modify the web page (/var/www/html/index.html):
<html>
<title>
This is a test web page
</title>
<body>
<h1>This is a test web page</h1>
</body>
</html>
Next, we will change the permission of the /var/www/ folder and its contents, followed by a restart of the httpd daemon:
# chmod -R 755 /var/www
# service httpd restart
Then we try to access it from a browser.
Depending on how the server is set up, we may have to enable port 80 in the IPTables firewall for allowing incoming HTTP traffic from outside the server.
Next, let us change the context of the file. We will use the chcon command for it.
The –type flag for the command allows us to specify a new type for the target resource. Here, we are changing the file type to var_t.
# chcon –type var_t /var/www/html/index.html
We can confirm the type change:
# ls -Z /var/www/html/
-rwxr-xr-x. root root unconfined_u:object_r:var_t:s0 index.html
Then, when we try to access the web page, we may come across a Forbidden error, or the generic CentOS “Testing 123” page.
The error occurs because we changed the context of the index.html file to var_t and Apache can no longer read it.
To resolve this, we change the file type with the restorecon command. The -v shows the change of context labels:
# restorecon -v /var/www/html/index.html
restorecon reset /var/www/html/index.html context unconfined_u:object_r:var_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
Now, when we access the page it will show, “This is a test web page” text again.
Making sure files and directories have the correct context is pivotal to making sure SELinux is behaving as it should.
Context inheritance means unless specified by the policy, processes, and files are created with the contexts of their parents.
For instance, if we have a process “proc_a” spawning another process “proc_b”, the spawned process will run in the same domain as “proc_a” unless the SELinux policy specifies otherwise.
Similarly, if we have a directory with a type of “some_context_t”, any file or directory under it will have the same type of context unless the policy says otherwise.
To illustrate this, let us check the contexts of the /var/www/ directory:
# ls -Z /var/www
The HTML directory within /var/www/ has the httpd_sys_content_t type context. The index.html file within it has the same context (ie, the context of the parent):
drwxr-xr-x. root root system_u:object_r:httpd_sys_script_exec_t:s0 cgi-bin
drwxr-xr-x. root root system_u:object_r:httpd_sys_content_t:s0 html
In a copy operation, the copied file or directory will assume the type context of the target location.
In the command below, we copy the index.html file (with “httpd_sys_content_t” type context) to the /var/ directory:
# cp /var/www/html/index.html /var/
Now we can see it has changed to var_t, the context of its current parent directory:
# ls -Z /var/index.html
-rwxr-xr-x. root root unconfined_u:object_r:var_t:s0 /var/index.html
We can override this change of context by the –preserver=context clause in the cp command.
We preserve the original contexts when files or directories move.
In the following command, we move the /var/index.html to the /etc/ directory:
# mv /var/index.html /etc/
Eventually, if we check we can see that the var_t context is preserved under the /etc/ directory:
# ls -Z /etc/index.html
-rwxr-xr-x. root root unconfined_u:object_r:var_t:s0 /etc/index.html
We do this to simplify the backup process and also to tighten security.
Initially, we will create a directory named www under the root. We will also create a folder called HTML under www.
# mkdir -p /www/html
The ls -Z command will show these directories have been created with the default_t context:
# ls -Z /www/
drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 html
Then we copy the contents of the /var/www/html directory to /www/html:
# cp /var/www/html/index.html /www/html/
We now edit the httpd.conf file to point to this new directory as the website’s root folder. We will also have to relax the access rights for this directory.
# vi /etc/httpd/conf/httpd.conf
i. First, we comment out the existing location for document root and add a new DocumentRoot directive to /www/html:
# DocumentRoot “/var/www/html”
DocumentRoot “/www/html”
ii. We also comment out the access rights section for the existing document root and add a new section:
#<Directory “/var/www”>
# AllowOverride None
# Allow open access:
# Require all granted
#</Directory>
<Directory “/www”>
AllowOverride None
# Allow open access:
Require all granted
</Directory>
iii. Finally, we restart the httpd daemon:
# service httpd restart
Once done, the web page will give us the same “403 Forbidden” error (or default “Testing 123” page).
This happens because the index.html file’s context changes during the copy operation. We need to change it back to its original context (httpd_sys_content_t).
To change file contents we can use the commands chcon and restorecon. However, this method is only temporary: a file system relabel or restorecon command will revert the file back to its original context.
Also, running chcon requires knowledge of the correct context for the file.
If we run restorecon, the file will have the correct context re-applied and the changes will be made permanent.
Conveniently, SELinux “remembers” the context of every file or directory in the server.
In CentOS 7, contexts of files already existing in the system are in the /etc/selinux/targeted/contexts/files/file_contexts file.
Contexts of new directories and files are in the /etc/selinux/targeted/contexts/files/file_contexts.local file.
So when we run the restorecon command, SELinux will look up the correct context from one of these two files and apply it to the target.
The command below shows an extract from one of the files:
# cat /etc/selinux/targeted/contexts/files/file_contexts
…
/usr/(.*/)?lib(/.*)? system_u:object_r:lib_t:s0
/opt/(.*/)?man(/.*)? system_u:object_r:man_t:s0
/dev/(misc/)?agpgart -c system_u:object_r:agp_device_t:s0
/usr/(.*/)?sbin(/.*)? system_u:object_r:bin_t:s0
/opt/(.*/)?sbin(/.*)? system_u:object_r:bin_t:s0
/etc/(open)?afs(/.*)? system_u:object_r:afs_config_t:s0
…
To permanently change the context of our index.html file under /www/html, we have to follow a two-step process.
i. First, we run the semanage fcontext command.
This will write the new context to the /etc/selinux/targeted/contexts/files/file_contexts.local file. We do this for both directories.
# semanage fcontext –add –type httpd_sys_content_t “/www(/.*)?”
# semanage fcontext –add –type httpd_sys_content_t “/www/html(/.*)?”
ii. To ensure, we can check the file context database:
# cat /etc/selinux/targeted/contexts/files/file_contexts.local
iii. We can see the updated contexts as:
# This file is auto-generated by libsemanage
# Do not edit directly.
/www(/.*)? system_u:object_r:httpd_sys_content_t:s0
/www/html(/.*)? system_u:object_r:httpd_sys_content_t:s0
iv. Then, we run the restorecon command to relabel the file or directory:
# restorecon -Rv /www
restorecon reset /www context unconfined_u:object_r:default_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
restorecon reset /www/html context unconfined_u:object_r:default_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
restorecon reset /www/html/index.html context unconfined_u:object_r:default_t:s0->unconfined_u:object_r:httpd_sys_content_t:s0
v. Now accessing the web page will work.
In addition, the nifty tool, matchpathcon can help troubleshoot context problems.
This command will look at the current context of a resource and compare it with what is listed under the SELinux context database. If different, it will suggest the changes.
For instance, in the /www/html/index.html file we use the -V flag that verifies the context:
# matchpathcon -V /www/html/index.html
The matchpathcon output will show that the context is verified.
/www/html/index.html verified.
On the other hand, for an incorrectly labeled file, the message will say:
/www/html/index.html has context unconfined_u:object_r:default_t:s0, should be system_u:object_r:httpd_sys_content_t:s0
Domain transition is the method where a process changes its context from one domain to another.
The illustration of the same is like this:
Process a -> Executable file -> Process b
Context a -> Context x -> Context b
Domain transition is fairly common in SELinux.
For instance, consider the vsftpd process on the server. If it is not running, we can run the service vsftpd start command to start the daemon.
Next, we consider the systemd process. It is the replacement of the System V init process and runs within a context of init_t:
# ps -eZ | grep init
system_u:system_r:init_t:s0 1 ? 00:00:02 systemd
system_u:system_r:mdadm_t:s0 773 ? 00:00:00 iprinit
The process running within the init_t domain is a short-lived one.
To check the domain contexts of the files and processes, we run:
# ls -Z /usr/sbin/vsftpd
-rwxr-xr-x. root root system_u:object_r:ftpd_exec_t:s0 /usr/sbin/vsftpd
In order to check the process, we run:
# ps -eZ | grep vsftpd
system_u:system_r:ftpd_t:s0-s0:c0.c1023 7708 ? 00:00:00 vsftpd
So here the process running in the init_t domain is executing a binary file with the ftpd_exec_t type. That file starts a daemon within the ftpd_t domain.
The application or the user cannot control this transition.
This has been stipulated in the SELinux policy that loads into memory as the system boots.
In SELinux, pre-written policies control such access.
Domain transition is subject to three strict rules:
i. The parent process of the source domain must have the execute permission for the application sitting between both the domains.
ii. The file context for the application must identify as an entrypoint for the target domain.
iii. The original domain must allow transitioning to the target domain.
Taking the vsftpd daemon example above, let us run the sesearch command with different switches to see if the daemon conforms to these three rules.
i. First, the source domain init_t needs to have execute permission on the entrypoint application with the ftpd_exec_t context. So if we run the following command:
# sesearch -s init_t -t ftpd_exec_t -c file -p execute -Ad
The result shows that processes within init_t domain can read, get attribute, execute, and open files of ftpd_exec_t context:
Found 1 semantic av rules:
allow init_t ftpd_exec_t : file { read getattr execute open } ;
ii. Then, we check if the binary file is the entrypoint for the target domain ftpd_t:
# sesearch -s ftpd_t -t ftpd_exec_t -c file -p entrypoint -Ad
Found 1 semantic av rules:
allow ftpd_t ftpd_exec_t : file { ioctl read getattr lock execute execute_no_trans entrypoint open } ;
The output verifies the same.
iii. And finally, the source domain init_t needs to have permission to transition to the target domain ftpd_t:
# sesearch -s init_t -t ftpd_t -c process -p transition -Ad
As we can see below, the source domain has permission:
Found 1 semantic av rules:
allow init_t ftpd_t : process transition ;
This article covers Files and Processes in SELinux. Basically, managing file and process context are at the heart of a successful SELinux implementation.
With SELinux, a process or application will have only the rights it needs to function and NOTHING more. The SELinux policy for the application will determine what types of files it needs access to and what processes it can transition to.
SELinux policies are written by app developers and shipped with the Linux distribution that supports it. A policy is basically a set of rules that maps processes and users to their rights.
SELinux enforces something we can term as “context inheritance”. What this means is that unless specified by the policy, processes and files are created with the contexts of their parents.
So if we have a process called “proc_a” spawning another process called “proc_b”, the spawned process will run in the same domain as “proc_a” unless specified otherwise by the SELinux policy.
SELinux in Action: Testing a File Context Error
1. First, let's create a directory named www under the root. We will also create a folder called html under www:
mkdir -p /www/html
2. If we run the ls -Z command, we will see these directories have been created with the default_t context:
ls -Z /www/
drwxr-xr-x. root root unconfined_u:object_r:default_t:s0 html
3. Next we copy the contents of the /var/www/html directory to /www/html:
cp /var/www/html/index.html /www/html/
The copied file will have a context of default_t. That's the context of the parent directory.
We now edit the httpd.conf file to point to this new directory as the web site's root folder.
i. We will also have to relax the access rights for this directory.
vi /etc/httpd/conf/httpd.conf
ii. First we comment out the existing location for document root and add a new DocumentRoot directive to /www/html:
# DocumentRoot "/var/www/html"
DocumentRoot "/www/html"
iii. We also comment out the access rights section for the existing document root and add a new section:
#<Directory "/var/www">
# AllowOverride None
# Allow open access:
# Require all granted
#</Directory>
<Directory "/www">
AllowOverride None
# Allow open access:
Require all granted
</Directory>
We leave the location of the cgi-bin directory as it is. We are not getting into detailed Apache configuration here; we just want our site to work for SELinux purposes.
iv. Finally, restart the httpd daemon:
service httpd restart
Once the server has been restarted, accessing the web page will give us the same “403 Forbidden” error (or default “Testing 123” page) we saw before.
The error is happening because the index.html file's context changed during the copy operation. It needs to be changed back to its original context (httpd_sys_content_t).