SELinux deep dive

selinuxAt the start of my Linux adventure, I’ve read a lot about installing software xyz and then of course tried it myself. What I thought was odd, was that so many guides advocate to disable SELinux. I didn’t even understand what SELinux was at the time, but I did know it was a security mechanism.

There are many examples of such guides and what comes to mind at the moment is Zimbra mailserver and ownCloud (as a side note, I’ve already switched to Nextcloud) Not coincidentally these are services I need and now I have to disable my security? Why is everybody so relaxed about just disabling it?

The answer to the above questions is: ignorance. I do not mean this in a negative way, absolutely not because I love people writing guides for other people, to help them out. It’s just because people don’t understand it, do not know what it does and when they disable it, their software works. “Well, let’s just leave it at that then” is the thought.

SELinux history

As I am telling in this blog over and over again, I love security almost as much as I love Linux. I think in this last year I’ve became pretty aware off security related stuff. I’m always thinking about how to improve security, trying to get into the head of hackers and how to protect my devices from harm.

So when somebody is telling me: ah, just disable that security mechanism, red flags pop in my mind all over the place. This can’t be right. And it isn’t. SELinux was originally developed by the NSA for securing their systems and was later accepted in the Linux kernel in 2003. Its basis builds on the concepts of least privileges, giving a process exactly the rights it needs to perform a task.

When it was just integrated in the kernel it was a hard task to get it running without breaking at least a service or two. The “just disable it” idea originated from this time. But now many years, versions and tools later this really is no longer the case. Every decent system can leave it enabled and ‘Enforcing’ and you really should.

Before we go any further, install the below tools so you are able to perform any SELinux command you need.

# yum install policycoreutils-python setools-console setroubleshoot setroubleshoot-server

That will give you the complete toolbox.

Mandatory Access Control

SELinux is an addition to the standard Linux file permissions. The standard permissions are from the category Discretionary Access Control (DAC). The problem with DAC is, is that file owners can mess things up. File owners have full control and can therefore for instance delete stuff they shouldn’t.

In comes Mandatory Access Control (MAC). The MAC mechanism gets enforced by the Linux kernel. It is system wide and owners can’t mess things up. SELinux limits the resources a process can access, even when running as root. That being said, the focus of MAC is not normal users, but services running with elevated permissions.

Let’s take a simple access request. First the request has to deal with DAC. When it is denied, the request is dropped and MAC doesn’t even come into play. When DAC allows it though, it goes through to MAC and thus has to pass another barrier. Only when MAC allows it, the request is allowed.

How does SELinux determine if something is allowed or not? SELinux is all about labels. Everything on the filesystem is being labeled: files, directories, users, groups, processes, etc. Then we give SELinux a policy and in combination with labels, SELinux makes access control decisions.

For processes only, a label / security context is often called a ‘domain’. A domain is a sandbox and a compromised domain can only wreak havoc in that particular domain.

The standard SELinux action is to DENY the request. So there should be a policy to allow something.

SELinux modes

For SELinux there are 3 modes:

  1. Disabled. The system is disabled, there will be no enhanced security and labeling and therefore no logging
  2. Permissive. The system is enabled, there will be labeling and logging but NO enhanced security. It only logs what it would deny, but is not enforcing it
  3. Enforcing. The system is enabled, it is enforcing its policy and of course the whole system is logging and being labeled

Take a look at the below command and output.

# setstatus

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

The ‘Loaded policy name’ is important but will be discussed below. For this section I want to point you to ‘Current mode’ and ‘Mode from config file’. On a running system you can (temporary) switch modes.

Switching modes

A very common example: your system is enforcing its policy and you run into, what seems, security related problems. Without checking the logs (not advised) you could set SELinux in Permissive mode (also not advised) to check if your problem is SELinux related:

# setenforce 0

Check your status again with the above sestatus command or:

# getenforce

After testing, of course you have to put it back in enforcing mode:

# setenforce 1

Now whatever you switch it to, when you reboot your machine, the setting will revert to what it is set to in the configuration file. So with the above commands you are not permanently changing the SELinux status. To do so (and check out the config file), take a look at /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

Here you could make permanent changes, but you’d be better off using the exact configuration above.

Permissive domains

As stated in that very common example, system administrators might temporary set their system into permissive mode to troubleshoot apparent security related problems. This puts your system momentary at more risk than is needed. When the administrator forgets to put it back into enforcing mode, the risk is even worse.

Enter permissive domains. This lets you put a specific domain in permissive mode. You can then check the logs on AVC messages (explained further on), make your adjustments, test some more and get the domain out of permissive mode.

Check permissive domains:

# semanage permissive -l

Check which domain a process (in this case Apache) runs in:

# ps -eZ | grep httpd

You’ll see that httpd runs in the httpd_t domain. Add the domain to permissive domains:

# semanage permissive -a httpd_t

Do your troubleshooting and get it out of the permissive domains again:

# semanage permissive -d httpd_t

Check your permissive domains again:

# semanage permissive -l

Have a look at the output of the below command, the bottom left, for your total permissive domains:

# seinfo

As a side note, just to create a little bit of awareness, the httpd_t domain already can do A LOT of tasks, even in enforcing mode, and this goes for a lot of domains. Take a look at what it can do with this command:

# sesearch --allow | grep httpd_t

Labeling

So to everything on the filesystem a security context is added, which is called labeling. What is very nice to know and is a prime example about the integration of SELinux in the Linux operating system, is that almost every command that SELinux has a part in, the labels can be checked with the -Z flag. This is very consistent though the operating system.

For instance:

# ls -alZ

checks all files in the directory and there SELinux security context (aka label).

# ps -eZ | grep httpd

checks all processes, take all Apache processes and shows you in which SELinux domain it runs.

Take a look at a random directory with the # ls -alZ command. The type of labels that we have are:

  1. User
  2. Role
  3. Type
  4. Level

And with the above ‘ls’ command they show as username_u:role_role:type_t:s_level. Now the standard policy is the ‘Targeted Policy’ which uses ‘Type enforcement’. This means that this policy uses ‘3. Type’ (and thus type_t) to make its access decisions.

With this ‘Type enforcement’ the ‘2. Role’ and ‘4. Level’ can safely be ignored and ‘1. User’ only plays a small part but again, the access decisions are being based on ‘3. Type’. So this simplifies the meaning behind the above ‘ls’ command enormously.

More on labeling

With # seinfo you can take a look at all the above elements. The -u flag is for users, the -r flag is for roles and the -t flag is for type. The level is only used in Multi Level Security (MLS) mode, which you won’t need unless you are working in high level government security agencies (and when you are, you won’t be reading this post).

Then there is the unconfined_t domain. This is the Sodom and Gomorrah of SELinux if you will. Any process that doesn’t have a SELinux policy, confining it to a domain, will end up in the unconfined_t domain. These are mostly user processes and custom apps. As time goes by this gets less and less though, the system is constantly being updated to secure your system even more.

Processes running in this domain are not even subject to MAC, only to DAC. The so called targeted processes (with a specific domain label) run in a confined domain. The sandboxes I’ve mentioned earlier.

You can force relabel a complete filesystem. This really should be your last resort since it will reset SELinux back to defaults. Relabeling the filesystem can be done by placing a file called .autorelabel in the root of the filesystem. So a simple:

# touch /.autorelabel

And then reboot.

Labeling commands

There are globally two ways to edit the type_t / label on your files and folders.

# chcon and # semanage fcontext. I’ll give two practical examples and for everything else you should read the man pages. Important here is that ‘chcon’ is temporary and lost on reboot. The last command puts your changes in the file_contexts.local file (mentioned further on) and is permanent.

I’ve only used the ‘semanage fcontext’ command so I can give you two examples of that one. A great example was my LMD installation that gave problems with SELinux not letting the maldet process to write to my maldet logs:

# semanage fcontext -a -t var_log_t /usr/local/maldetect/logs(/.*)?

Another one is giving Apache the correct permissions for my Nexcloud installation:

# semanage fcontext -a -t httpd_sys_rw_content_t '/var/www/cloud.yourdomain.com(/.*)?'

The -a flag is for add, the -t flag for type.

After you run the commands you can immediately encompass them in the running policy with:

# restorecon -vr /usr/local/maldetect/logs

# restorecon -vr /var/www/cloud.yourdomain.com

Important SELinux files

Policy lookups are recorded in the Access Vector Cache (AVC). The log that this is being recorded to is /var/log/audit/audit.log. So to find any SELinux related incident in your log, you can run the following command:

# grep AVC /var/log/audit/audit.log

You can do more than just look at your logs in this fashion, but more on that a bit further on.

Your core configuration resides in /etc/selinux. Now the config file we already took a look at. The next important thing in this directory is your ‘targeted’ folder, which holds all your configuration for your targeted policy. Snoop around for a bit to see what’s in here. There are a couple of things especially worth mentioning in the targeted configuration.

The /etc/selinux/targeted/active directory. This is the current, running SELinux configuration. Config files in other directories can also be found here.

The /etc/selinux/targeted/active/modules directory. Here you can find all active SELinux modules. Modules specify, on the basis of the labels and policy, what can be allowed and what not. With the AVC entries in the logs, you can easily create your own modules.

The /etc/selinux/targeted/contexts/files/file_contexts file. This file holds ALL default labeling configuration of all known important files. When you autorelabel the filesystem as explained below, this file is being followed.

The /etc/selinux/targeted/context/files/file_contexts.local file. This file holds all non standard configuration, basically configuration a system administrator later put in.

Under normal circumstances you don’t touch these files yourself but the commands from your SELinux toolbox will do it for you.

Creating modules

One of the last things on my agenda, is about creating your own modules. When you’ve installed all tools from the first section you can do this on your own. Mind you though: you will be creating an exception on standard SELinux behavior. You really should be sure that it is OK to do so and not harmful to your system.

When I installed ClamAV on my system all seemed well but after my OSSEC installation I became aware off AVC denied messaged in my logs. SELinux prevented ClamAV access to a certain temporary folder it really needed. Since the folder name changed constantly, I couldn’t make an exception by name. For these kind of problems there are custom modules.

First grep the messages and see what process need access:

# grep AVC /var/log/audit/audit.log

In my case the process was named ‘clamdscan’. We’re going to grep for the process alone and with the ‘audit2allow’ tool create a Type Enforcement policy file:

# grep clamdscan /var/log/audit/audit.log | audit2allow -m clamdscan.local > clamdscan.local.te

You can cat the clamdscan.local.te and/or make changes to it if you’d like. The above is really an extra step to check if the policy is indeed what you would expect it to be. Then we’re going to create the real module:

# grep clamdscan /var/log/audit/audit.log | audit2allow -m clamdscan.local

And activate it:

# semodule -i clamdscan.local.pp

You can check you modules in /etc/selinux/targeted/active/modules and always unload them with:

# semodule -d clamdscan.local

And then delete it:

# semodule -r clamdscan.local

Booleans

Last one: booleans. Simply said this is an ON / OFF switch and there are a lot of these switches. List them all:

# getsebool -a

Or take for instance only Apache booleans:

# getsebool -a | grep httpd

As you can see, the amount of options are huge. To get more info on, let’s say, the httpd_can_network_connect boolean:

# semanage boolean -l | grep httpd_can_network_connect

And of course you can turn these switches on or off:

# setsebool httpd_can_network_connect x

Where 1/on/true turns it on and 0/off/false turns it off.

Practical example. Again, with ClamAV. Right after the installation I discovered I was plagued by returning AVC denied messages in my logs. I had turned on a boolean already that I knew was needed for AV scanning, but now I discovered I needed another one. So these two booleans solved the problem for me:

# setsebool -P antivirus_can_scan_system 1

# setsebool -P clamd_use_jit 1

Thanks for reading and good luck!

2 comments Add yours
  1. Hi rajshekhar,

    Always nice to get some feedback. SELinux is a wonderful security mechanism in my opinion. I almost don’t come across it anymore since I’ve switched to Debian, so probably won’t do a new article on it in a while.

    I really think MLS is only used in the most secure environments like governments, NSA, etc. I for one have never touched a server where it was configured. Doing some searching around, I think your best bet for more, comprehensive information is the Red Hat documentation:
    https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/6/html/security-enhanced_linux/mls

    Good luck!

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.