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/.ssh/authorized_keys file using the
refresh-auth script (or via the
hgadmin administrative repository.
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== email@example.com
For a more detailed explanation of how this works see: How mercurial-server works
First off, download and unzip mercurial-server from here:
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.
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
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
#!/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:
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
[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 firstname.lastname@example.org [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
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://email@example.com/helloworld
If all is well then you should see a
helloworld folder appear in
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
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 <firstname.lastname@example.org> 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://email@example.com/anewrepo
This should result in output that looks similar to this:
running "C:\Program Files\TortoiseHg\TortoisePlink.exe" -ssh -2 -agent -v firstname.lastname@example.org "hg init anewrepo" running ""C:\Program Files\TortoiseHg\TortoisePlink.exe" -ssh -2 -agent -v email@example.com "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
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://firstname.lastname@example.org/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: