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 protocol, which provides the protection on the contents
of the files via encryption. However, rsync over SSH requires 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 set up rsync over chroot SSH, which
limits the access to a designated area, and how to set up 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 networks for data sharing and backup, replacing
the application served by traditional 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/). This
article is based on OpenSSH_3.9p1 and OpenSSH_4.0p1.
This patch allows 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 must 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 differences between the
old and new script are 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 a Red Hat-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 contain the magic token "/./", for example:
backup:x:0:0:Backup Account:/apps02/backup/./home:/bin/bash
Upon establishing the SSH session, the user's new root directory is
"/apps02/backup" in this example.
As rsync connects to the 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 available under the 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 be available 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 libraries that rsync
and the shell (in this example, it is bash) use. On Red Hat-based
Linux systems (we tested on Red Hat 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 that the dependencies of the rsync and shell binaries are
complete, we can 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. With these files in place, we can run rsync over chroot SSH.
Rsync-Only Access over Chroot SSH
While we make rsync work under the 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 recommended to disable the shell access.
We note in a 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 files transfer,
SSH protects the data being transferred, and chroot protects data
on the server from unauthorized access. The dummysh limits the access
to rsync only.
While rsync server implements chroot, it lacks the SSH protection
that is often required. Besides, opening an additional rsync server
port presents a security risk and sometimes is not possible either
technically or politically. Sftp and scp lack 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 use in public networks, in areas traditionally
served by anonymous 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 his Master Degrees in Physics (Peking)
and Statistics (Columbia). Currently, he is applying his knowledge
to Unix systems administration, database management, and programming.
He can be reached at: xw73@columbia.edu. |