README
author Paul Crowley <paul@lshift.net>
Tue, 22 Apr 2008 13:51:19 +0100
changeset 26 2c4f499ea12f
parent 20 f4daa224dc7e
child 28 583ed103e021
permissions -rw-r--r--
Explain limitations of branch/file rule combination

hg-admin-tools

A set of tools for managing authorization and access control for
ssh-based Mercurial repositories

Paul Crowley, paul@lshift.net, 2008

This software may be used and distributed according to the terms
of the GNU General Public License, incorporated herein by reference.

WHAT IT GIVES YOU

These tools make it easier to provide a centralized repository host
with read/write access to many repositories for many developers.
Access control is managed with a special repository on the server
called "hgadmin"; pushes to this repository immediately change the
rules that are in effect.

Inside "hgadmin" is a "keys" directory containing the SSH keys of all
developers who have access, and a file "hg-ssh-access.conf" which
gives a set of rules defining who can do what to what.

HOW IT WORKS

All of the repositories controlled by these tools are owned by a
single user (the "hg" user in what follows), but many remote users can
act on them.  We don't use file permissions to achieve that - instead,
developers log in as the "hg" user when they connect to the repository
host using ssh, using ssh URLs of the form
"ssh://hg@repository-host/repository-name".  A restricted shell
prevents them from using this access for unauthorized purposes.

Developers are authenticated only using SSH keys; no other form of
authentication is supported.  When a developer attempts to connect to
a repository via ssh, the SSH daemon searches for a match for that
user's key in ~hg/.ssh/authorized_keys.  If the developer is
authorised to connect to the repository they will have an entry in
this file.  The entry includes a "command" prefix which specifies that
the restricted shell should be used; this shell is passed an argument
identifying the developer.  The shell parses the command the developer
is trying to execute, and consults a rules file to see if that
developer is allowed to perform that action on that repository.  The
bulk of the work of the restricted shell is done by the Python program
"hg-ssh", but the shell script "hg-ssh-wrapper" sets up some
configuration so that you can change it to suit your local
installation.

The file ~hg/.ssh/authorized_keys is generated by "refresh-auth",
which recurses through a directory of files containing SSH keys and
generates an entry in authorized_keys for each one, using the name of
the key file as the identifier for the developer.  These keys will
live in the "keys" subdirectory of a repository called "hgadmin".  A
hook in this repository re-runs "refresh-auth" on the most recent
version after every push.

Finally, a hook in an extension is run for each changeset that is
remotely committed, which uses the rules file to determine whether to
allow the changeset.

GETTING STARTED

This is only one setup - it can be tweaked in many ways, and is as
specific as it is only in the interests of brevity.

You, and all users of this repository host, will need SSH public key
authentication set up, preferably working with ssh-agent so you don't
have to type in your passphrase all the time.  I assume you've done
that in what follows, so if you've done something different you'll
need to change it appropriately.

Issue these commands to get the repository host started.  These are
written out here rather than encapsulated in a script because many of
them may need to be different for your local setup.  You will need
root access on the repository host, because you need to create a new
user.

   ssh -A repository-host
   ssh-add -L >> /tmp/my-ssh-public-key
   sudo adduser --system --shell /bin/sh --group --disabled-password \
     --gecos "Mercurial repositories" hg
   sudo -u hg -H -s
   cd
   mkdir -p admin repos/hgadmin/keys/admin .ssh
   cd admin
   hg clone http://hg.opensource.lshift.net/hg-admin-tools
   cp hg-admin-tools/hg-ssh-wrapper hg-admin-tools/remote-hgrc ~
   cd ../repos/hgadmin
   hg init .
   echo "init user=admin/*" > hg-ssh-access.conf
   cp /tmp/my-ssh-public-key keys/admin/myname
   hg add
   hg commit -m "initial commit"
   cp ~/admin/hg-admin-tools/hgadmin-hgrc .hg/hgrc
   ../../admin/hg-admin-tools/refresh-auth ./hg-ssh-wrapper
   exit
   exit

You are now the sole user able to change and create repositories on
this repository host.  To administer these controls (and test your
access), check out hgadmin:

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

You can now add other users by putting their keys in an appropriate
subdirectory of the "keys" directory, and control their access by
editing hg-ssh-access.conf.  Changes will take effect as soon as you
push them to "ssh://hg@repository-host/hgadmin".

Users authorized to do so can now also create new repositories on this
host with "clone":

  hg clone . ssh://hg@repository-host/my-project-name

HG-SSH-ACCESS.CONF

Each line of hg-ssh-access.conf has the following syntax:

<rule> <condition> <condition> ...

Rule is one of

init - allow any operation, including the creation of new repositories
write - allow reads and writes to this file in this repository
read - allow the repo to be read but reject matching writes
deny - deny all requests

A condition is a globpattern matched against a relative path, one of:

user=<globpattern> - user's key
repo=<globpattern> - repo (as the user supplies it)
file=<globpattern> - file in the repo
branch=<globpattern> - name of the branch

The first rule in the file which has all its conditions satisfied is
used to determine whether an action is allowed.

Paths cannot contain any special characters except "/"; glob patterns
cannot contain any special characters except "/" and "*".  "*" matches
zero or more characters not including "/" while "**" matches zero or
more characters including "/".

Blank lines and lines that start with "#" are ignored.

FILE CONDITIONS

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 file

When the first two of these decisions are being made, nothing is known
about what files might be changed, and so all file conditions
automatically succeed for the purpose of such decisions.  This means
that doing tricky things with file conditions can have
counterintuitive consequences:

- You cannot limit read access to a subset of a repository with a
"read" rule and a file condition: any user who has access to a
repository can read all of it and its full history.  Such a rule can
only have the effect of masking a later "write" rule, as in this
example:

   read repo=specialrepo file=dontwritethis
   write repo=specialrepo

allows all users to read specialrepo, and to write to all files
*except* that any 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 to all the files they can write to on any branch.
In other words, something like this will have the intended effect

  write user=docs/* branch=docs file=docs/*

But something like this will not have the intended effect; it will
effectively allow 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/*

LOCKING YOURSELF OUT

If you find yourself "locked out" - that is, that you no longer have
the permissions needed in hgadmin - you can break back in again if
you're able to become the "hg" user on the repository host.  Once you
are that user, delete ~hg/.ssh/authorized_keys (to stop any user who
might have access but shouldn't from using the repository while you
fix things).  Then go into ~hg/repos/hgadmin, do an "hg update", edit
things to your satisfaction, and commit the change.  Finally, run
~/admin/hg-admin-tools/refresh-auth to regenerate
~hg/.ssh/authorized_keys. 

THANKS

Thanks for reading this far.  If you use hg-admin-tools, please tell
me about it.

Paul Crowley, 2008