3 A set of tools for managing authorization and access control for
4 ssh-based Mercurial repositories
6 Paul Crowley, paul@lshift.net, 2008
8 This software may be used and distributed according to the terms
9 of the GNU General Public License, incorporated herein by reference.
13 These tools make it easier to provide a centralized repository host
14 with read/write access to many repositories for many developers.
15 Access control is managed with a special repository on the server
16 called "hgadmin"; pushes to this repository immediately change the
17 rules that are in effect.
19 Inside "hgadmin" is a "keys" directory containing the SSH keys of all
20 developers who have access, and a file "hg-ssh-access.conf" which
21 gives a set of rules defining who can do what to what.
25 All of the repositories controlled by these tools are owned by a
26 single user (the "hg" user in what follows), but many remote users can
27 act on them. We don't use file permissions to achieve that - instead,
28 developers log in as the "hg" user when they connect to the repository
29 host using ssh, using ssh URLs of the form
30 "ssh://hg@repository-host/repository-name". A restricted shell
31 prevents them from using this access for unauthorized purposes.
33 Developers are authenticated only using SSH keys; no other form of
34 authentication is supported. When a developer attempts to connect to
35 a repository via ssh, the SSH daemon searches for a match for that
36 user's key in ~hg/.ssh/authorized_keys. If the developer is
37 authorised to connect to the repository they will have an entry in
38 this file. The entry includes a "command" prefix which specifies that
39 the restricted shell should be used; this shell is passed an argument
40 identifying the developer. The shell parses the command the developer
41 is trying to execute, and consults a rules file to see if that
42 developer is allowed to perform that action on that repository. The
43 bulk of the work of the restricted shell is done by the Python program
44 "hg-ssh", but the shell script "hg-ssh-wrapper" sets up some
45 configuration so that you can change it to suit your local
48 The file ~hg/.ssh/authorized_keys is generated by "refresh-auth",
49 which recurses through a directory of files containing SSH keys and
50 generates an entry in authorized_keys for each one, using the name of
51 the key file as the identifier for the developer. These keys will
52 live in the "keys" subdirectory of a repository called "hgadmin". A
53 hook in this repository re-runs "refresh-auth" on the most recent
54 version after every push.
56 Finally, a hook in an extension is run for each changeset that is
57 remotely committed, which uses the rules file to determine whether to
62 This is only one setup - it can be tweaked in many ways, and is as
63 specific as it is only in the interests of brevity.
65 You, and all users of this repository host, will need SSH public key
66 authentication set up, preferably working with ssh-agent so you don't
67 have to type in your passphrase all the time. I assume you've done
68 that in what follows, so if you've done something different you'll
69 need to change it appropriately.
71 Issue these commands to get the repository host started. These are
72 written out here rather than encapsulated in a script because many of
73 them may need to be different for your local setup. You will need
74 root access on the repository host, because you need to create a new
77 ssh -A repository-host
78 ssh-add -L >> /tmp/my-ssh-public-key
79 sudo adduser --system --shell /bin/sh --group --disabled-password \
80 --gecos "Mercurial repositories" hg
83 mkdir -p admin repos/hgadmin/keys/admin .ssh
85 hg clone http://hg.opensource.lshift.net/hg-admin-tools
86 cp hg-admin-tools/hg-ssh-wrapper hg-admin-tools/remote-hgrc ~
89 echo "init user=admin/*" > hg-ssh-access.conf
90 cp /tmp/my-ssh-public-key keys/admin/myname
92 hg commit -m "initial commit"
93 cp ~/admin/hg-admin-tools/hgadmin-hgrc .hg/hgrc
94 ../../admin/hg-admin-tools/refresh-auth ./hg-ssh-wrapper
98 You are now the sole user able to change and create repositories on
99 this repository host. To administer these controls (and test your
100 access), check out hgadmin:
104 hg clone ssh://hg@repository-host/hgadmin
107 You can now add other users by putting their keys in an appropriate
108 subdirectory of the "keys" directory, and control their access by
109 editing hg-ssh-access.conf. Changes will take effect as soon as you
110 push them to "ssh://hg@repository-host/hgadmin".
112 Users authorized to do so can now also create new repositories on this
115 hg clone . ssh://hg@repository-host/my-project-name
119 Each line of hg-ssh-access.conf has the following syntax:
121 <rule> <condition> <condition> ...
125 init - allow any operation, including the creation of new repositories
126 write - allow reads and writes to this file in this repository
127 read - allow the repo to be read but reject matching writes
128 deny - deny all requests
130 A condition is a globpattern matched against a relative path, one of:
132 user=<globpattern> - user's key
133 repo=<globpattern> - repo (as the user supplies it)
134 file=<globpattern> - file in the repo
135 branch=<globpattern> - name of the branch
137 The first rule in the file which has all its conditions satisfied is
138 used to determine whether an action is allowed.
140 Paths cannot contain any special characters except "/"; glob patterns
141 cannot contain any special characters except "/" and "*". "*" matches
142 zero or more characters not including "/" while "**" matches zero or
143 more characters including "/".
145 Blank lines and lines that start with "#" are ignored.
149 The rules file is used to make four decisions:
151 - Whether to allow a repository to be created
152 - Whether to allow access to a repository
153 - Whether to allow a changeset on a particular branch at all
154 - Whether to allow a changeset to change a particular file
156 When the first two of these decisions are being made, nothing is known
157 about what files might be changed, and so all file conditions
158 automatically succeed for the purpose of such decisions. This means
159 that doing tricky things with file conditions can have
160 counterintuitive consequences:
162 - You cannot limit read access to a subset of a repository with a
163 "read" rule and a file condition: any user who has access to a
164 repository can read all of it and its full history. Such a rule can
165 only have the effect of masking a later "write" rule, as in this
168 read repo=specialrepo file=dontwritethis
169 write repo=specialrepo
171 allows all users to read specialrepo, and to write to all files
172 *except* that any changeset which writes to "dontwritethis" will be
175 - For similar reasons, don't give "init" rules file conditions.
177 - Don't try to deny write access to a particular file on a particular
178 branch - a developer can write to the file on another branch and then
179 merge it in. Either deny all writes to the branch from that user, or
180 allow them to write to all the files they can write to on any branch.
181 In other words, something like this will have the intended effect
183 write user=docs/* branch=docs file=docs/*
185 But something like this will not have the intended effect; it will
186 effectively allow these users to write to any file on any branch, by
187 writing it to "docs" first:
189 write user=docs/* branch=docs
190 write user=docs/* file=docs/*
195 If you find yourself "locked out" - that is, that you no longer have
196 the permissions needed in hgadmin - you can break back in again if
197 you're able to become the "hg" user on the repository host. Once you
198 are that user, delete ~hg/.ssh/authorized_keys (to stop any user who
199 might have access but shouldn't from using the repository while you
200 fix things). Then go into ~hg/repos/hgadmin, do an "hg update", edit
201 things to your satisfaction, and commit the change. Finally, run
202 ~/admin/hg-admin-tools/refresh-auth to regenerate
203 ~hg/.ssh/authorized_keys.
207 Thanks for reading this far. If you use hg-admin-tools, please tell