Implementing rsync only access over chroot ssh Julie Wang and Michael Wang Rsync provides a flexible and efficient way to transfer files. Unlike ftp and scp, it can transfer the subset of files that has changed, and only the differences within each file. Rsync can transfer files over ssh protocal, which provides the protection on the contents of the files via encryption. However, rsync over ssh requires the a shell account on the remote site. This shell account, if unrestricted, can potentially read and write beyond the intended area. It may fill the /tmp space as a trivial example. This article presents how to setup rsync over chroot ssh, which limits the access to a designated area; and how to setup rsync only account with other access methods disabled. With these two security measures, it is expected that rsync only access over chroot ssh can be used in public network for data sharing, and backup, replacing the application served by tranditional FTP with enhanced flexibility and efficiency provided by rsync, and security provided by ssh. CHROOT SSH Chroot() is a system call to make the supplied directory appear as root directory, thus limiting the access beyond the directory. This functionality can be added to the portable OpenSSH release via a 30-line patch. The patch and the patched source are maintained by James Dennis (http://chrootssh.sourceforge.net/). What described here are based on OpenSSH_3.9p1 and OpenSSH_4.0p1. This patch let sshd to look for "/./" in the user's home directory, and perform chroot on the directory before this token. This restricts the user to the area under this directory. The concept is the same as in the Washington University's ftp server. The installation of chroot ssh is done via the standard command sequence: configure, make, and make install. In our case, we prefer to have the new SSH server installed on a separate area, /usr/local/ssh, for the ease of maintenance. The new sshd is configured as: ./configure --prefix=/usr/local/ssh After the software install, The startup script for sshd needs to be changed to start the new sshd. The change is simple, just replacing the path to various binaries and configuration files. In our case, the new startup script is named new_sshd, and the difference between the old and new script is shown below: < # Init file for OpenSSH server daemon > # Init file for OpenSSH server daemon (with chroot) < [ -f /etc/sysconfig/sshd ] && . /etc/sysconfig/sshd > [ -f /usr/local/ssh/etc/sysconfig/sshd ] && . /usr/local/ssh/etc/sysconfig/sshd < KEYGEN=/usr/bin/ssh-keygen > KEYGEN=/usr/local/ssh/bin/ssh-keygen < SSHD=/usr/sbin/sshd > SSHD=/usr/local/ssh/sbin/sshd < RSA1_KEY=/etc/ssh/ssh_host_key > RSA1_KEY=/usr/local/ssh/etc/ssh_host_key < RSA_KEY=/etc/ssh/ssh_host_rsa_key > RSA_KEY=/usr/local/ssh/etc/ssh_host_rsa_key < DSA_KEY=/etc/ssh/ssh_host_dsa_key > DSA_KEY=/usr/local/ssh/etc/ssh_host_dsa_key In Redhat based systems, chkconfig can be used to disable the old startup script and enable the new one as: # chkconfig --del sshd # chkconfig --add new_sshd Once you have established a connection, stopping the old sshd daemon does not disconnect the existing session. The switching of the sshd daemon can be simply done as: ./sshd stop ./new_sshd start To prevent possible lockup - the old sshd is disabled but the new sshd is not started after reboot, and if you don't have access to the console, it is recommended that the telnet daemon be temporarily enabled. RSYNC OVER CHROOT SSH To utilize the chroot feature, the remote user's home directory should contains the magic token "/./", for example: backup:x:0:0:Backup Account:/apps02/backup/./home:/bin/bash Upon establishing ssh session, the user's new root directory is "/apps02/backup" in this example. As rsync connects to remote host via ssh, rsync is started on the fly in server mode (rsync --server). To make rsync work over chroot ssh, rsync must be availble under new root. This can be done simply by copying the rsync binary as well as the dynamic libraries it uses under the new, restricted root with the same directory structure. Besides the rsync command, the shell (and associated libraries) must available. This is because rsync is run using the user's shell, as documented in session.c: /* * Execute the command using the user's shell. This uses the -c * option to execute the command. */ argv[0] = (char *) shell0; argv[1] = "-c"; argv[2] = (char *) command; argv[3] = NULL; execve(shell, argv, env); The "ldd" command will print out the shared libaries that rsync and the shell (in this example, it is bash) uses. On Redhat based Linux systems (we tested on Redhat 9 and Fedora Core release 2), the binaries and associated libraries are: bin/bash usr/bin/rsync lib/ld-linux.so.2 lib/libdl.so.2 lib/libresolv.so.2 lib/libtermcap.so.2 lib/tls/libc.so.6 usr/lib/libpopt.so.0 To test the dependencies of the rsync and shell binaries are complete, we can just to run them under the chroot environment, like this: chroot /apps02/backup bash -c "rsync -h" Note that once we are in this chroot environment, the common commands such as "ls" are not available. We have to use shell built-in's. For example, instead of "ls" you can use "echo *", and so on. Anyway, with these files in place, we can run rsync over chroot ssh. RSYNC ONLY ACCESS OVER CHROOT SSH While we make rsync work under chroot ssh environment, the ssh and slogin are also available (sftp and scp are not as the sftp and scp subsystems are not available in the chroot environment). For security reasons, it is desired to disable the shell access. We note in previous section that for the rsync access, the only purpose of the full functional login shell is to run the rsync command. This is also revealed by the strace command in Linux: execve("/bin/bash", ["bash", "-c", "rsync --server ..."]) Armed with this knowledge, we will be able to build a dummy shell, named dummysh, which provides this functionality only. A shell version of dummysh is shown as below: #!/bin/bash if (( $# == 0 )) then printf "%s\n" "shell access is disabled. sorry." exit 1 elif (( $# == 2 )) && [[ $1 == "-c" && $2 == "rsync --server"* ]] then exec $2 fi The dummy shell can be tuned to enable or disable certain accesses. For example, we can write the conditions as: $2 == /usr/local/ssh/libexec/sftp-server || $2 == "rsync --server"* to allow sftp and rsync accesses. To enable dummysh, change the user login shell to /bin/dummysh, and copy it to both unrestricted and restricted environments. A C version of dummy shell would be more secure, and it helps to get rid of the real shell, bash in this example, altogether. SUMMARY This setup combines the best features from rsync, ssh, and chroot. rsync provides the flexibility and efficiency in file transfer, ssh protects the data being transferred, and chroot protects data on the server from unauthorized access. The dummysh can limits the access to rsync only. While rsync server implements chroot, it lacks the ssh protection which is often required. Besides, opening an additional rsync server port presents a security risk, and sometimes not possible either technically or politically. sftp and scp lacks the flexibility and efficiency provided by rsync, especially when a directory tree is involved, such as a web site. We hope this setup can find its use in public network, in areas traditionally served by anonymouns and private FTP. /* Julie Wang works for Independence Air (http://www.flyi.com/). She manages * Oracle databases, Unix operating systems, Lawson enterprise systems, * among others. She can be reached at Julie.Wang@flyi.com. * * Michael Wang earned Master Degrees in Physics (Peking) and * Statistics (Columbia). Currently, he is applying his knowledge to * Unix System Administration, Database Management, and Programming. * He can be reached at: xw73@columbia.edu. * * This article is published by Sys Admin, August 2005 issue. * . */ PS1: The rsync-only account can be made as rsync read only, rsync write only, and rsync read and write. This is because the remote rsync command has "--sender" option when it is requested to send, and does not have the option when it is requested to receive. We can allow or disallow the send or receive request as a site policy. PS2: The rsync only shell account can be setup to allow su. This is possible because su does not go through ssh and chroot(). The login shell (/bin/dummysh) it uses is the one in unrestricted environment. On the other hand, the login shell (/bin/dummysh) ssh/chroot uses is in the restricted environment. So if /bin/dummysh in unrestricted environment is made to execut a real shell, this login is a normal login for the purpose of su. Example: # grep backup /etc/passwd backup:x:0:0:Backup Account:/apps02/backup/./home:/bin/dummysh # cat /bin/dummysh #!/bin/bash exec /bin/bash # su - backup bash-2.05b# ps -eaf | grep $$ root 24166 24165 0 21:56 pts/32 00:00:00 /bin/bash /bin/dummysh does not even appear in the process table due to "exec" call. On the other hand, if /bin/dummysh in chroot() environment is as shown above, it blocks ssh access. Example: $ ssh backup@mercury backup@mercury's password: Last login: Thu Jul 14 21:57:27 2005 from 192.168.1.1 shell access is disabled. sorry. Connection to mercury closed. But the rsync access is allowed: $ rsync -av --rsh=ssh backup@mercury:/home/ backup@mercury's password: receiving file list ... done -rw-r--r-- 0 2004/09/16 01:03:24 a ... sent 16 bytes received 123 bytes 39.71 bytes/sec total size is 848 speedup is 6.10 This setup provides a solution for the requirements in many environments: - disallow direct login - allow su - allow file transfer