Table of Contents

Chroot provides an extra layer for security. However, care must be taken when setting up the environment to avoid unexpected results. Arch Wiki has a page that contains detailed steps and explanations, which should work on Debian as well with a few tweaks. Here we assumes Nginx is already installed using apt.

Initiate directory structure

The chroot jail is created at JAIL=/opt/chroot.

mkdir $JAIL && cd $JAIL
mkdir {dev,etc,run,tmp,usr,var}
mkdir usr/{sbin,lib,lib64,share}
mkdir var/{www,log,lib}
mkdir usr/lib/x86_64-linux-gnu
mkdir var/{log,lib}/nginx
ln -s usr/sbin sbin
ln -s usr/lib lib
ln -s usr/lib64 lib64

Create device nodes

Nginx requires /dev/null, /dev/random and /dev/urandom.

cd $JAIL/dev
mknod -m 0666 null c 1 3
mknod -m 0666 random c 1 8
mknod -m 0444 urandom c 1 9

For some unprivileged containers that disallow mknod execution, consider using mount --bind as an alternative.

cd $JAIL/dev
touch null random urandom
mount --bind /dev/null null
mount --bind /dev/random random
mount --bind /dev/urandom urandom

Copy files

Copy module configurations:

cp -r /usr/share/nginx $JAIL/usr/share
cp -r /lib/nginx $JAIL/lib

Copy web contents:

cp -r /var/www/* $JAIL/var/www

Copy configuration files:

cp -rfvL /etc/nginx $JAIL/etc

You may want to restore symlinks in $JAIL/etc/nginx/modules-enabled.

Copy the executable:

cp /sbin/nginx $JAIL/sbin

Copy dylibs:

cp $(ldd /sbin/nginx | grep /lib/ | sed -sre 's/(.+)(\/lib\/x86_64-linux-gnu\/\S+).+/\2/g') $JAIL/lib/x86_64-linux-gnu
cp /lib64/ld-linux-x86-64.so.2 $JAIL/lib64

Copy system files:

cp /lib/x86_64-linux-gnu/libnss_* $JAIL/lib/x86_64-linux-gnu
cp -rfvL /etc/{services,localtime,nsswitch.conf,nscd.conf,protocols,hosts,ld.so.cache,ld.so.conf,resolv.conf,host.conf} $JAIL/etc

Fake user database

cat /etc/passwd | grep www-data > $JAIL/etc/passwd
cat /etc/passwd | grep nobody >> $JAIL/etc/passwd
cat /etc/group | grep www-data > $JAIL/etc/group
cat /etc/group | grep nogroup >> $JAIL/etc/group
cat /etc/shadow | grep www-data > $JAIL/etc/shadow
cat /etc/shadow | grep nobody >> $JAIL/etc/shadow
cat /etc/gshadow | grep www-data > $JAIL/etc/gshadow
cat /etc/gshadow | grep nogroup >> $JAIL/etc/gshadow

Mount temporary filesystem

mount -t tmpfs none $JAIL/run -o 'noexec,nodev,size=1M'
mount -t tmpfs none $JAIL/tmp -o 'noexec,nodev,size=10M'
cat >> /etc/fstab << EOF
> tmpfs   /opt/chroot/run   tmpfs   rw,noexec,nodev,relatime,size=1024k   0       0
> tmpfs   /opt/chroot/tmp   tmpfs   rw,noexec,nodev,relatime,size=10240k  0       0
> EOF

Restrict file permissions

chown -R root:root $JAIL
chown -R www-data:www-data $JAIL/var/www
chown -R www-data:www-data $JAIL/etc/nginx
chown -R www-data:www-data $JAIL/var/log/nginx
chown -R www-data:www-data $JAIL/var/lib/nginx
find $JAIL -user root -type d -print | xargs chmod -rw
find $JAIL -user www-data -print | xargs chmod o-rwx
chmod +rw $JAIL/{run,tmp}

Edit configurations

Add systemd chroot support:

systemctl edit nginx
systemctl daemon-reload
[Service]
PIDFile=/opt/chroot/run/nginx.pid
RootDirectory=/opt/chroot
User=www-data
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
AmbientCapabilities=CAP_NET_BIND_SERVICE
ExecStop=/sbin/nginx -g 'daemon on; master_process on;' -s quit

Comment unnecessary lines in $JAIL/etc/nginx.conf:

# already running as www-data
#user www-data;
# external modules bring tons of dependencies
#include /etc/nginx/modules-enabled/*.conf;

Finish

systemctl restart nginx