JimmyG | Blog | Book | Life | Projects | Contact

Subversion over SVN+SSH on Debian

Posted: Tue 4th Dec 2007, 5:02pm
Tags: debian, hosting

The traditional way of setting up subversion is via Apache but what if you don't want to install Apache? In that case you can use svnserve but what if your host won't let you run a persistant server? In that case you can create a linux system account for every user and give them induvidual SSH logins. You then give your users a URL starting svn+ssh:// instead of the usual http://. The Subversion client recognises this form of URL and invokes a local ssh process, connecting to the host, authenticating as the user, then spawning a private svnserve process on the remote machine running as that user. The svnserve command is being invoked in tunnel mode (-t) and its network protocol is being tunneled over the encrypted connection by ssh, the tunnel-agent. svnserve is aware that it's running as the user, and if the client performs a commit, the authenticated username will be attributed as the author of the new revision.

This is all well and good but you might not wa nt to have to create user accounts for each of the users you want to give access to subversion. In this case you can create one system user account and use a different SSH private key for each user you want to grant access to. You then setup the ~/.ssh/authorized_keys file so that each user only has access to svnserve and that each svnserve process is started with their particular username so that all the commits are handled correctly. This is only possible with subversion 1.1.0 and above.

This is what we are going to do for two users, james and mike. james is a linux user, mike is a Windows user.

First let's setup the server:

sudo apt-get install subversion openssh-server

Then create a subversion user svn, a home directory, an .ssh directory and finally set the correct permissions:

sudo useradd svn
sudo mkdir /home/svn
sudo mkdir /home/svn/.ssh
sudo chown -R svn:svn /home/svn

If you intend to be able to login as svn for testing purposes later you should set a password:

sudo passwd svn

Create the directory for the repositories:

sudo mkdir /var/svn

Setup a repo repository and commit the first import:

sudo mkdir /var/svn/repo
sudo mkdir /tmp/repo
sudo mkdir /tmp/repo/branches
sudo mkdir /tmp/repo/tags
sudo mkdir /tmp/repo/trunk
sudo svnadmin create /var/svn/repo
sudo svn import /tmp/repo file:///var/svn/repo -m "initial import"
sudo rm -rf /tmp/repo

Change the permissions so that the svn user and svn group have access:

sudo chown -R svn:svn /var/svn/repo

Set the permissions to 770 and the umask to 2 so that any files are created have the correct permissions:

sudo chmod 2770 -R /var/svn/repo

At this point you can test the setup manually. As a user other than svn try this:

svn co svn+ssh://svn@localhost/var/svn/repo .

This should prompt you for the password a couple of times and then checkout the repository.

We are half way there, next we need to set up two SSH public-private key pairs, one for Mike, one for James. As the svn user run:

ssh-keygen -t rsa -b 1024 -f mike.key
ssh-keygen -t rsa -b 1024 -f james.key

With each command you can just press <Enter> twice unless you want to set a password. You will now have the files mike.key, mike.key.pub, james.key and james.key.pub in the current directory. The .key files are the private keys. They should be given securely to Mike and James repsectively. Anyone who gets hold of the files will have the same permissions to the repository as James and Mike so they should be treated with the same respect as a password and not posted online!

Since Mike is on Windows he will need WinSCP, PuTTY, PuTTYgen and TortoiseSVN (the latter requires adminsitrator access to install).

Here's what he does:

From James's point of view the process is a bit simpler becasue on Linux the .key file doesn't need to be converted to a .ppk file:

Now that the private keys are set up it is time to setup the public keys on the subversion server. Copy the contents of mike.key.pub and james.key.pub to /home/svn/.ssh/authorized_keys so that the each publick key is on a separate line:

touch .ssh/authorized_keys
cat mike.key.pub >> .ssh/authorized_keys
cat james.key.pub >> .ssh/authorized_keys

It should look something like this but with lots of characters in place of ...:

ssh-rsa AAA...acFHU= svn@example
ssh-rsa AAA...acBDH= svn@example

The three columns are TYPE KEY COMMENT. Since the last part is a comment it makes sense to change it to the names of the users so you remember which line referrs to who:

ssh-rsa AAA...acFHU= mike
ssh-rsa AAA...acBDH= james

This is all well and good but at the moment Mike and James can also SSH directly into the svn account so we want to restrict their access. Each line in the authorized_keys file can also have a command="COMMAND" section at the start. The subversion client executes svnserve -t when it SSHs in but you can also specify the executable to use explicitly:

command="/usr/bin/svnserve -t" ssh-rsa AAA...acFHU= mike
command="/usr/bin/svnserve -t" ssh-rsa AAA...acBDH= james

Now you might want to limit the users to a particular repository, you can do this with the -r option:

command="/usr/bin/svnserve -t -r /var/svn/" ssh-rsa AAA...acFHU= mike
command="/usr/bin/svnserve -t -r /var/svn/" ssh-rsa AAA...acBDH= james

James and Mike can access the repo repository with svn+ssh://svn@localhost/repo instead of svn+ssh://svn@localhost/var/svn/repo

You will probably want to limit the other things the users can do with the svn account so you'd probably add no-port-forwarding,no-pty,no-agent-forwarding,no-X11-forwarding to the command like this:

command="/usr/bin/svnserve -t -r /var/svn/",no-port-forwarding,no-pty,no-agent-forwarding,no-X11-forwarding ssh-rsa AAA...acFHU= mike
command="/usr/bin/svnserve -t -r /var/svn/",no-port-forwarding,no-pty,no-agent-forwarding,no-X11-forwarding ssh-rsa AAA...acBDH= james

So far, the svnserve command still doesn't know which username it is supposed to be using so we add the --tunnel-user option to tell it explicitly:

command="/usr/bin/svnserve -t -r /var/svn/ --tunnel-user=mike",no-port-forwarding,no-pty,no-agent-forwarding,no-X11-forwarding ssh-rsa AAA...acFHU= mike
command="/usr/bin/svnserve -t -r /var/svn/ --tunnel-user=james",no-port-forwarding,no-pty,no-agent-forwarding,no-X11-forwarding ssh-rsa AAA...acBDH= james

At this point everything should now work as expected. Both James and Mike should be able to access and commit to the repository without needing a password and their commits should be logged as james and mike respectively because they are using different private keys and we are using the --tunnel-user approach. No other users should be able to access the repository except the svn user if you know the password.

Further reading:

http://svnbook.red-bean.com/en/1.1/ch06s03.html http://allyourtech.com/content/articles/23_12_2005_setting_up_subversion_and_tortoisesvn.php http://allyourtech.com/content/articles/24_12_2005_ditching_the_password_prompts_in_tortoisesvn.php http://svn.collab.net/repos/svn/trunk/notes/ssh-tricks http://svn.haxx.se/dev/archive-2004-03/0253.shtml http://bitworking.org/news/Getting_subversion_svn_ssh____to_work_with_PuTTY http://www.linuxfromscratch.org/blfs/view/svn/server/svnserver.html

For testing it is also possible to set the $SVN_SSH variable specifying the private key to use with -i so that you can try connecting as different users. For example:

export SVN_SSH="ssh -i /home/james/.ssh/mike.key