Sometimes I want to transfer files including ownership. This is not possible as normal user as the
chown(2) system call requires special privileges, that is:
uid == 0. However, I do not want to open ssh access for root, but go with the usual way to elevate my privileges: sudo.
I will go through common solutions presented on the web and explain why these do not work at all without significant modifications on the remote host and then present a working solution using X11-Forwarding that is less invasive.
Proposed non-working solutions
The naive solution would be to try to inject sudo into the remote command that rsync wants to run:
$ rsync --rsync-path="sudo rsync" -a foo $HOST:foo sudo: no tty present and no askpass program specified rsync: connection unexpectedly closed (0 bytes received so far) [sender] rsync error: error in rsync protocol data stream (code 12) at io.c(226) [sender=3.1.2]
However, as you can see that will not work as sudo wants to prompt for your password. The ssh connection is non-interactive and does not have any pseudo-tty allocated. This could be enabled with
-e "ssh -tt" to force pseudo-tty allocation in ssh. However, this will fail again, because the password prompt of sudo will actually be read by rsync. As rsync cannot understand this message and expected to talk to the remote rsync daemon instead, this will result in a protocol mismatch.
One way to get this to work is to get rid of the password prompt of sudo. In several tutorials, I found the suggestion to first authorize to sudo remotely by running
ssh -t $HOST sudo -v and then the following authentication attempts will pass without password because of sudo’s caching. However, this will not work at all with
tty_tickets in sudo, which is the default configuration. That means the password is cached for each login session separately and therefore, the user needs to authenticate again with their password on the next connection. That could be changed, but it is usually wise to stick with the default configuration for tools that are relevant to the security of the host.
Other suggestions include granting the remote user the right to execute rsync without password via
username ALL= NOPASSWD:/usr/bin/rsync
However, that will basically defeat the whole purpose of prompting for a password. With this configuration, an attacker could easily overwrite any file in the system. Therefore I do not want to use something like this.
Working solution with X11 forwarding
The main problem as explained above is that the sudo password prompt is in the same channel as the rsync communication. Therefore the authentication has to be moved to another channel to avoid this conflict. In order to stop sudo from prompting on the tty (which basically is the same as standard input over ssh), sudo can be told to launch another program instead. This program will obtain the password from the user and pass it on to sudo. One example for such a program would be ssh-askpass, which presents a X11 window prompting for the password.
In order to get this to work, first enable X11 forwarding in ssh, use
sudo -A to stop sudo from prompting itself, and also pass the command we want to run in the environment variable
rsync -e "ssh -X" --rsync-path="SUDO_ASKPASS=/usr/bin/ssh-askpass sudo -A rsync" -a foo $HOST:foo
The resulting prompt is not very pretty, but it works for me. Other options would include
ssh-askpass-gnome, which presents a window with GTK widgets instead.
The actual problem here is the inflexibility of the rsync protocol. It would be nice to be able to first do any kind of authentication and run custom commands, and only then start the actual rsync protocol by handing over the file descriptor. Moving the authentication to another channel is a workaround that bypasses this limitation. For the solution presented here, ssh-askpass needs to be present on the remote host and you need to be allowed to use X11 forwarding.
Other possible options would include forwarding a Unix socket connection with ssh and then use this with a custom
askpass tool (running both remote and local) to relay the password to sudo. Unfortunately,
SUDO_ASKPASS has to be a single file without any arguments at an absolute path and therefore it is not possible to pass a custom script on the fly. This means the
askpass tool would always need to be installed beforehand.