SELinux in a practical way

SELinux is often seen as an evil, complex, unnecessary and especially annoying security component which exists in a lot of Linux distributions. Often you can hear something like: “Disable SELinux and try again” or , “The first thing I do on a new server is to disable SELinux”. The problem with SELinux is that it looks very complex and that it looks like you need to spend ages to understand it. In this post, I’ll try to explain a few basic SELinux principles and especially focus on daily, practical problems related to SELinux and their solutions. Don’t forget that there’s a very good reason for SELinux and it would be a shame to not use it.

SELinux stands for Security Enhanced Linux. It’s a kernel security module that is responsible for mandatory access control. This means that something won’t work unless it’s explicitly allowed by SELinux. The big advantage of SELinux is that it makes your system much more secure because it provides a more granular approach to security. It’s way more flexible that standard permissions.

When does SELinux usually get in your way?

Most distributions that come with SELinux already have standard set of rules, called a policy. This policy allows you to do most, unharmful, things on you system without you really noticing that it’s running SELinux. Usually SELinux comes in the picture as soon as you start to try non-standard stuff. For example trying to run Apache on a port different than 80 or 443, have your webroot in another, non-standard location or trying to communicate between different kinds of services.

When a new packages get’s in the repository, and it requires special permissions to work with SELinux, the policy usually get’s an update. Of course if you use software that’s coming from another source, there’s a big chance that your policy doesn’t contain all required SELinux entries and won’t run as out of the box as you expected.

How to see if it’s really SELinux that’s in the way?

The best way to check if a problem is caused by SELinux is to tail the audit log in /var/log/audit/audit.log and look for entries of type AVC.

Some examples:

For example, we changed the config of Apache to let it listen on port 90 instead of port 80.

When trying to start the Apache, we get an error message:

The following appears in the syslog/journal:

As you see, there isn’t anything that tells us that the above error is SELinux related and this is where SELinux usually tends to be annoying. In most situations, you will start to check various settings, permissions,… and you lose a lot of time to double check and see that actually everything should be correct.

A good thing for such situations is to check the audit log immediately. This way, you can be sure that the problem is or isn’t SELinux related:

The above line in the audit log tells us that it was SELinux that blocked httpd to bind on TCP port 90.

Another example is when you try to access a file which is copied from somewhere else or which has been extracted from an archive and doesn’t have the correct SELinux security context.

Imagine that I created a nice html-file in my home directory and I decide to make it accessible via my webserver. To be sure, I even change the owner of the file to apache and give it all possible permissions (not a good idea):

You would expect the above to work fine but when I try to access the file, I get a 403 Forbidden in my browser and the following is in the Apache error log:

As with the previous example, you can really get frustrated with such issue since there isn’t any hint or clue related to SELinux.

When checking the audit log, we can see that indeed SELinux blocked access to the file for Apache:

The above line in the logfile tells us that httpd can’t access a file with a security contect of user_home_t.

How to solve SELinux-related issues

In the above examples, I showed how to know that SELinux is blocking something. Now I’ll try to explain what to do in such case to solve the problem and allow the action.

I’m completely aware that there are SELinux contexts, labels, classes, ports,… but for me, there are generally four scenarios to solve SELinux issues:

  • SELinux is blocking because of file-context or labels
  • SELinux is blocking “normal” functionality because it could be dangerous
  • SELinux is blocking because the use of non-standard locations/ports
  • SELinux custom issues

Each of them needs to be solved in a different way to do things correctly. Using a custom module to solve a context-related problem would solve the issue but potentially creates a security hole. The order which I listed here is the order which you should follow for most cases.

Solving SELinux problems related to file-context or labels

The first thing you should try in order to solve your SELinux issue, is to check the file lables. You can usually see that a problem is related to the file-context when the log line has tclass=file in it. This means that a file (or directory) has the wrong “label”. Every file on a SELinux system get’s such label and this greatly influences how SELinux treats every file.

In the above example, I moved a file from my homedirectory to the apache webroot. When moving files, permissions aren’t touched so the initial security context (or label) stayed on the file.

Before the move:

After the move (and even change of permissions):

As you can see, the file in the webroot has a context of user_home_t. SELinux doesn’t allow Apache to access files with such label.

To solve this, we need to set the correct label on the file. The easiest way is to do relabel the file according to the policy:

The label changed from user_home_t to httpd_sys_content_t and this is a context that allows httpd to access the file.

In this case, we could easily solve the problem by relabeling the file. SELinux knows which context is needed for files in /var/www/html to make things work.

In some cases, for example when you would want your homedir to be the webroot for Apache, the policy will not set the correct context for your file when doing a relabel. In such case you’ll need to set the context yourself but it’s important to know that in some cases, your system might relabel all files on the filesystem (for example after a policy upgrade or disk restore).

To prevent problems with a filesystem relabel, you can change the policy regarding labels yourself in /etc/selinux/targeted/contexts/files/.

Allowing “normal” functionality that could be dangerous

Setting the correct SELinux label on files can’t solve every SELinux problem. The next thing to try is setting one of the SELinux booleans. SELinux has a whole list of booleans to set that influence the behavior of SELinux.

To get a complete list of booleans an their value:

You could browse the list of booleans and guess which one to set to solve your problem. A better way is to use audit2allow.

Audit2allow analyses a line in the audit log and will propose you with a possible solution. For example when we want Apache to send emails with PHP directly to an SMTP server, we would get something like this in the audit log:

To find out if the we can solve this issue by setting an SELinux boolean, we can pipe the line in the logfile to audit2allow:

As you can see, audit2allow proposes us multiple solutions. A good match in this case would be to set the boolean httpd_can_sendmail to 1. To do so, use setsebool:

The -P option makes this change permanent or persistent over a reboot.

After setting the boolean to true, Apache is allowed to connect to an SMTP-server without SELinux getting in the way.

Solving SELinux issues related to the use of non-standard locations/ports

Next in row are issues that can be solved by setting certain values for SELinux with semanage. The concept is almost the same as with the booleans, only now you can set specific values and not allow or disbale something. In 99% of the cases, this method is what you would use to allow daemons to listen on non-standard ports.

When we use the example where we were trying to let httpd bind to port 90, we got the following message in the audit log:

As with the previous problem, we can pipe this logline to audit2allow in order to see how we can solve this problem:

Unlike the previous use of audit2allow, we do not get suggestions related to sebooleans. We could build a module that allows the above suggestion by audit2allow but that would cause httpd to be able to listen on any reserved port.

Better in this case is to use semanage to set the allowed ports for httpd.

To get a list of allowed ports for most daemons:

To allow Apache to bind on port 90:

Directly after changing the allowed ports with semanage, we see it appear in the list.

Now let’s restart Apache to test if this change in SELinux worked:

Solve SELinux issues that can be resolved by loading a custom module

If none of the above methods work to solve your issue, you can build a custom SELinux module. This can be done manually or by using audit2allow.

An example where you use such type of solution would be a situation where multiple processes need to access a file and each of them requires a specific SELinux security context (or label). To solve this issue you can’t change the label since that would cause the other process to be denied and vice versa.

Imagine a situation where you want FTP-access to the webroot of your server. This would require a file label that allows httpd to access files and would require another label that allows proftpd to allow the same file. A file can only have one label. Since the files for this example are located in /var/www/html, it’s a good idea to keep their default labels defined in the policy.

One solution to this problem could be to set a boolean that allows the FTP-server to have full access to all files on the system but probably that’s not what we want.

Besides allowing access to all files, we can create a custom module that will allow the FTP-server to access files that have the correct label for http.

Without any action, a line similar like this one would appear in the audit log after trying to access a file with a httpd_sys_content_t context via FTP:

Audit2allow tells us indeed that we can give access to all files for FTP:

For our solution, we need the last line in the audit2allow output. We can ask audit2allow to translate this to an SELinux module:

Option -m will display the source for a custom module that allows the specific action which was denied. The syntax isn’t very hard and it’s quit easy to read that a service with type ftpd_t will get read access to directories with label httpd_sys_content_t.

To build and activate the module as it was proposed by audit2allow:

The -M option will build the source as it was displayed and generates a .pp file (which is the actual module) and a .te file (which is the source).

After building the module, it needs to be installed with semodule.

The above actions will allow ftpd to list a directory that has a httpd_sys_content_t label but won’t allow read or write of the files in those directories. We could repeat all actions multiple times for each action we need but that would take a lot of time and generate a lot of different modules.

A more clean way is to edit the source of the custom module yourself and allow all actions that you need in one module:

The previous command (audit2allow -M) create the source of the module in a .te file which we can use as a base for our custom module.

Edit the file to look like this:

After changing the source, we need to create/build the actual SELinux module ourselves and activate it:

After these steps, we have a persistent SELinux module that allows proftpd to read and write files on our webroot.

As you can see, SELinux isn’t that hard but you need to invest some time to get trough the basics. Once you did, you will feel satisfied for running machines that are more secure and have all functionality enabled.

2 thoughts on “SELinux in a practical way

  1. very good article. selinux is not hard but problem with material or proper documentation to explain in a simple and better way. I have seen most of the selinux documentation or article fail to explain properly or take complex approach, which adds further complexity to already complex subject.

    Your article start with simple examples and covers most the stuff in a single page. I would suggest to write few more article on SELINUX if you have some time.

    Srinivas Kotaru

Leave a Reply

Your email address will not be published. Required fields are marked *