major.io words of wisdom from a systems engineer

Confine untrusted users (including your children) with SELinux

SELinux PenguinThe confined user support in SELinux is handy for ensuring that users aren’t able to do something that they shouldn’t. It seems more effective and easier to use than most of the other methods I’ve seen before. Thanks to Dan for reminding me about this during his SELinux in the Enterprise talk from this year’s Red Hat Summit.

There are five main SELinux user types (and a handy chart in the Fedora documentation):

  • guest_u: - no X windows, no sudo, and no networking
  • xguest_u: - same as guest_u, but X is allowed and connectivity is allowed to web ports only (handy for kiosks)
  • user_u: - same as xguest_u, but networking isn’t restricted
  • staff_u: - same as user_u, but sudo is allowed (su isn’t allowed)
  • unconfined_u: - full access (this is the default)

One interesting thing to note is that all users are allowed to execute binary applications within their home directories by default. This can be switch off via some booleans (which I’ll demonstrate in a moment).

Let’s kick off a demonstration to show the power of these restrictions. First off, let’s get a list of the default configuration:

# semanage login -l

Login Name           SELinux User         MLS/MCS Range        Service

__default__          unconfined_u         s0-s0:c0.c1023       *
root                 unconfined_u         s0-s0:c0.c1023       *
system_u             system_u             s0-s0:c0.c1023       *

By default, all new users come with no restrictions (as shown by unconfined_u). I’ll create a new user called selinuxtest and set a password. If I ssh to the server as the selinuxtest user, I see that I’m unconfined:

$ id -Z
unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023

That’s what we expected. Let’s apply the strongest restrictions to this user and apply guest_u:

# semanage login -a -s guest_u selinuxtest

I’ll start a new ssh session as selinuxtest and try out some commands that I’d normally expect to work on a Linux server:

$ ping google.com
ping: icmp open socket: Permission denied
$ curl google.com
curl: (7) Failed to connect to 74.125.225.129: Permission denied
$ sudo su -
sudo: unable to change to sudoers gid: Operation not permitted
$  ./hello
Hello world
$ file hello
hello: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=0x5ffb25a7171c3338d6c76147cccc666ddc752dde, not stripped

The networking and sudo restrictions applied as we expected. However, I was able to compile a small “Hello World” binary in C and run it. That could become a problem for some servers. Let’s adjust a boolean that will restrict this activity:

# getsebool -a | grep exec_content
auditadm_exec_content --> on
guest_exec_content --> on
secadm_exec_content --> on
staff_exec_content --> on
sysadm_exec_content --> on
user_exec_content --> on
xguest_exec_content --> on
# setsebool guest_exec_content off

Now I try running the binary again as my selinuxtest user:

$ ./hello
-bash: ./hello: Permission denied

I can’t execute binary content in my home directory or in /tmp any longer after adjusting the boolean. Let’s switch selinuxtest to xguest_u:

# semanage login -a -s xguest_u selinuxtest

And now I’ll re-test as the selinuxtest user:

$ curl -si google.com | head -1
HTTP/1.1 301 Moved Permanently
$ ping google.com
ping: icmp open socket: Permission denied

I have full web connectivity but I can’t do anything else on the network. Now for a switch to user_u:

# semanage login -a -s user_u selinuxtest

And testing user_u with selinuxtest reveals:

$ ping -c 1 google.com
PING google.com (74.125.225.134) 56(84) bytes of data.
64 bytes from ord08s09-in-f6.1e100.net (74.125.225.134): icmp_seq=1 ttl=57 time=29.3 ms

--- google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 29.332/29.332/29.332/0.000 ms
$ curl -si google.com | head -n1
HTTP/1.1 301 Moved Permanently
$ sudo su -
sudo: PERM_SUDOERS: setresuid(-1, 1, -1): Operation not permitted

Networking is wide open but I still don’t have sudo. Let’s try staff_u:

# semanage login -a -s staff_u selinuxtest

Testing staff_u with selinuxtest gives me the expected results:

$ sudo su -
[sudo] password for selinuxtest:

I didn’t add selinuxtest to sudoers, so this command would fail. However, I’m actually allowed to execute it now.

These restrictions could be very helpful when dealing with users that you don’t fully trust on your system. You could use these restrictions to add a kiosk user to a Linux machine and allow family members or coworkers to surf the web using your device. In addition, you could use the restrictions as an extra layer of protection on heavily shared servers to prevent users from consuming resources or generating malicious traffic.