Restricted Bash

- - posted in bash, shell, ssh

While working on deploying a web-application recently, I needed to transfer a ‘build artifact’ (fancy name for a .tgz) from a Continuous Integration server to an RPM repository server.

We already have an existing RPM repository server that uses Apache, and once my tarball was in the correct location, it would be available over HTTP for all to consume.

Cutting to the chase – What is the simplest way by which I could automatically transfer a ~20 MiB file from one CentOS host to another? I didn’t want to install an FTP server or any extra Apache module on the existing RPM host that would then support multi-part file uploads.

The quickest solution, it seemed was an scp or an rsync.

So, how would this CI host be authorized to open an SSH tunnel to the web-server? Where would the identity key reside? There is no elaborate keyserver in this ecosystem.

I decided to the transfer the responsibility of protecting the system from the identity key to the remote host’s operating system.

A new user called tarballs on the RPM repository host with its HOME set to /var/www/html/tarballs, and set its SHELL to rbash.

What is rbash?

When the bash is started with the name rbash (ln -s /bin/bash /bin/rbash) or by invoking bash like this: bash -r, it starts up in a restricted way, which is handy while setting up more controlled environments. I know of it thanks to Saurabh ‘Rob’ Mookherjee, a sysadmin whom I work with. When in bash’s restricted mode, one cannot change directories, use commands with a / in them, neither can one change the PATH or the SHELL variables. A more comprehensive list of contraints can be found in the manpage for bash.

So, all is good except the tarballs user still has access to all executables that exist in its PATH that the system assigns by default. A quick hack in the /etc/profile.d to unset the PATH for the tarballs user and there is hardly anything the tarballs user can do once logged in. The only required executble binary was /usr/bin/ln to make a symlink called ‘latest’ to the most recent tarball that was SCP’ed over. I copied this binary to tarballs’ HOME. A kludge, I admit.

Now, from my Continuous Integration agent, I can script these two commands to be run everytime a new build artifact is to be uploaded to the repository.

scp -v -i tarball_identity tmp/build7f3cd88.tar.gz  
ssh -v -i tarball_identity "ln -sf build7f3cd88.tar.gz latest"

For reference, here is what I have on the repository host:

[ ~]# cat /etc/profile.d/ 
if [ `whoami` = 'tarballs' ]; then unset PATH; fi

In larger, more complicated systems that support different products and web-apps, I have seen the occasional file that is rsync’ed to another host, or a larger script residing remotely being invoked over SSH. While such things usually happen inside of a VPN or a DMZ, it is still a risky proposition to have an identity file being checked into the codebase or lying on an arbitrary host. While having a more robust security solution should certainly be on the list, creating a separate user on the remote host that has only enough privileges to perform a said task is a great idea. Once such a user exists, we have effectively moved that responsibility from the SSH identity keyfile to the remote host’s operating system.

Bear in mind that this infrastructure lies in a secure corporate datacenter with access to the machines restricted to trusted co-workers. Also, while the RPM repository host is important, all the data it holds can be easily mirrored and reproduced. Solely relying on an rbash is by no means a solution for any mission-critical host that is directly exposed to the internet or any untrusted zone.