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.
23 All of the repositories controlled by these tools are owned by a
24 single user (the "hg" user in what follows), but many remote users can
25 act on them. We don't use file permissions to achieve that - instead,
26 developers log in as the "hg" user when they connect to the repository
27 host using ssh, using ssh URLs of the form
28 "ssh://hg@repository-host/repository-name". A restricted shell
29 prevents them from using this access for unauthorized purposes.
30 Developers are authenticated only using SSH keys; no other form of
31 authentication is supported.
38 - "sudo" root privileges
39 - an ssh-key set up with ssh-agent
41 Ensure there is no user called "hg" on the repository host, and run
42 "./install" to create them. You are now the sole user able to change
43 and create repositories on this repository host. To give access to
44 others, check out hgadmin - as yourself, and on whichever host is most
45 convenient, but using the ssh-key with which you set up the
50 hg clone ssh://hg@repository-host/hgadmin
53 You can now add other users by putting their keys in an appropriate
54 subdirectory of the "keys" directory, and control their access by
55 editing hg-ssh-access.conf. Changes will take effect as soon as you
56 push them to "ssh://hg@repository-host/hgadmin".
58 Users authorized to do so can now also create new repositories on this
61 hg clone . ssh://hg@repository-host/my-project-name
65 Each line of hg-ssh-access.conf has the following syntax:
67 <rule> <condition> <condition> ...
71 init - allow any operation, including the creation of new repositories
72 write - allow reads and writes to this file in this repository
73 read - allow the repo to be read but reject matching writes
74 deny - deny all requests
76 A condition is a globpattern matched against a relative path, one of:
78 user=<globpattern> - user's key
79 repo=<globpattern> - repo (as the user supplies it)
80 file=<globpattern> - file in the repo
81 branch=<globpattern> - name of the branch
83 The first rule in the file which has all its conditions satisfied is
84 used to determine whether an action is allowed.
86 Paths cannot contain any special characters except "/"; glob patterns
87 cannot contain any special characters except "/" and "*". "*" matches
88 zero or more characters not including "/" while "**" matches zero or
89 more characters including "/".
91 Blank lines and lines that start with "#" are ignored.
95 The rules file is used to make four decisions:
97 - Whether to allow a repository to be created
98 - Whether to allow access to a repository
99 - Whether to allow a changeset on a particular branch at all
100 - Whether to allow a changeset to change a particular file
102 When the first two of these decisions are being made, nothing is known
103 about what files might be changed, and so all file conditions
104 automatically succeed for the purpose of such decisions. This means
105 that doing tricky things with file conditions can have
106 counterintuitive consequences:
108 - You cannot limit read access to a subset of a repository with a
109 "read" rule and a file condition: any user who has access to a
110 repository can read all of it and its full history. Such a rule can
111 only have the effect of masking a later "write" rule, as in this
114 read repo=specialrepo file=dontwritethis
115 write repo=specialrepo
117 allows all users to read specialrepo, and to write to all files
118 *except* that any changeset which writes to "dontwritethis" will be
121 - For similar reasons, don't give "init" rules file conditions.
123 - Don't try to deny write access to a particular file on a particular
124 branch - a developer can write to the file on another branch and then
125 merge it in. Either deny all writes to the branch from that user, or
126 allow them to write to all the files they can write to on any branch.
127 In other words, something like this will have the intended effect
129 write user=docs/* branch=docs file=docs/*
131 But something like this will not have the intended effect; it will
132 effectively allow these users to write to any file on any branch, by
133 writing it to "docs" first:
135 write user=docs/* branch=docs
136 write user=docs/* file=docs/*
141 When a developer attempts to connect to a repository via ssh, the SSH
142 daemon searches for a match for that user's key in
143 ~hg/.ssh/authorized_keys. If the developer is authorised to connect
144 to the repository they will have an entry in this file. The entry
145 includes a "command" prefix which specifies that the restricted shell
146 should be used; this shell is passed an argument identifying the
147 developer. The shell parses the command the developer is trying to
148 execute, and consults a rules file to see if that developer is allowed
149 to perform that action on that repository. The bulk of the work of
150 the restricted shell is done by the Python program "hg-ssh", but the
151 shell script "hg-ssh-wrapper" sets up some configuration so that you
152 can change it to suit your local installation.
154 The file ~hg/.ssh/authorized_keys is generated by "refresh-auth",
155 which recurses through a directory of files containing SSH keys and
156 generates an entry in authorized_keys for each one, using the name of
157 the key file as the identifier for the developer. These keys will
158 live in the "keys" subdirectory of a repository called "hgadmin". A
159 hook in this repository re-runs "refresh-auth" on the most recent
160 version after every push.
162 Finally, a hook in an extension is run for each changeset that is
163 remotely committed, which uses the rules file to determine whether to
168 If you find yourself "locked out" - that is, that you no longer have
169 the permissions needed in hgadmin - you can break back in again if
170 you're able to become the "hg" user on the repository host. Once you
171 are that user, delete ~hg/.ssh/authorized_keys (to stop any user who
172 might have access but shouldn't from using the repository while you
173 fix things). Then go into ~hg/repos/hgadmin, do an "hg update", edit
174 things to your satisfaction, and commit the change. Finally, run
175 ~/admin/hg-admin-tools/refresh-auth to regenerate
176 ~hg/.ssh/authorized_keys.
180 Thanks for reading this far. If you use hg-admin-tools, please tell