Restricting rsync over ssh

by Juliet Kemp

I recently did some rearranging of our network setup, in part with the explicit aim of removing user logon access to several of the servers (both for security and for performance reasons). In general this has worked out fine - we use NFS so that users can still access all the relevant directories. However, the RAID array is used, among other things, for users to back up their laptops to - which means that the server running the RAID array needs to act as an rsync server.

By default these days, rsync runs over ssh. Which is great, but means that just restricting ssh access to the admin (me!) for the RAID array machine isn't an option; since then rsync too will fail. I wasn't keen on the idea of running it without ssh; not only a security issue, but also because it seemed that would mean having to keep another set of usernames/passwords, rather than relying on LDAP/Kerberos.

The solution I've come up with is slightly clunky but does the trick:

  • ssh is allowed, but only for a subset of users (using the AllowUsers directive in /etc/ssh/sshd_config) - those who've told me they need rsync backup access.
  • These users are then added to /etc/password, with the shell set as '/bin/nologin' (thus overriding the LDAP data) (this and the next step are the slightly clunky parts!)
  • They're also added to /etc/shadow, with *K* (meaning 'use Kerberos') as the password (maintaining two lots of passwords would be Very Bad).
  • /bin/nologin looks like this:
    # Script to disallow remote login - set as shell in /etc/passwd
    if (expr "$2" : 'rsync ..server .* .raid' > /dev/null)
        if (expr "$2" : '.*;' > /dev/null)
            /bin/sh "$@"
        echo "***********************************"
        echo "*        No login allowed!        *"
        echo "***********************************"

The trick I used for finding the command that's being sent (which is not the same as the command that you type on the command line) was to first set up /bin/nologin simply as

echo $@ > /tmp/command
and then examine /tmp/command on the rsync server.

Note that this doesn't worry about looking terribly hard for shell escapes (although it does look for anyone trying to pass an extra command in using ; - e.g. rsync directory/ "server:/data/directory/;rm -rf /" ). I'm using this in a local-access-only setup with a small number of reasonably trusted users, not on a machine that's open to the world at large, so I'm prepared to take more compromises than if the circs were different.

Also note that unfortunately rsync doesn't handle echo statements well - so there is no message to the user if they are misusing rsync (whether deliberately or accidentally). Again, in my case this is fine as the user will just contact me if they're legitimate and behaving legitimately.


Howard Katz
2006-05-23 08:52:45
Have you considered scponly?

I would consider this more robust and it also allows for a CHROOT jail.

Lawrence D'Oliveiro
2006-05-24 00:28:50
Why not just use the "command=" option in your users' ~/.ssh/authorized_keys files? See the sshd(8) man page for more info.
Juliet Kemp
2006-05-24 08:30:44
Howard: thanks for that. Unfortunately the server in question is Solaris & there's no version of scponly for Solaris 10. I'll certainly bear it in mind if/when I move to a Linux server for the RAID array; and if I have time may see if I can get it to work on S10.

Lawrence: I did consider that, but home directories are NFS-mounted across the entire network, and obviously users own their own ~/.ssh/authorized_keys files (so could alter any restrictions I put in). I don't want to prevent them from accessing their own authorized_keys file; and setting up separate home dirs for that machine only seems like more grief that the above solution. Having said that, it has just occurred to me that I could I suppose set all permitted users to have the same home directory (using /etc/passwd) & then I'd just have one file (/usr/common_home/.ssh/authorized_keys or whatever) that all would access. I might experiment with that; thanks.

2006-07-31 00:39:36
A number of issues here, the most obvious is that you haven't considered the environment, in particular the path. Note that ssh allows (for the traditional rsh compatibility reasons) environment variables to be passed in (and set up) as part of the session. I'd be worried also about users being able to send up new shell configs (~/.bashrc and suchlike), though this may be OK in this case.

Lawrence: the command= won't necessarily help you, because the command line varies depending on where your destination (or source) directory is.

Rsync also is a pretty disgustingly written program when it comes to the whole command invocation thing - unlike, say, cvs pserver (which although unbelievably painful in many ways, seems to be a reasonable model for such things) it does all of its negotiation on the command line (for at least the ssh model) rather than within the protocol. There are other things that won't hit you that it does completely wrong, but I was writing some code to multiplex rsync and some other command invocations over a single channel, and it doesn't quite behave itself with respect to writing data to sockets closed at the other end.