Using rsync to create a limited ability to write remote files

September 4, 2024

Suppose that you have an isolated high security machine and you want to back up some of its data on another machine, which is also sensitive in its own way and which doesn't really want to have to trust the high security machine very much. Given the source machine's high security, you need to push the data to the backup host instead of pulling it. Because of the limited trust relationship, you don't want to give the source host very much power on the backup host, just in case. And you'd like to do this with standard tools that you understand.

I will cut to the chase: as far as I can tell, the easiest way to do this is to use rsync's daemon mode on the backup host combined with SSH (to authenticate either end and encrypt the traffic in transit). It appears that another option is rrsync, but I just discovered that and we have prior experience with rsync's daemon mode for read-only replication.

Rsync's daemon mode is controlled by a configuration file that can restrict what it allows the client (your isolated high security source host) to do, particularly where the client can write, and can even chroot if you run things as root. So the first ingredient we need is a suitable rsyncd.conf, which will have at least one 'module' that defines parameters:

[backup-host1]
comment = Backup module for host1
# This will normally have restricted
# directory permissions, such as 0700.
path = /backups/host1
hosts allow = <host1 IP>
# Let's assume we're started out as root
use chroot = yes
uid = <something>
gid = <something>

The rsyncd.conf 'hosts allow' module parameter works even over SSH; rsync will correctly pull out the client IP from the environment variables the SSH daemon sets.

The next ingredient is a shell script that forces the use of this rsyncd.conf:

#!/bin/sh
exec /usr/bin/rsync --server --daemon --config=/backups/host1-rsyncd.conf .

As with the read-only replication, this script completely ignores command line arguments that the client may try to use. Very cautious people could inspect the client's command line to look for unexpected things, but we don't bother.

Finally you need a SSH keypair and a .ssh/authorized_keys entry on the backup machine for that keypair that forces using your script:

from="<host1 IP>",command="/backups/host1-script",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty [...]

(Since we're already restricting the rsync module by IP, we definitely want to restrict the key usage as well.)

On the high security host, you transfer files to the backup host with:

rsync -a --rsh="/usr/bin/ssh -i /client/identity" yourfile LOGIN@SERVER::backup-host1/

Depending on what you're backing up and how you want to do things, you might want to set the rsyncd.conf module parameters 'write only = true' and perhaps 'refuse options = delete', if you're sure you don't want the high security machine to be able to retrieve its files once it has put them there. On the other hand, if the high security machine is supposed to be able to routinely retrieve its backups (perhaps to check that they're good), you don't want this.

(If the high security machine is only supposed to read back files very rarely, you can set 'write only = true' until it needs to retrieve a file.)

There are various alternative approaches, but this one is relatively easy to set up, especially if you already have a related rsync daemon setup for read-only replication.

(On the one hand it feels annoying that there isn't a better way to do this sort of thing by now. On the other hand, the problems involved are not trivial. You need encryption, authentication of both ends, a confined transfer protocol, and so on. Here, SSH provides the encryption and authentication and rsync provides the confined transfer protocol, at the cost of having to give access to a Unix account and trust rsync's daemon mode code.)


Comments on this page:

You can also do this with straight rsync-invoked-by-ssh and captive keys. The sending side does something like rsync -av -e ssh /path/to/file.tgz user@destination:/var/foo/, while the recipient's authorized_keys file for user looks something like:

from="192.0.2.1",command="rsync --server -vlogDtpre.iLsfxCIvu . /var/foo/" ssh-rsa AAAAB3NzaC....IrQ== senduser@sender

Thus preventing the corresponding private key from being used to do anything other than rsync into the /var/foo directory, from the specified sender address (in this example, 192.0.2.1 ).

By nel at 2024-09-05 13:50:40:

at the cost of having to … trust rsync's daemon mode code

If you've got unprivileged user namespaces enabled (as most Linux distributions do by default; or if you're willing to make bwrap setuid), it'd be a fairly simple matter to prefix the rsync command with a "bwrap" invocation that leaves only a chosen directory writable. Basically, --ro-bind /usr /usr, symlink each of /bin /sbin /lib /lib64 into their /usr counterparts, --bind the directory to be writable, and add --dev and --proc and ro-bind /etc as required. All that and --unshare-all --disable-userns will considerably reduce the amount of trust required of rsync.

You could always run rsync chrooted or in a container.

By nel at 2024-09-06 15:03:25:

Fazal, just in case you (or other readers) didn't know, bwrap—also called BubbleWrap—is a utility for ad-hoc container creation. And while I don't fully understand the details, it can somehow do the equivalent of chroot without its security problems, which means it can be used without privilege, if the system is appropriately configured. bwrap also tends to be more convenient, since one doesn't have to edit fstab to add extra /proc and /dev mounts and such within a "chroot" (for programs that need them).

I've been using it quite a bit in recent years; sometimes for security (sandboxing), sometimes just to stop programs from writing junk into my home directory. In the case of Chromium and Firefox, I can use "managed" policies and "autoconfig" files, which they look for in directories not normally writable by unpriviliged users.

Written on 04 September 2024.
« TLS Server Name Indications can be altered by helpful code
The problems (Open)ZFS can have on new Linux kernel versions »

Page tools: View Source, View Normal.
Search:
Login: Password:

Last modified: Wed Sep 4 22:56:20 2024
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.