How to setup different SVN+SSH repositories on the same server

A common solution to host a SVN repository for small projects is to tunnel SVN via SSH. In this scenario, the repository resides on a server hosted by a specific user (repository owner). He will let other users connect to his account, identified by their SSH identities, but limit their acces to the utilisation of the svnserver command with specific parameters including the repository location. One SSH identity is therefore bound to a specific directory. This means, that a user has to have several identity files. Until recently, I didn't know how to set this up. Here's how.

Create SSH identity files

Let's say we want to set up two repositories project1 and project2. Let's create two keys (one for each project) for the user ingo on his personal workstation first (see ssh-keygen man page for details):

  1. ingo@workstation:~$ mkdir .ssh/project1 .ssh/project2
  2. ingo@workstation:~$ ssh-keygen -t rsa -f .ssh/project1/id_rsa
  3. Generating public/private rsa key pair.
  4. Enter passphrase (empty for no passphrase):
  5. Enter same passphrase again:
  6. Your identification has been saved in .ssh/project1/id_rsa.
  7. Your public key has been saved in .ssh/project1/id_rsa.pub.
  8. The key fingerprint is:
  9. dc:d3:c6:13:4f:cb:e1:a0:53:0b:c1:4e:ed:5c:86:3f ingo@workstation
  10. ingo@workstation:~$ ssh-keygen -t rsa -f .ssh/project2/id_rsa
  11. [...]

Create SVN repositories

Now, let's create the repositories on the server (see here for more information):

  1. owner@server:~/svn$ mkdir project1 project2
  2. owner@server:~/svn$ svnadmin create project1/
  3. owner@server:~/svn$ svnadmin create project2/

In the SSH config file ~/.ssh/authorized_keys (create the file if not present), we need to add the two following lines (see here for more information):

  1. command="svnserve -t --tunnel-user=ingo -r /home/owner/svn/project1",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-rsa AAAAB3Nz1...
  2. command="svnserve -t --tunnel-user=ingo -r /home/owner/svn/project2",no-port-forwarding,no-agent-forwarding,no-X11-forwarding,no-pty ssh-rsa AAAAB3Nz2...

AAAAB3Nz1... and AAAAB3Nz2... have to be replaced by the content of the files ~/.ssh/project1/id_rsa.pub and ~/.ssh/project1/id_rsa.pub of ingos workstation. Each lines limits the acces of the SSH key in this line to a specific command specified by command= and forbids a couple of SSH features for security reasons. Here, the command is svnserver with the SVN user name ingo and one of the SVN repository we just created.

Use specific identity file

Now comes the interesting part. SVN doesn't provide a possibility to specify a SVN identitfy when doing a SVN checkout. But we can define a new "protocol" (or tunnel) which uses SSH with a specific identity file. In the SVN config file ~/.subversion/config on ingos workstation, we add the following two lines into the [tunnels] section (see here and here for more information).

  1. [tunnels]
  2. project1 = $SVN_SSH ssh -i /home/ingo/.ssh/project1/id_rsa
  3. project2 = $SVN_SSH ssh -i /home/ingo/.ssh/project2/id_rsa

Now, we have two new protocols that we use instead of svn+ssh://: svn+project1:// and svn+project2://. The checkout therefor looks like this:

  1. ingo@workstation:~$ mkdir project1 project2
  2. ingo@workstation:~$ svn checkout svn+project1://owner@server.com/ project1/
  3. Checked out revision 0.
  4. ingo@workstation:~$ svn checkout svn+project2://owner@server.com/ project2/
  5. Checked out revision 0.

What's happening in the background?

Here's what happened in the background: SVN sees the protocol svn+project1:// and executes therefor SSH with the parameters we specified in the config file. SSH will connect to the server as owner using the identity file ~/.ssh/project1/id_rsa. The server will find the entry of this identity file in .ssh/authorized_keys in owners home directory and execute svnserver with the specified parameters. Now, the local svn process doing the checkout and the svnserver on the server communicate directly. svnserver will only allow operations in the project1 repository using the SVN user ingo. Using another identity file will either change the SVN repository or the SVN user (for another person working on the projects).

Summary

We've just seen a way to setup multiple SVN repositories on the same server. We were using a SSH tunnel to connect to these directories, which enables other users than the repository owner to use these repositories securely. On the client side, we saw how to define new tunnels using a specific SSH identity. That way, we can use different identity files for different SVN repositories.

If you have questions about that guide, please post a comment.

Update (08/08/2009)

I just realized that the command option no-forwarding is not correct and removed it from the example. With that option, I was not able to connect to the svn+ssh server, but was asked for my password instead. I suppose that removing that option is reasonably save, as the official SVN book doesn't use it either.