Understanding kernel capabilities

When you perform a ps aux command — or a sudo ps aux command if you've mounted /proc with the hidepid=1 or hidepid=2 option — you'll see many processes that are owned by the root user. This is because these processes have to access some sort of system resource that unprivileged users can't access. However, having services run with full root privileges can be a bit of a security problem. Fortunately, there are some ways to mitigate that. 

For example, any web server service, such as Apache or Nginx, needs to start with root privileges in order to bind to ports 80 and 443, which are privileged ports. However, both Apache and Nginx mitigate this problem by either dropping root privileges once the service has started or by spawning child processes that belong to a non-privileged user. Here, we can see that the main Apache process spawns child processes that belong to the non-privileged apache user:

[donnie@centos7-tm1 ~]$ ps aux | grep http
root 1015 0.0 0.5 230420 5192 ? Ss 15:36 0:00 /usr/sbin/httpd -DFOREGROUND
apache 1066 0.0 0.2 230420 3000 ? S 15:36 0:00 /usr/sbin/httpd -DFOREGROUND
apache 1067 0.0 0.2 230420 3000 ? S 15:36 0:00 /usr/sbin/httpd -DFOREGROUND
apache 1068 0.0 0.2 230420 3000 ? S 15:36 0:00 /usr/sbin/httpd -DFOREGROUND
apache 1069 0.0 0.2 230420 3000 ? S 15:36 0:00 /usr/sbin/httpd -DFOREGROUND
apache 1070 0.0 0.2 230420 3000 ? S 15:36 0:00 /usr/sbin/httpd -DFOREGROUND
donnie 1323 0.0 0.0 112712 964 pts/0 R+ 15:38 0:00 grep --color=auto http
[donnie@centos7-tm1 ~]$

But not all software can do this. Some programs are designed to run with root privileges all the time. For some cases — not all, but some — you can fix that by applying a kernel capability to the program executable file.

Capabilities allow the Linux kernel to divide what the root user can do into distinct units. Let's say that you've just written a cool custom program that needs to access a privileged network port. Without capabilities, you'd either have to start that program with root privileges and let it run with root privileges or jump through the hoops of programming it so that it can drop root privileges once it's been started. By applying the appropriate capability, a non-privileged user would be able to start it, and it would run with only the privileges of that user. (More about that later.)

There are too many capabilities to list here (there's about 40 in all), but you can see the full list by using the following command:

man capabilities

Returning to our previous example, let's say that we need to use Python to set up a very primitive web server that any non-privileged user can start. (We'll do this with Python 2, because it doesn't work with Python 3.) For now, we'll do this on a CentOS 8 machine.

The names of the Python packages and executable files are different between CentOS 7, CentOS 8, and Ubuntu. I'll show you all three sets of commands when we get to the hands-on lab.

The command for running a simple Python web server is as follows:

python2 -m SimpleHTTPServer 80

However, this won't work because it needs to bind to port 80, which is the privileged port that's normally used by web servers. At the bottom of the output from this command, you'll see the problem:

socket.error: [Errno 13] Permission denied

Prefacing the command with sudo will fix the problem and allow the web server to run. However, we don't want that. We'd much rather allow non-privileged users to start it, and we'd much rather have it run without root user privileges. The first step in fixing this is to find the Python executable file, like so:

[donnie@localhost ~]$ which python2
/usr/bin/python2
[donnie@localhost ~]$

Most times, the python or python2 command is a symbolic link that points to another executable file. We'll verify that with a simple ls -l command:

[donnie@localhost ~]$ ls -l /usr/bin/python2
lrwxrwxrwx. 1 root root 9 Oct 8 17:08 /usr/bin/python2 -> python2.7
[donnie@localhost ~]$

So, the python2 link points to the python2.7 executable file. Now, let's see if there are any capabilities assigned to this file:

[donnie@localhost ~]$ getcap /usr/bin/python2.7 
[donnie@localhost ~]$

No output means that there are none. When we consult the capabilities man page, we'll find that the CAP_NET_BIND_SERVICE capability seems to be what we need. The one-line description for it is: bind a socket to internet domain privileged ports (port numbers less than 1024)Okay; that sounds good to me. So, let's set that on the python2.7 executable file and see what happens. Since we used getcap to look at the file capabilities, you can probably guess that we'll use setcap to set a capability. (And you'd be correct.) Let's do that now:

[donnie@localhost ~]$ sudo setcap 'CAP_NET_BIND_SERVICE+ep' /usr/bin/python2.7
[sudo] password for donnie:
[donnie@localhost ~]$ getcap /usr/bin/python2.7
/usr/bin/python2.7 = cap_net_bind_service+ep
[donnie@localhost ~]$

The +ep at the end of the capability name means that we're adding the capability as effective (activated) and permitted. Now, when I try to run this web server with just my own normal privileges, it will work just fine:

[donnie@localhost ~]$ python2 -m SimpleHTTPServer 80
Serving HTTP on 0.0.0.0 port 80 ...

When I use Firefox on my host machine to connect to this server, I will see a file and directory listing of everything that's in my home directory:

Linux capabilities can also be quite useful in other ways. On any Linux system, the ping utility needs root privileges in order to craft the network packets that it needs to do its job. However, everybody and his brother can use ping as just a normal user. If you look at the ping executable file, you'll see that two capabilities have been assigned to it by the Linux maintainers:

[donnie@localhost ~]$ getcap /usr/bin/ping
/usr/bin/ping = cap_net_admin,cap_net_raw+p
[donnie@localhost ~]$

As cool as all this seems, there are some downsides:

  • Trying to figure out exactly which capabilities a program needs isn't always straightforward. In fact, it can require a good bit of experimentation before you can get things right.
  • Setting capabilities isn't a cure-all. A lot of times, you'll see that setting a specific capability still won't allow the program to do what you need it to do. Indeed, there may not even be a capability that will allow the program to function without root privileges as you want it to.
  • Performing a system update could replace executable files that you assigned capabilities to. (With ping, we don't have to worry about this since the capabilities were set by the Linux maintainers.)

Okay, so there's a good chance that you might never actually have to set any capabilities. However, this is one of the tools that my IoT Security client uses to help lock down IoT devices, so this does have a practical use. And besides, capabilities are a building block for some of the technologies that we'll look at a bit later.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.152.162