mercurial-servermercurial-server makes a group of repositories available to the developersyou choose, identified by ssh keys, with easy key and access managementbased on hg.http://hg.opensource.lshift.net/mercurial-server/Copyright (C) 2008-2009 LShift Ltd. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.SUMMARYmercurial-server makes a group of repositories available to the developersyou choose, identified by ssh keys, with easy key and access managementbased on hg.All of the repositories controlled by mercurial-server are owned by asingle user (the "hg" user in what follows), but many remote users can acton them, and different users can have different permissions. We don't usefile permissions to achieve that - instead, developers log in as the "hg"user when they connect to the repository host using ssh, using ssh URLs ofthe form "ssh://hg@repository-host/repository-name". A restricted shellprevents them from using this access for unauthorized purposes. Developersare authenticated only using SSH keys; no other form of authentication issupported. To give a user access to the repository, place their key in anappropriately-named subdirectory of "/etc/mercurial-server/keys" and run"/usr/local/lib/mercurial-server/refresh-auth". You can then control whataccess they have to what repositories by editing the control file"/etc/mercurial-server/access.conf", which can match the names of these keysagainst a glob pattern. For convenient remote control of access, you can instead (if you have theprivileges) make changes to a special repository called "hgadmin", whichcontains its own "access.conf" file and "keys" directory. Changes pushed tothis repository take effect immediately. The two "access.conf" files areconcatenated, and the keys directories merged.QUICK STARTYou and all developers using this system will need an SSH public key, andwill almost certainly want to be running ssh-agent (or its equivalent, egPageant under Windows). If you're not familiar with ssh-agent, you shouldlearn about that before using this.In what follows, certain operations (eg installing mercurial-server itself)have to be done on the repository server (which we call "repository-host"),but any operation that involves checking in or out of Mercurial can be donewherever is most convenient to you; the most usual arrangment would be thatyou'd do these things at the machine you sit at, and on which you runssh-agent, which is what authenticates you when you talk to the repositoryserver.Ensure there is no user called "hg" on the repository host, and run"./install". This installs the mercurial-server files and control files, andcreates and sets up the "hg" user.Place your SSH public key in the directory "/etc/mercurial-server/keys/root".I suggest creating yourself a directory and naming the key after your hostname(ie the file is called something like"/etc/mercurial-server/keys/root/yourname/yourhostname") so that you caneasily manage users who have a different key on each host they use. Then run"/usr/local/lib/mercurial-server/refresh-auth".The repository is now ready to use, and you are now the sole user able tochange and create repositories on this repository host. CREATING REPOSITORIESTo create a new repository, you clone a local repository onto the remoteserver. So if you want a new empty repository called "myproject", you can do(as yourself): hg init myproject hg clone myproject ssh://hg@repository-host/myprojectADDING OTHER USERSBecause your key is in the "keys/root" subdirectory, you have the equivalentof "root privileges" over mercurial-server (not the whole computer, justmercurial-server). You can add other root users by putting their keys next toyours, or you can make less privileged users by putting their keys in the"keys/users" subdirectory - these users will be able to read and write to anyrepository (except one - see below) but will not be able to create newrepositories. As always, when you change "/etc/mercurial-server/keys" you needto re-run "/usr/local/lib/mercurial-server/refresh-auth".LOGGINGEvery push and pull is logged with the key used: see the file .hg/serve-log ineach repository.USING HGADMINIt can be inconvenient to log on to the repository server, become root, copykeys around, and run "refresh-auth" every time you want to change userprivileges. This is where mercurial-server shines :-) Suppose you have anotheruser's SSH public key in the file "/tmp/theirkey" (on the machine you sit at,not necessarily the repository server) and you want to give them user-levelaccess to the repository server. Run these commands: hg clone ssh://hg@repository-server/hgadmin cd hgadmin mkdir keys/user/thatuser cp /tmp/theirkey keys/user/thatuser/theirhostname hg add hg commit -m "Added key for thatuser" hg pushIn other words, hgadmin is a version controlled version of"/etc/mercurial-server/keys", and changes to it take effect immediately. Only"keys/root" users can act on "hgadmin" - those with keys in "keys/users" arelocked out. Multiple admins can use Mercurial's version control to cooperateon controlling access to the repository server in a natural way. You can alsoadd "root" users by putting their key in the "keys/root" directory in just thesame way - these users will now be able to control hgadmin and create newrepositories just as you can.Once you're working with "hgadmin", it can be convenient to remove all thekeys in "/etc/mercurial-server/keys" and all the entries in"/etc/mercurial-server/access.conf" and use hgadmin to control everything. Ifyou find yourself locked out, you can get back in again by restoring some ofthe entries you removed from these files - remember,"/etc/mercurial-server/access.conf" takes precedence over the "access.conf" in"hgadmin".ACCESS.CONFOut of the box, there are just two kinds of users: the ones with keys in"keys/root" and those in "keys/users". However, you can change this by editing"access.conf". There are two "access.conf" files, one in"/etc/mercurial-server" and one in "hgadmin"; the two are simply concatenatedbefore being read.Each line of access.conf has the following syntax:<rule> <condition> <condition> ...Rule is one ofinit - allow any operation, including the creation of new repositorieswrite - allow reads and writes to this file in this repositoryread - allow the repo to be read but reject matching writesdeny - deny all requestsA condition is a globpattern matched against a relative path, one of:user=<globpattern> - user's keyrepo=<globpattern> - repo (as the user supplies it)file=<globpattern> - file in the repobranch=<globpattern> - name of the branchThe first rule in the file which has all its conditions satisfied is used todetermine whether an action is allowed.Paths cannot contain any special characters except "/"; glob patterns cannotcontain any special characters except "/" and "*". "*" matches zero or morecharacters not including "/" while "**" matches zero or more charactersincluding "/".Blank lines and lines that start with "#" are ignored.FILE CONDITIONSmercurial-server supports file and branch conditions, which restrict anoperation depending on what files it modifies and what branch the work is on.However, the way these conditions work is subtle and can be counterintuitive -if you want to keep things simple, stick to user and repo conditions, and thenthings are likely to work the way you would expect.The rules file is used to make four decisions:- Whether to allow a repository to be created- Whether to allow access to a repository- Whether to allow a changeset on a particular branch at all- Whether to allow a changeset to change a particular fileWhen the first two of these decisions are being made, nothing is known aboutwhat files might be changed, and so all file conditions automatically succeedfor the purpose of such decisions. This means that doing tricky things withfile conditions can have counterintuitive consequences:- You cannot limit read access to a subset of a repository with a "read" ruleand a file condition: any user who has access to a repository can read all ofit and its full history. Such a rule can only have the effect of masking alater "write" rule, as in this example: read repo=specialrepo file=dontwritethis write repo=specialrepoallows all users to read specialrepo, and to write to all files *except* thatany changeset which writes to "dontwritethis" will be rejected.- For similar reasons, don't give "init" rules file conditions.- Don't try to deny write access to a particular file on a particular branch -a developer can write to the file on another branch and then merge it in.Either deny all writes to the branch from that user, or allow them to write toall the files they can write to on any branch. In other words, something likethis will have the intended effect: write user=docs/* branch=docs file=docs/*But something like this will not have the intended effect; it will effectivelyallow these users to write to any file on any branch, by writing it to "docs"first: write user=docs/* branch=docs write user=docs/* file=docs/* read user=docs/*HOW IT WORKSWhen a developer attempts to connect to a repository via ssh, the SSH daemonsearches for a match for that user's key in ~hg/.ssh/authorized_keys. If thedeveloper is authorised to connect to the repository they will have an entryin this file. The entry includes a "command" prefix which specifies that therestricted shell "/usr/local/lib/mercurial-server/hg-ssh" should be used; thisshell is passed an argument identifying the developer. The shell parses thecommand the developer is trying to execute, and consults a rules file to seeif that developer is allowed to perform that action on that repository.The file ~hg/.ssh/authorized_keys is generated by "refresh-auth", whichrecurses through two directories of files containing SSH keys and generates anentry in authorized_keys for each one, using the name of the key file as theidentifier for the developer. These keys will live in the "keys" subdirectory"/etc/mercurial-server" and the "keys" subdirectory of a repository called"hgadmin". A hook in this repository re-runs "refresh-auth" on the most recentversion after every push.Finally, hook in an extension is run for each changeset that is remotelycommitted, which uses the rules file to determine whether to allow thechangeset.SECURITY OF MERCURIAL-SERVERmercurial-server relies entirely on sshd to grant access to remote users. As aresult, it runs no daemons, installs no setuid programs, and no part of itruns as root except the install process: all programs run as the user hg. Andany attack on mercurial-server can only be started if the Bad Guys alreadyhave a public key in ~hg/.ssh/authorized_keys, otherwise sshd will bar theway. No matter what command the user tries to run on the remote system viassh, mercurial-server is run. It parses the command line the user asked for, and interprets and runs thecorresponding hg operation itself if access is allowed, so users can only readand add to history within repositories; they cannot run any other hg command.In addition, every push and pull is logged with a datestamp, changeset ID andthe key that performed the operation.However, while the first paragraph holds no matter what bugs mercurial-servercontains, the second depends on the relevant code being correct; though theentire codebase is currently only about twice as long as this README,mercurial-server is a fairly new program and may harbour bugs. Backups areessential!THANKSThanks for reading this far. If you use mercurial-server, please tell me aboutit.Paul Crowley, 2009