I’ve always had a bad conscience about the audit trail on the servers I manage. Sure, we use personal accounts and sudo, so we know who ran every command. Unfortunately, the command in the sudo log is often just “bash”.

The reason for this is simple: It is quite awkward to work in the shell when TAB completion doesn’t work. You want to read the error log in /var/log/httpd? Too bad, the directory is off limits for normal users. So what is the filename? Well, first run “sudo ls” on it, then cut and paste the filename after “sudo less”. No, that will make me go mad. “sudo -i” it is.

After lamenting about this recently I had a lightning bulb moment: Isn’t this the sort of thing Linux capabilities(7) set out to handle? And lo and behold:

CAP_DAC_OVERRIDE Bypass file read, write, and execute permission checks. (DAC is an abbreviation of "discretionary access control".) CAP_DAC_READ_SEARCH * Bypass file read permission checks and directory read and exe‐ cute permission checks;

The first one is really powerful - it allows all read and write access to files, as if you were root.

The other one is exactly what we need. It allows all getdents(2) and stat(2) calls as if you were root.

How do capabilities work?

Capabilities are a set of permissions which are granted to an executable. Not a process, not a user, not a session. The original use case for capabilities was tcpdump, shortly followed by ping. Let’s have a look:

$ ls -l /bin/ping -rwxr-xr-x. 1 root root 44752 Nov 19 2015 /bin/ping*

The period after r-x indicates the file has extended attributes, and capabilities are among them. Other than that there are no other special features.

$ getcap /bin/ping /bin/ping = cap_net_admin,cap_net_raw+ep

This is what grants ping the ability to send raw ICMP packets on the network, even if the user isn’t root. Before capabilities came along, ping was typically setuid root. In ping’s case, we don’t mind that every user gets access to this ability, but for a shell it would raise privacy issues.

Applying capabilities to bash

Let’s try it out:

$ cp /bin/bash /tmp/testsh $ chmod 700 /tmp/testsh $ sudo setcap CAP_DAC_READ_SEARCH = ep /tmp/testsh

=ep means effective and permissive. Don’t blame me for obtuse syntax :-).

If you run /tmp/testsh now, you will be able to use TAB-completion within /var/log/httpd/ or wherever else you like. Try it out! Go wild! You will still need sudo to do destructive operations. You can check the capabilities of a process like this:

$ grep CapPrm /proc/ $$ /status CapPrm: 0000000000000004

Notice that bit 4 is set. Compare with:

$ grep CapPrm /proc/self/status CapPrm: 0000000000000000

Here /proc/self is the grep process spawned by the shell, so this demonstrates that the capability is not inherited by child processes.

Adding tabashco!

Having your own personal /tmp/testsh is fine, but it would be better to share it with your sysadmin colleagues.

As mentioned earlier, the capability is granted to the program, so we need to use Unix permissions to restrict access to these super powers. A common denominator for access could be membership in the group “wheel”.

Basically, we create a copy of /bin/bash , chmod it to 550 and change its group. After a brief brainstorming (“adminbash”, “tab-bash”, etc.), I ended up with “tabashco” (not a trademark - yet).

The Puppet code to make this copy looks like this:

class baseconfig::tabashco ( $path = '/usr/local/bin' , $group = 'wheel' , ) { file { " ${path} /tabashco" : source => '/bin/bash' , owner => 'root' , group => $group , mode => '0550' , } exec { "setcap cap_dac_read_search=ep ${path} /tabashco" : path => '/usr/sbin:/sbin' , refreshonly => true , subscribe => File [ " ${path} /tabashco" ] } }

Puppet will make sure that tabascho is updated whenever /bin/bash changes.

Finally, you need to put code like this in your ~/.bash_profile (or in a file in /etc/profile.d )

if [ -x /usr/local/bin/tabashco ] ; then case $0 in * tabashco ) : ;; * ) echo "Spicing up your shell!" exec tabashco " $@ " ;; esac fi