Recently, I needed to set up a chroot environment for an ssh user on one of my Linux machines. That is, when this user logged into the machine, I needed it to be trapped inside a chroot “jail”, i.e., a sub-hierarchy within the filesystem, such that it would only have access to the files within that sub-hierarchy. I searched the Web for how to do this and did not find any comprehensive instructions, although I did find bits and pieces. I’ve managed to figure it out, so I’m posting what I did in case it might be useful to others.
(For the curious: I wanted to do this so I could do a nightly backup to a remote host with rdiff-backup, without a password and yet without allowing the security of the remote host to be compromised if someone manages to get their hands on the SSH key with no passphrase.)
There are packages such as mach that assist with setting up chroot environments, but I wanted to roll my own, to ensure that (a) it would have only the minimal set of necessary files and (b) I would fully understand how it was set up.
Without further ado, here’s a well-commented shell script which illustrates the entire process. Note that this has been tested with Fedora, but it should work with minimal modifications on other Linux distributions as well.
#!/bin/bash -xe # Read through this entire script! You need to understand what it's # doing. Note especially the configuration section at the beginning # and the mount commands at the end. # Set to the root of your jail. THIS WILL BE ERASED AND RECREATED. R=/home/chroot # Set to the list of RPMs you know you need. NEEDED_PACKAGES="rdiff-backup" # Set to the list of directories, e.g., home directories, you want to # copy into the jail. COPY_DIRS="/home/chtest" # NOTE: The SSH daemon reads the user's *real* home directory, not the # chroot'd one, so the place to edit things like # ~user/.ssh/authorized_keys to control access is in /home/<user>, not # in $R/home/<user>. # If you want this script to automatically update # /etc/security/chroot.conf for some users to point at the new chroot, # set the usernames here. CHROOT_CONF_USERS="chtest" # If you want this script to update /etc/pam.d/system_auth, set this # variable to a non-empty value. DO_SYSTEM_AUTH=yes # NOTE: If you have directories containing some or all of the RPMS # you'll need, then put them in a local yum repository before running # this script, so that they can be copied from there rather than # downloaded. Then update the following variable to indicate which # repositories should be disabled and enabled. In the example shown # below, I've copied all the needed RPMs into a local repository # called "local", so that's the only one I want to use. You can make # this variable empty if you just want to use the standard repos. YUM_REPO_ARGS="--disablerepo=* --enablerepo=local" # If we're recreating an old chroot, we need to unmount these before # trying to remove and recreate it. umount $R/proc 2>/dev/null umount $R/dev/pts 2>/dev/null # Remove everything and start over. rm -rf $R # Create the root directory. mkdir -p $R # Needed so there's an RPM database to work with before anything has # been installed. mkdir -p $R/var/lib/rpm # Needed so that yum doesn't complain about not being able to create # transaction files in /var/lib/yum. mkdir -p $R/var/lib/yum # Copy the devices currently available in /dev, since they're the ones # that are likely to be needed in the chroot as well. mkdir -p $R/dev tar -C /dev -c . | tar -C $R/dev -x # These were in the devpts filesystem, which we're going to mount # within the chroot. rm -rf $R/dev/pts mkdir -p $R/dev/pts # Install the packages we need. rpm --root=$R --initdb yum --installroot=$R $YUM_REPO_ARGS -y install $NEEDED_PACKAGES # Copy our existing passwd and group files, as well as the home # directories we want to have in the jail. cp -f /etc/passwd /etc/group /etc/shadow /etc/gshadow $R/etc for dir in $COPY_DIRS; do tar -c $dir | tar -C $R -xp done # The file /etc/security/chroot.conf controls which users get # chroot'd. The first field is a regexp to match against usernames, # and the second is the directory into which the user should be # chroot'd. for user in $CHROOT_CONF_USERS; do echo "^$user\$ $R" >> /etc/security/chroot.conf done # We need to add a line to /etc/pam.d/system-auth to enable the # automatic chroot functionality. if [ "$DO_SYSTEM_AUTH" ]; then echo "session required pam_chroot.so onerr=fail" >> /etc/pam.d/system-auth fi mkdir -p $R/proc # should already exist, but what the heck # You need to ensure that these commands get run every time the # machine reboots, or the chroot jail won't work properly after a # reboot. mount -t proc proc $R/proc mount -t devpts devpts $R/dev/pts