Hardening with SELinux
I finally got around to hardening my web server over the holiday break. Well, hardening more. A very important step in hardening linux is ensuring selinux is both enabled and enforcing. Of course the risk is that Security Enhanced Linux starts blocking things that should not be blocked…
I have been reluctant to have SELinux enforcing on all my instances of linux for a long time. It turns out that nowadays, with the maturity of the feature and its associated tools, this is pretty much an indefensible position.
Sure, back in the mid-noughties SELinux had its issues and caused much grief for sysadmins, but things have changed… a lot. This post documents me catching up, and will hopefully be useful to anyone trying to do the same!
SELinux is the most essential security tool available on linux. Period. No other single tool succeeds succeeds so well at securing a system from the inside out.
It originated from a 3-letter agency with a special interest in security. You can read more about it at wikipedia.
I used Centos 7 for this post. Most of my machines use Centos without a GUI installed. The tools should be identical on Red Hat (RHEL) and very similar on any modern linux distro. Of course the system commands may differ on your platform.
For the rest of the post I’ll assume you are using Centos 7.
[sysadmin@imago ~]$ cat /etc/centos-release CentOS Linux release 7.6.1810 (Core)
How SELinux Works
For those familiar with networking, SELinux works a little like a firewall, in that it controls activity between sources and targets based on a configured policy. That policy may include definitions of the source and target objects and rules about the types of activity allowed between them.
SELinux differs from a firewall in that it is interested in the activity within your system, not between systems on a network. And it does a very good job of this.
KEY CONCEPT: Objects
Objects are those “sources” or “targets” in our firewall analogy, either performing an action, or having an action performed on them.
Objects include a whole range of linux “things”, such as processes, files, directories, devices and sockets. But typically we’ll be dealing with processes and the filesystem.
SELinux has total visibiility and control of the activity between any of these objects. For example, when a process tries to read a file, or open a network socket, SELinux has the power to permit or deny this action.
KEY CONCEPT: CONTEXTS
While SELinux can is very flexible and can get very complicated, the basics are actually quite simple.
Every object on the system is labelled with an SELinux context. The context is stored in a label, so the terms ‘context’ and ‘label’ are often used interchangably.
A label has four fields: user, role, type and security level.
KEY CONCEPT: TYPES
In this series we will mainly be concerned with the type field. The type is what’s usually used to construct the default policy on most linux distributions. And for most situations that’s all that’s required.
The user and role fields are generally not used except in the most hardened environments. They are there to allow an ever richer set of SELinux policies, termed Role-Based Access Control (RBAC). This sounds similar to the tradional DAC but is much more powerful, and we won’t cover it in this series.
SELinux labels are already there, whether you are using them or not.
If you were to look at the files in a directory with the “-Z” option, you will see the SELinux context associated with each object (file or directory), including the type.
Here is how my home directory looks:
[sysadmin@server ~]$ ls -aZ drwx------. sysadmin sysadmin system_u:object_r:user_home_dir_t:s0 . drwxr-xr-x. root root system_u:object_r:home_root_t:s0 .. -rw-------. sysadmin sysadmin system_u:object_r:user_home_t:s0 .bash_history -rw-r--r--. sysadmin sysadmin system_u:object_r:user_home_t:s0 .bash_logout -rw-r--r--. sysadmin sysadmin system_u:object_r:user_home_t:s0 .bash_profile -rw-r--r--. sysadmin sysadmin system_u:object_r:user_home_t:s0 .bashrc -rw-------. sysadmin sysadmin system_u:object_r:user_home_t:s0 .lesshst drwx------. sysadmin sysadmin system_u:object_r:ssh_home_t:s0 .ssh -rw-rw-r--. sysadmin sysadmin system_u:object_r:user_home_t:s0 tmp1 -rw-rw-r--. sysadmin sysadmin system_u:object_r:user_home_t:s0 tmp2 -rw-------. sysadmin sysadmin unconfined_u:object_r:user_home_t:s0 .viminfo -rw-r--r--. sysadmin sysadmin system_u:object_r:user_home_t:s0 .vimrc
The context for this directory is ‘system_u:object_r:user_home_dir_t:s0’, and this contains the four field previously mentioned.
Type are easily spotted by ending in “_t”. The type of this directory and most of the files within is “user_home_t”.
Processes also have a context and a type, and simillarly, these can be shown with the “-Z” switch. Most of the standard utilities that use permissions have adopted this switch:
[sysadmin@server ~]$ ps -aZ LABEL PID TTY TIME CMD unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 2744 pts/0 00:00:00 sudo unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 2748 pts/0 00:00:00 su unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 2749 pts/0 00:00:00 bash unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 3648 pts/0 00:00:00 tail unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 3649 pts/0 00:00:00 grep unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 5524 pts/1 00:00:00 ps
Here, all the processes my user has initiated are of type “unconfined_t”.
Actually, the correct term for a process is domain, not type. But the ‘_t’ naming convention is still used. I suspect this is because processes are more complex that files and would be assoicated with particular addresses in RAM for example. But be aware that type and domain could be used interchangably in respect to processes.
Unlike a network firewall, which comes with either no rules or a ‘allow everything’ rule, SELinux comes with a default policy and a rich set of default rules that are a workable starting point for most systems. Remember SELinux is mandatory access control. Similar to context labels which are already there, so is a policy.
We’ll see how to determine which policy is in effect below.
Rules basically boil down to which type of objects can do what actions to which types of objects.
For example can a process of type “unconfined_t” write to a directory with the “user_home_dir_t” type? Can it read from the same directory? Or modify a particular file in that directory?
How SELinux Rules Are Applied
SELinux is loaded into the kernel at boot, so it’s able to be right at the core of all activity on your system.
TRADITIONAL SECURITY CONTROLS
You may already be familiar with the regular user+group filesystem permissions system used on Linux. This system is known as Discretionary Access Control (DAC).
When you are using SELinux, DAC remains in effect. In fact DAC is applied before SELinux. If for example DAC blocks an action, then SELinux will not see the attempted action or log it. SELinux does not modify DAC’s filesystem permissions or behaviour in any way. It runs in series.
SELinux interoperates similarly with other security controls, such as the host firewall.
Generally SELinux does not replace other controls, it supplements them.
SELinux is a kind of Mandatory Access Control (MAC). Everything is policed by SELinux once it’s enforcing.
Further, all policies are defined at the administrator level. DAC allows regular users to change permission on files and directories they have write access to.
SELInux improves the separation between processes, so successful privilege escalation attacks on one process are less likley to lead to other parts of the system being compromised.
SELinux is also offer much more fine-grained control, even without using the RBAC features.
The default action is deny. If there is no rule explicitly allowing access, actions are blocked. This limits the damage from both malicious and accidental activity.
In Part 2…
And that’s pretty much all we need to know to get started with some hands-on.
In the next post we will learn how to the determine the status and and current mode of operation on a system, and how to change it. Then we’ll touch on where and how SELinux logs information.
Join me in Troubleshooting SELinux – Part 2.