Last modified 4 years ago Last modified on 19.09.2017 15:50:07

rsync over SSL


The Right Way(tm) of doing rsync over SSL. Uses stunnel 4.x.

How to download

git clone

How to build


How to use


How does it work?

Typical setup

stunnel is used by most of how-tos out there on how to encapsulate rsync in SSL. This kind of setup is pretty simple: you need to start two stunnel daemons:

  1. On the rsync/SSL server side, stunnel accepts SSL connection, decrypts it and forwards to rsync --daemon, which is bound to address.
  2. On the rsync/SSL client side, stunnel accepts raw protocol connection, encapsulates it with SSL and forwards to the daemon from the previous point.

On client side, your rsync connects to localhost:<stunnel-port>. This approach is ugly, troublesome, can lead to security issues and it doesn't scale.


Three daemons while my hunch says one is enough. Isn't it ugly? And imagine that you need to maintain all of them!


Your rsync can't connect to the server. Which connection is broken? To client stunnel daemon? To server stunnel? To rsync daemon?


Imagine that you have authentication/authorization based on client SSL certificates. It's great unless an untrusted user can connect to client stunnel daemon, and he can connect if he has an account on client machine. Or unless he can connect to rsync daemon, which is the case when he has an account on server machine.

Sure, you can limit who can connect to which ports using owner netfilter module, but this makes complicated setup just more complicated. Do you really want this?

Setup Complexity

You have a client daemon to connect to one server. Now you want to use another SSL enabled rsync server. And another. And another three. Pop! And suddenly you're maintaining on client machine daemons for each rsync server you use. Now try to remember which server is assigned to which port. And what if you just need to use the server once? You still need to setup a new stunnel daemon.

Correct setup

First of all, let's get rid of client stunnel.

stunnel can work in inetd mode. This means, instead of listening on some TCP port for plain-text connections it can take STDIN/STDOUT. Now think of how rsync works over SSH: /usr/bin/rsync spawns ssh user@somehost, which executes /usr/bin/rsync on the remote side, and then local rsync talks to ssh's STDIN/STDOUT.

Hey! That's exactly what inetd mode is! Can we use /usr/bin/stunnel instead of /usr/bin/ssh? Of course we can. This is what rsync has the --rsh option for.

Now we want to dispense with one daemon on the server side.

stunnel in server mode, instead of connecting to some TCP port can spawn an external program (config options exec and execargs). Very much like inetd. On the other side, rsync can work under inetd. Combining these two, you get stunnel spawning rsync. Single daemon instead of two, and nobody can connect to rsync daemon unless connects to stunnel first.

Technical details

To detect whether it is executed in inetd-like manner, rsync --daemon checks if its STDIN (file descriptor 0) is a socket. If yes, this socket is used for reading and writing, and STDOUT is closed.

Note that while the socket can be created in any manner (e.g. accept(2), connect(2), socketpair(2), protocol AF_INET, AF_INET6, AF_UNIX), a regular pipe (pipe(2)) won't do, and rsync --daemon will try to listen on a TCP port on its own.