Mercurial SharedSSH setup with hg-admin-tools

Date: Fri Apr 25 2014 Mercurial

I'm looking at setting up a multi-user mercurial installation using the SharedSSH methodologies. There are three methods listed on the page, of which the hg-admin-tools method is the most comprehensive. All three of these methods use a common starting point.. the access to the repository is through an ssh URL, and ssh keys are used to coordinate access to the repositories by listing individual keys in .ssh/authorized_keys. By adding peoples ssh keys to .ssh/authorized_keys the first line of giving repository access permission is based on whether their key appears in authorized_keys.

The key is to simplify management of repositories and the users allowed to access the repositories. Of the three SharedSSH methodologies the hg-admin-tools method offers the most capabilities. However my first attempt to set it up ran into a difficulty because the install script requires that the user be running an ssh-agent. It was my first time encountering ssh-agent and it took a little while to understand how to work around the fact that I don't have ssh-agent available to me.

In the hg-admin-tools distribution is a README explaining that to install hg-admin-tools one must simply run the install script. However, this script failed for me, as I said. Let's dig a little into the script.

The install script starts with

ssh-add -L > first-adminkey

The server I'm using to test is running Ubuntu and this command gave me an empty file. Hurm.. Unfortunately the documentation wasn't much help, except to note that "ssh-add -L" is a way to retrieve an ssh key from the ssh-agent. As I said I don't have an ssh-agent and it wasn't clear if there was any connection between normal ssh keys and any key which ssh-agent would provide. However tracing further in hginit it's clear that the contents of the first-adminkey file become a file in the hgadmin repository which is to contain an ssh key:-

mv admin/hg-admin-tools/first-adminkey repos/hgadmin/keys/admin/first

Let's back up a half step. The hg-admin-tools method relies on a repository named hgadmin and inside this repository is a directory named keys within which are to be placed files containing ssh keys of registered users. In other words the file hgadmin/keys/admin/first would be a file containing an ssh key.

Ah hah! This gives us a simple workaround. The idea here is to simply initialize the hg-admin-tools with an ssh key of someone who will be the administrator of the repositories. Therefore it's a simple matter of copying your personal ssh key into the file first-adminkey then to comment out the ssh-add command in the "install" script before running it. That is, install would start like so:

set -e
# NOTE: Instead copy your personal ssh key to first-adminkey
# ssh-add -L > first-adminkey

Once you do this the rest of the install script will function as designed. I'm happy to say this workaround did the trick for me, and I've been able to do several operations on the test repository setup.

Now let's look at what hg-admin-tools lets one do.

First, let's retrieve the hgadmin repository. On your normal user account execute these commands

$ mkdir ~/hg
$ cd ~/hg
$ hg clone ssh://hg@repository-host/hgadmin
$ cd hgadmin

The URL ssh://hg@repository-host is where the shared repositories will be stored. The "hg" user ID is created by the "install" script (FWIW "install" can only be run on a Unix host on which the "adduser" command exists). Everybody who uses any of these shared repositories will access the repositories with a URL that begins with ssh://hg@repository-host. The .ssh/authorized_keys file for this "hg" user contains some magic listing anybody who has an ssh key registered with hgadmin.

The hgadmin directory contains two things: a) a file named "hg-ssh-access.conf" and b) a directory named "keys"

In "keys/admin/first" will be the ssh key which started out life in the first-adminkey file discussed above. To register a user with hgadmin requires these steps:-

  • Make a file named keys/users/user-name which contains that users public ssh key
  • Execute: hg add
  • Execute: hg push

There is a bit of magic inside the ssh://hg@repository-host/hgadmin repository such that when it receives an incoming changeset it regenerates the .ssh/authorized_keys file for the "hg" login. The presence of a key file inside the keys directory is what registers some person with hgadmin, and to unregister someone requires simply to delete their key file and push hgadmin back to the server because the regenerated .ssh/authorized_keys file will not have their ssh key.

The file named "hg-ssh-access.conf" contains definitions controlling who can do what in the shared repositories. The hg-admin-tools README contains the nitty-gritty-details... rather than go through everything let's look at some highlights.

By default the file contains this:

$ cat hg-ssh-access.conf 
init user=admin/*

What this says is any user who has an ssh key file under the "hgadmin/keys/admin/" will be able to initialize a repository. Remember that the key in first-adminkey became "keys/admin/first".

Creating a new repository in the shared repositories is done this way:-

$ cd ~
$ mkdir newrepo
$ cd newrepo
$ hg init
$ hg clone . ssh://hg@repository-host/newrepo

The new repository will exist with the pathname "~hg/repos/newrepo" but be accessed with the ssh URL: ssh://hg@repository-host/newrepo

Once the repository is created you can clone it: hg clone ssh://hg@repository-host/newrepo

I found however that this repository functioned a bit oddly. In particular the files contained within the repository did not show up. Once the new repository is created (as above) it was necessary to give it an hgrc file which contains the following:-

$ cat .hg/hgrc 
changegroup.aaaaa_update = hg update -C default > /dev/null

On every update to the repository an "hg update" will be run so that all the changesets are applied.

Granting read-only access to a repository is done as so

  • Make sure the user is registered as discussed above
  • In hg-ssh-access.conf add: read repo=repo-name user=users/user-name
  • Then make sure to "hg push" the hgadmin repository

That user will then be able to clone the named repository: hg clone ssh://hg@repository-host/newrepo

Granting a user the ability to push changes into a repository is done similarly. However the line to add will begin with "write" rather than "read". Once they are listed with a "write" line that user will be able to "hg push" into the repository.