Mercurial is a great version control system, go check it out if you haven’t so far. Besides the features of the version control system itself, there is one thing that is very important to me. Multiple developers must be able to access a single shared repository in a simple and secure way. I very much like to use SSH as the underlying protocol. But how do you really setup a mercurial repository shared via SSH?
It’s not at all that complicated. All you need is a fresh UNIX user account (let’s name it hg) and some SSH voodoo. Let’s start with the UNIX account. The basic idea is to have a shared UNIX user account, hosting one or more mercurial repositories. Developers may access the repositories by logging into that shared account. So let’s create a new account. As root type:
localhost:~# useradd -m -c 'Mercurial Repositories' hg localhost:~# ls -ld ~hg drwxr-xr-x 3 hg hg 4096 2010-03-18 01:52 /home/hg localhost:~#
You’ve got a new user account with a new home directory called /home/hg. Now let’s setup a mercurial repository inside that home directory. First we become the hg user using su - hg which also places us directly into the correct home directory (~hg). For our new mercurial repository we need a place. If we only wanted one repository we could just initialize ~hg as hg repository, but eventually we might want more. So we’ll create a subdirectory as a place for our repository, using mkdir project1 and initialize it cd project1; hg init. That’s it.
hg@localhost:~$ mkdir project1 hg@localhost:~$ cd project1; hg init hg@localhost:~/project1$ ls -la total 12 drwxr-xr-x 3 fries fries 4096 2010-03-18 02:06 . drwx------ 79 fries users 4096 2010-03-18 02:06 .. drwxr-xr-x 3 fries fries 4096 2010-03-18 02:06 .hg hg@localhost:~/project1$
We’ve just created a new UNIX user account and initialized a new mercurial repository in a directory called ~hg/project1. Not too complicated so far. Now, how can this repository be reached via network? Well its URL is ssh://hg@ip-or-hostname/project1.
But wait a second, we didn’t set a password for the hg account. So how are we supposed to log in? Well, to play with our new mercurial server we could just set a password and we are good. But that would have some consequences:
- We would need to share a single password with all developers
- Each person knowing the password may login and get shell access
- With shell access one could completely delete the repository, change the password etc.
- Using SSH with username/password is less secure than using public key authetication
So instead of setting a password for the account, we will collect public keys from all persons having access to the repository and store them in ~hg/.ssh/authorized_keys. In this post I described how to create, transfer and install keys.
Using public key authentication we get rid of the shared password. We can also easily deny a person access to the repository by removing the corresponding key from the authorized_keys file. But we can do even more. The authorized_keys file allows to associate certain options and restrictions with a particular public key. The general format of this file id
options keytype base64-encoded-key comment
The options field is optional and is usually unused. So many people are not aware of it. There is one particularly useful option called command. It specifies a command that is executed whenever the corresponding key is used for authentication. Now mercurial comes with a handy program called hg-ssh which is exactly intended to be used in this scenario. hg-ssh takes a list of directories as command line arguments the associated key should have push/pull access to. In our example a authorized_keys file giving push/pull access to email@example.com would look like this:
command="hg-ssh project1" ssh-dss AAAAB3Nz...KpIDFfiHQ== firstname.lastname@example.org
To further narrow down what a remote user may do in our network we should also add these options: no-port-forwarding, no-X11-forwarding, no-agent-forwarding. That basically inhibits the remote user to open port forwards into or from our network. The final entry in authorized_keys for a single user haveing push/pull access to project1 is then:
command="hg-ssh project1",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-dss AAAAB3Nz...KpIDFfiHQ== email@example.com
You may now add more keys to the file in the same style. Note that you may have as many repositories as you want. And it is up to you to assign push/pull access to a particular key. You might also add a key with no command restriction at all and use this one to log in to the hg account for administration e.g. adding an other key to the authorized_keys file.
Now that your mercurial repository is all set up and accessible via ssh you might clone it using:
hg clone ssh://hg@ip-or-hostname/project1