How do we get good performance when using public/private key pairs for
authentication?
One reason to use svn+ssh is a preference for key pairs over passwords
for authentication. The svnbook describes how to setup a single system
user ID that can be used by multiple repository users:
http://svnbook.red-bean.com/nightly/en/svn.serverconfig.svnserve.html#svn.serverconfig.svnserve.sshtricks.fixedcmd
The ssh authorized_keys file is used to map public keys to Subversion
user IDs via the --tunnel-user option. Maintaining an authorized_keys
file like this is not much different from maintaining a Subversion
password file, the difference being it maps public keys to users rather
than users to passwords and passwords do not have to be maintained.
One big disadvantage of this setup is that the repository access pattern
gives poor Subversion performance. The reason is that svn+ssh has no
long-lived daemon but instead starts a new svnserve tunnel process for
each connection. This process has a small FSFS cache and that cache
starts empty. It's not possible to make the cache significantly bigger
as there may be a large number of processes, and even if it were larger
each operation starts with a new, empty, cache. To get good performance
while using key pairs we need a long-lived daemon of some sort.
I can simulate such a daemon by running a conventional svnserve daemon
and then replacing the svnserve tunnel process in the authorized_keys
file with a proxy process such as
command="socat STDIO TCP:localhost:3690"
In this case the client sets up an ssh tunnel, runs the socat proxy on
the server, and the svn protocol stream from the client gets handled by
the long-lived daemon. While this solves the performance problem it
doesn't really work: after authenticating to setup the ssh tunnel a
second authentication is required to pass a username to the svnserve
daemon. Running the svnserve daemon in anonymous mode solves the second
authentication problem but results in commits without svn:author and
disallows authz. A proxy like this is mostly pointless if Subversion
passwords still have to be maintained.
What we need is a new daemon that accepts an equivalent of --tunnel-user
for each connection. Here's a possible design:
1. A new svnserve "tunnel daemon" mode
svnserve --tunnel-daemon -T -r path -M size
This creates a named pipe and blocks reading from the pipe.
2. A new svnserve "tunnel proxy" started via the authorized_keys file:
svnserve --tunnel-proxy --tunnel-user name
This creates/binds/listens a localhost TCP socket and then writes
the port and the tunnel username to the named pipe. It then blocks
accepting a connection on the socket.
3. The tunnel daemon main thread receives the port and username and
hands these to a worker thread. The main thread goes back to
reading the named pipe. The worker thread connects back to the
tunnel proxy using the given port. Then the worker thread runs as
it would for our current svnserve tunnel mode.
4. The tunnel proxy accepts the connection from the tunnel daemon and
starts to proxy. It reads from stdin and writes to the socket, and
reads from the socket and writes to stdout.
The tunnel daemon will be able to read/write the repository as any
Subversion user. The named pipe can have the same OS permissions as the
repository and this limits communication between the tunnel-proxy and
the tunnel-daemon to system users that can already write to the
repository. Reads and writes on the named pipe should be atomic
provided they are less than PIPE_BUF.
Does this sound like a good idea?
APR has named pipe support but it is APR_ENOTIMPL on Windows so this
feature may start out as Unix only.
Are there alternative ways to get a long-lived daemon to do
authentication with public/private key pairs? Can svn:// with SASL do
it? It might be possible to extend SASL, but I think this would involve
client and server changes.
--
Philip Martin
WANdisco
Received on 2015-11-19 19:22:43 CET