Mercurial and mercurial-server on CentOS

In my previous two posts I explained how I configured Python 2.7 and Mercurial 1.9.3 on CentOS 5.5 x64 for HTTP access. HTTP access is fine but life is simpler when using SSH.

Overview
mercurial-server is one of many shared SSH publishing solutions to allow your development team have access to your central repository collection.

It kind of works like this. A single SSH account (hg) is created on your Mercurial central repo server. This account acts as a gateway account and it also owns all of the repositories on the server. The account is heavily restricted. You can’t logon to it using a username and password, only SSH keys are permitted and these keys must be imported using mercurial-server’s refresh-auth script (or via upload to a special administrative repository once the administrator’s public key has been installed).

Each developer generates a public and private SSH key pair. Their public key is uploaded to central repository server and is added to the hg account’s ~hg/.ssh/authorized_keys file using the refresh-auth script (or via the hgadmin administrative repository.

Each authorized_keys record also has the command option specified which restricts the hg logon to a single operation regardless of any commands specified by the client (which are ignored). For example:

no-pty,no-port-forwarding,no-X11-forwarding,no-agent-forwarding,command="/usr/local/share/mercurial-server/hg-ssh root/bob/bob.pub" ssh-dss AAAAB3NzaC1kc...Qfa02w== root@vostro1700.zygonia.net

For a more detailed explanation of how this works see: How mercurial-server works

Installation
First off, download and unzip mercurial-server from here:

mercurial-server 1.2 sources

wget http://dev.lshift.net/paul/mercurial-server/mercurial-server_1.2.tar.gz
tar zxf mercurial-server_1.2.tar.gz
cd mercurial-server_1.2.orig

Before building and installing there’s some tweaks I needed to make for my CentOS install.

On CentOS we need to prevent the docbook files being built. They’re not necessary and the build will fail on CentOS if you try to build them.

Open up Makefile, at around line 53 you’ll see this step:

installfiles: installetc installdoc pythoninstall

I removed the installdoc reference, so it should look like this:

installfiles: installetc pythoninstall

Next we need to amend the useradd command switches at around line 61. CentOS doesn’t support the --system switch and we need to use -r instead:

useradd:
        useradd -r --shell /bin/sh \
            --home /var/lib/mercurial-server --create-home \
            --comment  "Mercurial repositories" $(NEWUSER)

Finally, because I have both Python 2.4 and 2.7 installed and my Mercurial installation has been installed for Python 2.7, I need to ensure that Python 2.7 is called.

To make this easier I modified the Makefile just a bit more and added a PYTHON variable to point to the default version of python I want to use. This is because make doesn’t expand bash aliases, so even if you’ve aliased python to Python 2.7 it’ll be ignored. Setting a PYTHON variable and using that makes life easier. It also means you can override the python version from the command line. This is my revised Makefile:

#!/usr/bin/env make -f

PREFIX=/usr/local/share
LIBDIR=$(PREFIX)/mercurial-server
DOCDIR=$(PREFIX)/doc/mercurial-server
ETCDIR=/etc/mercurial-server
NEWUSER=hg
DOCBOOK_XSL=/usr/share/xml/docbook/stylesheet/nwalsh
PYTHON=/usr/bin/python2.7

INSTALL=install

build: build/html/index.html pythonbuild

setup-adduser: installfiles adduser inituser

# WARNING: this is experimental
setup-useradd: installfiles useradd inituser

remove:
        rm -Rfv $(DESTDIR)$(ETCDIR)
        rm -Rfv $(DESTDIR)$(LIBDIR)
        userdel -rf $(NEWUSER)

installetc:
        $(INSTALL) -d $(DESTDIR)$(ETCDIR)
        $(INSTALL) -m 644 -t $(DESTDIR)$(ETCDIR) \
            src/init/conf/access.conf
        $(INSTALL) -d $(DESTDIR)$(ETCDIR)/remote-hgrc.d
        $(INSTALL) -m 644 -t $(DESTDIR)$(ETCDIR)/remote-hgrc.d \
            src/init/conf/remote-hgrc.d/access.rc \
            src/init/conf/remote-hgrc.d/logging.rc
        $(INSTALL) -d $(DESTDIR)$(ETCDIR)/keys/root
        $(INSTALL) -d $(DESTDIR)$(ETCDIR)/keys/users

installdoc: build/html/index.html
        $(INSTALL) -d $(DESTDIR)$(DOCDIR)
        $(INSTALL) -m 644 -t $(DESTDIR)$(DOCDIR) README
        $(INSTALL) -d $(DESTDIR)$(DOCDIR)/html
        $(INSTALL) -m 644 -t $(DESTDIR)$(DOCDIR)/html build/html/index.html

build/html/index.html: doc/manual.docbook
        xsltproc --nonet -o $@ $(DOCBOOK_XSL)/html/docbook.xsl $^

build/pdf/manual.pdf: doc/manual.docbook
        mkdir -p build/pdf
        fop -xml $^ -xsl $(DOCBOOK_XSL)/fo/docbook.xsl $@

pythonbuild:
        $(PYTHON) setup.py build

pythoninstall:
        $(PYTHON) setup.py install \
            --install-purelib=$(DESTDIR)$(LIBDIR) \
            --install-platlib=$(DESTDIR)$(LIBDIR) \
            --install-scripts=$(DESTDIR)$(LIBDIR) \
            --install-data=$(DESTDIR)$(LIBDIR)

installfiles: installetc pythoninstall

adduser:
        adduser --system --shell /bin/sh --group --disabled-password \
            --home /var/lib/mercurial-server \
            --gecos  "Mercurial repositories" $(NEWUSER)

# WARNING: this is experimental
useradd:
        useradd -r --shell /bin/sh \
            --home /var/lib/mercurial-server --create-home \
            --comment  "Mercurial repositories" $(NEWUSER)

inituser:
        su -l -c "$(DESTDIR)$(LIBDIR)/init/hginit $(DESTDIR)$(LIBDIR)" $(NEWUSER)

I also added a remove rule to clean up and remove mercurial-server so you can do make remove. I left the docbook rules in place because one day I might get around to fixing that for CentOS, however they don’t get called.

All that remains now is to build and install mercurial-server, do this by running:

make setup-useradd

Configuration
The first thing we need to do is generate and install the administrators SSH public and private key pairs. If you already have a pair of key files you want to use then skip the next step:

To generate a new public/private key pair then use ssh-keygen:

[root@vostro1700 ~]# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/root/.ssh/id_rsa): kevin_rsa
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in kevin_rsa.
Your public key has been saved in kevin_rsa.pub.
The key fingerprint is:
86:fc:13:23:ec:3b:41:01:fb:59:b8:75:a9:da:cb:57 root@vostro1700.zygonia.net
[root@vostro1700 ~]#

You can do this on the mercurial server machine or you can do this on your workstation. Ultimately we want to get the public key file (kevin_rsa.pub) onto the server.

Once you’ve generated a pair of key files and you’ve got the public key file onto the mercurial server we need to copy it to the mercurial-server keys/root folder and then instruct mercurial-server to “import” it to the hg user’s authorized_keys file:

sudo mkdir -p /etc/mercurial-server/keys/root/kevin
sudo cp kevin_rsa.pub /etc/mercurial-server/keys/root/kevin
sudo -u hg /usr/local/share/mercurial-server/refresh-auth

We’re now ready to create new repositories.

Connecting From Linux
If your client machine is Linux and you’re already using SSH keys to authenticate against remote servers then the following will be familiar. First we start ssh-agent, then we add our private key. This means we don’t have to re-type our password:

$ eval `ssh-agent`
Agent pid 13464

What we just did there was launch ssh-agent but evaluate the output, this sets up a couple of handy environment variables (SSH_AUTH_SOCK and SSH_AGENT_PID). If we just did ssh-agent on its own then we’d see:

SSH_AUTH_SOCK=/tmp/ssh-RnDfd13515/agent.13515; export SSH_AUTH_SOCK;
SSH_AGENT_PID=13516; export SSH_AGENT_PID;
echo Agent pid 13516;

Next we need to add our private key:

$ ssh-add kevin_rsa
Enter passphrase for kevin_rsa:
Identity added: kevin_rsa (kevin_rsa)
$

Finally lets see if we can create a new repository on the remote server:

$ hg init ssh://hg@hg.mydomain.net/helloworld

If all is well then you should see a helloworld folder appear in /var/mercurial-server/repos.

Connecting From Windows – TortoiseHg
On my Windows Workstation I have the Selenic TortoiseHg bits installed. I also have PuTTY and its related tools installed as well because I use PuTTY but I also need PuTTYgen as well.

There’s a bit of a gotcha with PuTTY and SSH key files. If you generated your keyfiles using ssh-keygen then they need to be converted to work with PuTTY and Pageant (PuTTY’s equivalent of ssh-agent).

To do this launch PuTTYgen and select “Conversions -> Import key”:

Locate your private key file (kevin_rsa) which was generated using ssh-keygen and open it. You’ll be prompted for the private key’s password, enter it and hit enter. If the key file is imported successfully then PuTTYgen will pop up a notice and tell you what to do next:

Click OK and then click on “Save private key”, I saved mine as “kevin_rsa_putty.ppk” (the PuTTY tools like to see the .ppk extension so you may as well go along with that).

As an aside I save my keys in a hidden “.ssh” folder in my %USERPROFILE% folder.

As I mentioned earlier, Pageant does the equivalent job of ssh-agent. When you launch Pageant it’ll minimise itself into the notification area:

Load up your newly converted private key into Pageant by right clicking on it’s icon and selecting “Add Key”:

You’ll prompted for your private key file’s password so enter it and hit enter. To confirm the key is loaded right click on Pageant’s notification icon and select “View Keys”. All being well we should see our key in the key list:

Next we need to tell TortoiseHg about our SSH settings. In your Windows %USERPROFILE% directory there should be a file called mercurial.ini. If there isn’t then create one. Mine looks like this:

[tortoisehg]
ui.language = en_GB
vdiff = kdiff3

[ui]
merge = kdiff3
username = kevin <kevin.kenny@mydomain.net>
verbose = True
ssh="C:\Program Files\TortoiseHg\TortoisePlink.exe" -ssh -2 -agent -v

The important line line is the one that says:

ssh="C:\Program Files\TortoiseHg\TortoisePlink.exe" -ssh -2 -agent -v

TortoisePlink.exe is a recompiled variant of PuTTY’s Plink tool. Plink is the equivalent of Unix’s ssh tool.

That line is instructing TortoiseHg to connect to our remote Mercurial server using TortoisePlink and pickup our private key along the way from Pageant.

Save mercurial.ini and then open a command line window. Next try to create a new repository on your remote Mercurial server (I have all the verbosity switches turned on just so we can see what’s happening under the bonnet):

hg init ssh://hg@hg.mydomain.net/anewrepo

This should result in output that looks similar to this:

running "C:\Program Files\TortoiseHg\TortoisePlink.exe" -ssh -2 -agent -v hg@hg.zygonia.net "hg init anewrepo"
running ""C:\Program Files\TortoiseHg\TortoisePlink.exe" -ssh -2 -agent -v hg@hg.zygonia.net "hg -R anewrepo serve --stdio""
remote: Looking up host "hg.zygonia.net"
remote: Connecting to 172.16.3.21 port 22
remote: Server version: SSH-2.0-OpenSSH_4.3
remote: We claim version: SSH-2.0-PuTTY_Local:_Feb_27_2009_19:14:38
remote: Using SSH protocol version 2
remote: Doing Diffie-Hellman group exchange
remote: Doing Diffie-Hellman key exchange with hash SHA-1
remote: Host key fingerprint is:
remote: ssh-rsa 2048 6f:23:ab:c8:4a:12:8a:d9:b0:36:09:b4:49:54:97:c0
remote: Initialised AES-256 SDCTR client->server encryption
remote: Initialised HMAC-SHA1 client->server MAC algorithm
remote: Initialised AES-256 SDCTR server->client encryption
remote: Initialised HMAC-SHA1 server->client MAC algorithm
remote: Pageant is running. Requesting keys.
remote: Pageant has 1 SSH-2 keys
remote: Using username "hg".
remote: Trying Pageant key #0
remote: Remote debug message: Pty allocation disabled.
remote: Remote debug message: Port forwarding disabled.
remote: Remote debug message: X11 forwarding disabled.
remote: Remote debug message: Agent forwarding disabled.
remote: Remote debug message: Forced command: /usr/local/share/mercurial-server/hg-ssh root/kevin/kevin_rsa.pub
remote: Authenticating with public key "imported-openssh-key" from agent
remote: Sending Pageant's response
remote: Remote debug message: Pty allocation disabled.
remote: Remote debug message: Port forwarding disabled.
remote: Remote debug message: X11 forwarding disabled.
remote: Remote debug message: Agent forwarding disabled.
remote: Remote debug message: Forced command: /usr/local/share/mercurial-server/hg-ssh root/kevin/kevin_rsa.pub
remote: Access granted
remote: Opened channel for session
remote: Started a shell/command
remote: Sent EOF message
remote: Server sent command exit status 0
remote: Disconnected: All channels closed

Logon to your repository server and look in the /var/lib/mercurial-server/repos folder, there should be a new directory in there called anewrepo.

A Gotcha: When running Pageant and TortoiseHg, make sure that if you run either as “Administrator”, both run as administrator. I had an hour or so of head scratching when running hg init ssh://hg@hg.mydomain.net/anewrepo from an command prompt opened using “Run As Administrator” whilst Pageant was running under my normal Windows account.

In the next article I’ll cover adding new users and joining up the mercurial-server repository with Apache.

Very useful resources:

Sharing Mercurial repositories with mercurial-server
Using ssh-agent with ssh
Using Pageant for authentication

This entry was posted in Mercurial, Python. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>