Setting up my chroot environment for ssh user(s)

So I’ve done this a few times and I’m aware that there are numerous guides on the web which discuss this very same topic. However, every time I attempt this it always feels like I’m just muddling through… even with all of the guides. At any rate, this is the method I’ve settled on for creating my own chroot environment(s). Why?… because it works for me. Hopefully, someone else finds it useful as well. For anyone wondering, I did this on Debian 10.


I was making my chroot for purposes of allowing Apache users to be able to connect and edit their websites and therefore made /var/www as my chroot.

1. Make sure chroot directory has proper ownership.

chown root:root /var/www

2. Set permissions on chroot. 755 allows items in the directory to be read and browsed. You can restrict browsing by using 751 vs 755. The idea behind using 751 is that each user will have their own directory which they can browse within the chroot (see steps 12 and 13). If you don’t do these steps, DO NOT use 751.

chmod 755 /var/www

3. Create directory structure within chroot. This could vary based on your distribution and shell environment. However, this was what I needed for my desires within Debian 10 running bash.

mkdir /var/www/bin
mkdir -p /var/www/usr/bin
mkdir /var/www/sbin
mkdir -p /var/www/lib/x86_64-linux-gnu
mkdir /var/www/lib64
mkdir /var/www/etc
mkdir /var/www/dev

4. Set permissions on chroot system directories. (Again, you can use 751 if you complete steps 12 and 13 and wish to restrict browsing.)

chmod 755 /var/www/bin /var/www/usr /var/www/usr/bin /var/www/sbin /var/www/lib /var/www/lib/x86_64-linux-gnu /var/www/etc /var/www/dev

5. Create basic devices. I believe that this can vary a little as well. I think it depends on your kernel… not total sure. But these typically work.

mknod -m 666 /var/www/dev/null c 1 3
mknod -m 666 /var/www/dev/tty c 5 0
mknod -m 666 /var/www/dev/zero c 1 5
mknod -m 666 /var/www/dev/random c 1 8

6. Copy ‘unix_chkpwd’ and ‘whoami’ executeable into chroot. I didn’t realize I needed ‘whoami’ at first. However, it was necessary for the purposes of allowing me to create the familiar user@host$ prompt. Without it, my prompt looked like this: I have no name!@host

cp /sbin/unix_chkpwd /var/www/sbin/unix_chkpwd
cp /usr/bin/whoami /var/www/usr/bin/

7. Copy other desired executables into chroot. You can add or remove executeables as desired.

cp /bin/{bash,cp,ls,mkdir,mv,rm,rmdir,sh} /var/www/bin/
cp /usr/bin/{cat,echo,id,nano,scp,strace,dircolors,groups,ls,rsync,ssh,touch} /var/www/usr/bin/

8. Copy necessary shared libraries for the executables copied earlier. You can typically determine which libraries are needed for each executable using the ldd command. However, it doesn’t always give you everything you need. Sometimes you can locate missing libraries using the strace command.

The following commands copied all of the libaries I needed for the executables I copied earlier.

cp /lib/x86_64-linux-gnu/{libacl.so.1,liblzma.so.5,librt.so.1,libunwind-x86_64.so.8,libattr.so.1,libncursesw.so.6,libselinux.so.1,libz.so.1,libc.so.6,libnss_files.so.2,libtinfo.so.6,libdl.so.2,libpcre.so.3,libunwind-ptrace.so.0,libgcc_s.so.1,libpthread.so.0,libunwind.so.8} /var/www/lib/x86_64-linux-gnu/
cp /lib64/ld-linux-x86-64.so.2 /var/www/lib64/

9. Copy dependencies. Some executables (i.e. nano) depend on other programs as well. In those cases, you’ll need to copy those programs too. Don’t forget to copy any needed libraries for your dependancy programs as well. In order for nano to work, I also needed to do the following:

cp /usr/bin/xterm /var/www/usr/bin/
cp -r /lib/terminfo /var/www/lib/
cp /etc/nanorc /var/www/etc/
cp -r /usr/share/nano /var/www/usr/share/

10 . Make user info files (i.e. /etc/passwd, /etc/group, etc.) available in the chroot. There are a few ways of doing this. The simple way is just to copy the info over like this:

cp /etc/{group,gshadow,passwd,shadow} /var/www/etc

However, I don’t recommend this method because you’ll need to redo this with every addition and/or change to any user’s profile. My preference is to mount these files using the -bind option within /etc/fstab. This way they’ll stay updated within the chroot when changes are made to the system at large. You’ll first need to create the “target” files under /var/www/etc/ with the touch command…

touch /var/www/etc/group /var/www/etc/passwd /var/www/etc/gshadow /var/www/etc/shadow

.. and then add the following to the bottom of your /etc/fstab file.

/etc/group /var/www/etc/group none defaults,bind 0 0
/etc/passwd /var/www/etc/passwd none defaults,bind 0 0
/etc/shadow /var/www/etc/shadow none defaults,bind 0 0
/etc/gshadow /var/www/etc/gshadow none defaults,bind 0 0

11. Add all 4 of the following lines to the bottom of /etc/ssh/sshd_config for each user you want to be chrooted.

Match User user
ChrootDirectory /var/www
X11Forwarding no
AllowTcpForwarding no

At this point, after reloading /etc/fstab and restarting sshd (or just rebooting), you should have a functional chroot environment! However, in order for any user (other than root) to be able to write anything, they will need a directory with the proper permissions. The following additional steps acheive this.

12. Create a directory named /userdir within the chroot for the user chrootuser with permissions limiting access to just chrootuser.

mkdir /var/www/userdir
chown -R chrootuser:chrootuser /var/www/userdir
chmod 700 /var/www/userdir

13. Create a “dummy” /home directory from which you link to the different users’s chroot directories (necessary because of how the chrootuser‘s home directory is defined within /etc/passwd), and copy chrootuser‘s shell configs into /var/www/userdir.

mkdir /var/www/home
chmod 755 /var/www/home
ln -s ../userdir /var/www/home/chrootuser
chown -h chrootuser:chrootuser /var/www/home/chrootuser
cp /home/chrootuser/{.bashrc,.bash_profile} /var/www/userdir 

You now should have a working chroot environment that feels fairly normal and open’s into each user’s chroot directory by default. NOTE: If you’re doing this for purposes of running an Apache server, you’ll want different ownership of /var/www/userdir. Namely…

chown -R www-data:chrootuser /var/www/userdir