HOWTO : configuring openssh for chrooted sessions on linux, and run applications from the chroot cage (scp, rsync)

This document was born out of the need for me to upload/download files to a linux box, and to trigger file syncronization of the changes to a backup box by means of rsync through ssh.

Please note, that this have been tested on Redhat linux 7.3, make the nessessary ajustments for other systems

Be advised that if you only need to allow chfrooted sftp sessions, you do not need to chroot the openssh deamon and create a chroot environment as described below - all you need to do is to get an sftp-chroot patch from the openssh-devel list, and set sftp-server suid root. I was using that some time ago, but this was in the times of openssh 2.9. Now you need the updated patch that works with 3.5. If you get it working, please tell me (alex-ssh@verticon.co.il). Everything I needed (at that time) to make it work is here. The patch there is not completely fullproof, since it relies on the HOME env variable, but it was enough for me - i just needed to protect the box from "stupid" users, not from malicious ones.

The need : allowing chrooted shell sessions to a linux machine withing a limited enviropment.

The enviropment will allow :

  • SCP functionality.
  • Ability to ssh out of the chrooted shell to predefined hosts
  • Ability to rsync from withing the chrooted shell

    If you only need the first part of this howto, just skip the other two.

    To install a version of openssh that will allow chrooted sessions, you can follow 2 paths :
    1. Build a customized version of openssh, as described in this document
    2. Use the prebuilt RPMS, mantained by me (alex-ssh@verticon.co.il). Download and install my rpms here. Note, that these are quilt without the X askpass addons and kerberos support, since i use them in console-onle servers.
    3. Rebuild the RPMS from source, like i did to create the ones available above (read here how to do that)

    This version will allow chrooted sessions for certain users, indentifiable by the magic token "/./" in their home directory path : if the home directory /home/test/./hello, then the chroot directory will be /home/test/, and the home directory of the user inside chroot cage will be /hello

  • With time, i get lot's of email about this patch, one of them (from Dario) had another implementation so that sshd will chroot users who belong to a particular group (defaults to "shacc"), instead of checking the home directory for the magic string "/./". The difference is that some programs have problems with the home directory modified that way. The patch is available here, you can use this one it instead the standard /./ patch if you need this functionality ...

    Generally, you will need a source distribution of openssh, which is at this time is at version 3.5p1. You can download the tar file from http://www.openssh.org/portable.html.

    Untar the distribution, and configure it as the basic redhat ssh configuration (at least I like to do it like this)

    # tar -xzf openssh-3.5p1.tar.gz
    # cd openssh-3.5p1

    patch the source with this patch file  :

    # patch < openssh-chroot-3.5p1.patch

    configure the distribution :

    # ./configure --prefix=/usr --libexecdir=/usr/libexec/openssh --sysconfdir=/etc/ssh --mandir=/usr/share/man

    build and install :

    # make
    # make install

    copy the sshd.init startup file from the contrib/redhat directory to /etc/init.d

    # cp contrib/redhat/sshd.init /etc/init.d/sshd

    For these of us running redhat, run chkconfig to add sshd to /etc/rc.d system, otherwise add it manually to startup sequence

    # chkconfig --add sshd

    # /etc/init/sshd start

    i like to reboot the machine at this moment and test ssh by logging in (as root, usually)

    I am using scp to upload files to my web server, so here it goes

    Now add a new user with the chroot directory at /var/www, assign a password

    # useradd webmaster -d /var/www/./
    # passwd webmaster

    delete all the skeleton files created in the home directory (unless you really need them)

    Now you have to create a basic chroot enviropment (come basic system files inside the chroot cage), I have a shell file that does the trick (thanks to Gabriele Facciolo). You can edit the APPS variable in the script to add or remove applications

    # ./create_chroot_env webmaster

    Test the results

    # ssh webmaster@localhost
    # ls -l

    to make sure that you see the chrooted directory list, and not the top level root tree.

    if no errors are presented, try connecting to the server with WinSCP (you may disable "clear aliases" checkbox if you have deleted the skeleton files to speed the connection process up)

    *******************************************************************

    Now, let's allow ssh from the chrooted session, since you are going to use rsync over ssh.

    # cp /etc/nsswitch.conf etc
    # touch etc/resolv.conf
    # echo "order hosts" > etc/host.conf
    # echo "127.0.0.1 localhost" > etc/hosts

    repeat last command and add all the addresses of the hosts you want to be able to connect from the chroot cage, dns resolution will not work

    # mkdir dev
    # mknod -m 0666 dev/tty c 5 0
    # mknod -m 0644 dev/urandom c 1 9

    vi etc/passwd - change the home directory of the webmaster user (inside the chroot cage!) to "/"

    # mkdir .ssh
    # touch .ssh/known_hosts
    # chown webmaster:webmaster .ssh/known_hosts

    now you should be able to ssh to other hosts

    finally, to use rsync, just

    # cp /usr/bin/rsync usr/bin

    Just for the sake of it, here is the file i am using to rsync my backup webserver from inside the chroot cage :

    #!/bin/bash

    RSYNC_COMMAND="rsync /html/ webmaster@second_server:/html/ -e ssh -r --delete --times --modify-window=1 --exclude /serverid.html"

    $RSYNC_COMMAND -n -v

    while echo -n "Proceed with sync ?(y/n)"
    read response
    do
      case "$response" in
      'n')  echo "Syncronization cancelled"
            break
                           ;;
      "y")  $RSYNC_COMMAND -v
            echo "Syncronization completed"
            break
                           ;;
      esac
    done