In this post I'll be discussing how to quickly set up a "jail" using chroot in Ubuntu 14.04. A "jail" is essentially a special environment in which the user has limited capabilites. gi It's most commonly used when you want to let users execute arbitrary code on your server, but don't want to give them full capabilities. If the user were to obtain root capabilities, they can do anything on the server, which is very very bad.
At a hackathon a few weeks ago, I found that there weren't many good tutorials on the Internet that explain how to do this, so I spent a lot of time trying on my own. Hopefully my pain and suffering can save you some!
Also, just a disclaimer, there is most likely a hole in the system made using this method that I'm not aware of. However, this post is more geared towards quickly making a jail to fend off people just trying to mess with you using some quick and dirty code. Let's begin!
First let's update the server:
sudo apt-get update && sudo apt-get upgrade
Chroot is a command that lets you run a program that sees an arbitrary directory as the root directory. It makes it impossible for the program to modify or even see any files above the temporary-root directory, which is great for security. We're going to refer to this temporary-root directory as a "jail" from now on.
However, since the jailed program can only see the files within a certain directory and there's lots of dependencies that most programs rely on, we're essentially going to have to create a mini Unix filesystem (/usr, /bin, etc/) inside the jail directory. Doing this by hand would be a nightmarish task, but luckily there is a tool called Jailkit that automates many of these tasks.
First lets download Jailkit:
Decompress the tarball:
tar -xzvf jailkit-2.17.tar.gz
Cd into the directory:
Now let's install jailkit:
./configure && make
Test it out by typing
jk_init -h. A help page should show up.
Next let's make our jail directory:
It's going to ask if you want to move directory contents.
Let's also set an environment variable so we don't have repeat it so often and to ensure that we won't mistype it:
Now we're going to start copying programs into the jailed directory. Let's start with python. First put the path of the python execugable in an environment variable (this path differs on everyone's system):
Now let's initialize the jail and copy the python executable into the jail using a handy program from jailkit called jk_cp:
jk_init -j $j jk_lsh
jk_cp -j $j $python_path
The program jk_cp copies both python itself and all of it's dependencies (which are scattered across the system) into the jail directory.
We're also going to need bash to run complex commands, so let's copy that as well:
jk_cp -j $j /bin/bash
For python specifically, we also have to import some python libraries on the system. In my case, I'm using Python version 2.7, but you can replace "2.7" with whatever version you're using and there will likely be a directory in that location with the default libraries.
jk_cp -j $j /usr/lib/python2.7
Now let's make a user that's going to execute the arbitrary code sent in by users. Since this is arbitrary code he's executing, we're not going to be able to trust him very much. Thus, let's call him shady_steve:
useradd -m shady_steve
Now let's jail him using jailkit:
jk_jailuser -j $j shady_steve
Now we can finally put this all together using the chroot command:
chroot --userspec=1000 $j python
Yay, we've now simulated a python environment in our new little filesystem.
Now let's ensure that it's safe. First let's make a file that could contain important data in the root of our jail:
echo "this is important data!" >> $j/important_data
Let's cook up a quick python script that would try to delete the file
Copy paste the following:
import os print "Trying to remove important data...." os.remove('/important_data')
Now let's try to run it:
chroot --userspec=1000 $j bash -c "python /home/shady_steve/shady.py"
If the jail is properly configured, you should get an error from Python saying that permission is denied.
A little bit about this final chroot call: the userspec argument lets us run the python program as any user. 1000 is the user id of shady_steve (since it's the first user made on the mini filesystem it should always have the same id). Since chroot is a system call, it could be implemented differently on different operating systems. For example, the Ubuntu version offers a userspec argument, but the OSX version does not. Finally, we use bash to run the python program.
bash -c "command" will execute command. We need to run the python program using bash because it involves arguments (the filepath).
And there you go, you can now execute arbitrary code safely by putting in shady_steve's home folder inside the jail directory, and then running it using the chroot command above. If you want to add more programs, just use jk_cp to add as many as you'd like.
Posted: March 7th, 2015