SELinux provides tools to more finely control the activities allowed to users, processes, and daemons to limit the potential damage from vulnerabilities.
In the third and final part of our server security series, we will look at how we can enhance the security of Linux-based AWS EC2 instances with SELinux. We will learn how to set up SELinux on Amazon Linux, and we will walk through a simple example on Red Hat Enterprise Linux (RHEL).
In Linux, we can easily control access to an individual file or directory by modifying the standard file permissions. We can define if we want to allow read, or write, or even execute permissions to the file owner, to all members of a single group, or to everyone else. If the standard file permissions are insufficient, we can also define Access Control Lists (ACL) which allow us to set permissions on an even finer scale.
However, there is an obvious limitation to this type of access control: there is no good way to restrict or limit a process from accessing files and directories that it should not have access to in the first place. For example, an Apache web server should have access to /var/www/html/index.html
, but not /etc/passwd
.
This limitation can be addressed by using SELinux as an additional layer of access control. The main model used is called Type Enforcement where processes and file system objects are labelled based on their types. SELinux compartmentalizes processes by defining rules around the types in its policy to determine what the processes are allowed to access. SELinux policies deny everything by default unless it is explicitly allowed.
Activating SELinux
On Amazon Linux AMI release 2015.09, SELinux is disabled by default. I am not sure what the state of SELinux is on Amazon Linux but, in any case, you can enable it by performing the following steps.
$ sestatus SELinux status: disabled
Install the selinux-policy
, selinux-policy-targeted
and policycoreutils-python
packages, and ensure that SELinux is configured to be enforced.
# yum install selinux-policy selinux-policy-targeted policycoreutils-python # egrep -v -e ^\# -e ^$ /etc/selinux/config SELINUX=enforcing SELINUXTYPE=targeted
After that, edit the menu.lst
file and append "selinux=1 security=selinux"
at the end of the kernel
command:
# cd /boot/grub # cp -prv menu.lst menu.lst.default ‘menu.lst’ -> ‘menu.lst.default’ # vim menu.lst # diff -u menu.lst.default menu.lst --- menu.lst.default 2015-11-15 23:43:54.874843843 +0000 +++ menu.lst 2015-11-15 23:44:19.979125318 +0000 @@ -6,6 +6,6 @@ title Amazon Linux 2015.09 (4.1.10-17.31.amzn1.x86_64) root (hd0,0) -kernel /boot/vmlinuz-4.1.10-17.31.amzn1.x86_64 root=LABEL=/ console=ttyS0 +kernel /boot/vmlinuz-4.1.10-17.31.amzn1.x86_64 root=LABEL=/ console=ttyS0 selinux=1 security=selinux initrd /boot/initramfs-4.1.10-17.31.amzn1.x86_64.img
Finally, create an empty .autorelabel
file in the root directory to label the entire system with the SELinux context after the instance is rebooted. It will take some time, especially since this is the first time SELinux is enforced on the instance. We need to perform this step to avoid potential issues arising from mislabelled files.
# touch /.autorelabel # sync; init 6
In the system log, you will see the following message before it is rebooted again.
*** Warning -- SELinux targeted policy relabel is required. *** Relabeling could take a very long time, depending on file *** system size and speed of hard drives.
Once the system reboots by itself for the second time, you will find that SELinux is enforced.
$ getenforce Enforcing
Now we’ll walk through a simple example of how SELinux enforces its policy. We will change the default port number of the OpenSSH service and see how SELinux reacts. We will use Red Hat Enterprise Linux 7.1 to modify the SELinux policy so that we can use the new port number – although we could perform (almost) the exact same steps on Amazon Linux.
Make sure you modify your EC2 instance’s Security Group to include this "Custom TCP Rule"
before proceeding further.
On Red Hat Enterprise Linux 7.1, SELinux is enabled and enforced by default.
# cat /etc/redhat-release Red Hat Enterprise Linux Server release 7.1 (Maipo) # sestatus 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
We know that the default port number for the OpenSSH server is 22. We want to change it to port 31337. We can do so by modifying the /etc/ssh/sshd_config
configuration file to include "Port 31337"
.
# cd /etc/ssh # cp -prv sshd_config sshd_config.default # vim sshd_config # egrep -i ^.?port /etc/ssh/sshd_config #Port 22 Port 31337
After modifying the sshd_config
configuration file, we can restart the OpenSSH service.
But before we restart the service, let’s install the setroubleshoot-server
RPM package for the sealert
tool. I am unable to find this package on Amazon Linux, but it is available on Red Hat Enterprise Linux.
# yum -y install setroubleshoot-server
Everything that SELinux has denied will be logged in /var/log/audit/audit.log
. In order to make sense of the logs, you can use the sealert
to diagnose the denial messages in layman’s terms. It provides easy-to-understand explanations of the messages and suggests how we can go about addressing such denials next time.
Now we are ready to restart the OpenSSH service.
# systemctl restart sshd.service # systemctl status sshd.service sshd.service - OpenSSH server daemon Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled) Active: activating (auto-restart) (Result: exit-code) since Sun 2015-11-15 11:29:05 EST; 5s ago Process: 10871 ExecStart=/usr/sbin/sshd -D $OPTIONS (code=exited, status=255) Main PID: 10871 (code=exited, status=255) Nov 15 11:29:05 ip-[redacted].ec2.internal systemd[1]: sshd.service: main process exited, code=.../a Nov 15 11:29:05 ip-[redacted].ec2.internal systemd[1]: Unit sshd.service entered failed state. Hint: Some lines were ellipsized, use -l to show in full.
You will find that the OpenSSH service will not start. This is because the SELinux policy insists that the OpenSSH service should only bind the default port 22. If it finds that it uses a different port, it will not allow it. You can look at the /var/log/audit/audit.log
log file to find the offending SELinux denial message.
# grep 31337 /var/log/audit/audit.log | tail -1 type=AVC msg=audit(1447605071.365:127): avc: denied { name_bind } for pid=10893 comm="sshd" src=31337 scontext=system_u:system_r:sshd_t:s0-s0:c0.c1023 tcontext=system_u:object_r:unreserved_port_t:s0 tclass=tcp_socket
Now let’s see what the sealert
tool says.
# sealert -a /var/log/audit/audit.log 78% done'list' object has no attribute 'split' 100% done found 1 alerts in /var/log/audit/audit.log -------------------------------------------------------------------------------- SELinux is preventing /usr/sbin/sshd from name_bind access on the tcp_socket port 31337. ***** Plugin bind_ports (92.2 confidence) suggests ************************ If you want to allow /usr/sbin/sshd to bind to network port 31337 Then you need to modify the port type. Do # semanage port -a -t PORT_TYPE -p tcp 31337 where PORT_TYPE is one of the following: ssh_port_t, vnc_port_t, xserver_port_t. [...] Additional Information: Source Context system_u:system_r:sshd_t:s0-s0:c0.c1023 Target Context system_u:object_r:unreserved_port_t:s0 Target Objects port 31337 [ tcp_socket ] Source sshd Source Path /usr/sbin/sshd Port 31337 [...]
Working with SELinux types
The sealert
tool hinted that if we wanted to bind a different port, we would have to modify the SELinux port type to include the new port number.
# semanage port -l | grep ssh ssh_port_t tcp 22 # semanage port -a -t ssh_port_t -p tcp 31337 # semanage port -l | grep ssh ssh_port_t tcp 31337, 22
Once we have modified the SELinux SSH port type, let’s restart the OpenSSH service and see if it worked this time.
# systemctl restart sshd.service # systemctl status sshd.service sshd.service - OpenSSH server daemon Loaded: loaded (/usr/lib/systemd/system/sshd.service; enabled) Active: active (running) since Sun 2015-11-15 11:36:10 EST; 6s ago Main PID: 10965 (sshd) CGroup: /system.slice/sshd.service └─10965 /usr/sbin/sshd -D Nov 15 11:36:10 ip-[redacted].ec2.internal systemd[1]: Starting OpenSSH server daemon... Nov 15 11:36:10 ip-[redacted].ec2.internal systemd[1]: Started OpenSSH server daemon. Nov 15 11:36:10 ip-[redacted].ec2.internal sshd[10965]: Server listening on 0.0.0.0 port 31337. Nov 15 11:36:10 ip-[redacted].ec2.internal sshd[10965]: Server listening on :: port 31337. # netstat -napt | grep ssh | grep -i listen tcp 0 0 0.0.0.0:31337 0.0.0.0:* LISTEN 10965/sshd tcp6 0 0 :::31337 :::* LISTEN 10965/sshd
Awesome! It worked! I hope this article gives you the confidence to learn and explore using SELinux on your EC2 instance.

