1 mercurial-server |
1 mercurial-server |
2 |
2 |
3 A set of tools for managing authorization and access control for |
3 mercurial-server makes a group of repositories available to the developers |
4 ssh-based Mercurial repositories |
4 you choose, identified by ssh keys, with easy key and access management |
|
5 based on hg. |
5 |
6 |
6 Paul Crowley, paul@lshift.net, 2008-2009 |
7 Paul Crowley, paul@lshift.net, 2008-2009 |
7 |
8 |
8 This software may be used and distributed according to the terms |
9 This software may be used and distributed according to the terms |
9 of the GNU General Public License, incorporated herein by reference. |
10 of the GNU General Public License, incorporated herein by reference. |
10 |
11 |
11 http://hg.opensource.lshift.net/mercurial-server/ |
12 http://hg.opensource.lshift.net/mercurial-server/ |
12 |
13 |
13 WHAT IT GIVES YOU |
14 All of the repositories controlled by mercurial-server are owned by a |
14 |
15 single user (the "hg" user in what follows), but many remote users can act |
15 These tools make it easier to provide a centralized repository host |
16 on them, and different users can have different permissions. We don't use |
16 with read/write access to many repositories for many developers. |
17 file permissions to achieve that - instead, developers log in as the "hg" |
17 |
18 user when they connect to the repository host using ssh, using ssh URLs of |
18 All of the repositories controlled by these tools are owned by a single user |
19 the form "ssh://hg@repository-host/repository-name". A restricted shell |
19 (the "hg" user in what follows), but many remote users can act on them, and |
20 prevents them from using this access for unauthorized purposes. Developers |
20 different users can have different permissions. We don't use file permissions to |
21 are authenticated only using SSH keys; no other form of authentication is |
21 achieve that - instead, developers log in as the "hg" user when they connect to |
22 supported. |
22 the repository host using ssh, using ssh URLs of the form |
|
23 "ssh://hg@repository-host/repository-name". A restricted shell prevents them |
|
24 from using this access for unauthorized purposes. Developers are authenticated |
|
25 only using SSH keys; no other form of authentication is supported. |
|
26 |
23 |
27 To give a user access to the repository, place their key in an |
24 To give a user access to the repository, place their key in an |
28 appropriately-named subdirectory of "/etc/mercurial-server/keys" and run |
25 appropriately-named subdirectory of "/etc/mercurial-server/keys" and run |
29 "/etc/mercurial-server/refresh-auth". You can then control what access they have |
26 "/etc/mercurial-server/refresh-auth". You can then control what access they |
30 to what repositories by editing the control file |
27 have to what repositories by editing the control file |
31 "/etc/mercurial-server/access.conf", which can match the names of these keys |
28 "/etc/mercurial-server/access.conf", which can match the names of these |
32 against a glob pattern. |
29 keys against a glob pattern. |
33 |
30 |
34 For convenient remote control of access, you can instead (if you have the |
31 For convenient remote control of access, you can instead (if you have the |
35 privileges) make changes to a special repository called "hgadmin", which |
32 privileges) make changes to a special repository called "hgadmin", which |
36 contains its own "access.conf" file and "keys" directory. Changes pushed to this |
33 contains its own "access.conf" file and "keys" directory. Changes pushed to |
37 repository take effect immediately. The two "access.conf" files are |
34 this repository take effect immediately. The two "access.conf" files are |
38 concatenated, and the keys directories merged. |
35 concatenated, and the keys directories merged. |
39 |
36 |
40 QUICK START |
37 QUICK START |
41 |
38 |
42 You and all developers using this system will need an SSH public key, and will |
39 You and all developers using this system will need an SSH public key, and |
43 almost certainly want to be running ssh-agent (or its equivalent, eg Pageant |
40 will almost certainly want to be running ssh-agent (or its equivalent, eg |
44 under Windows). If you're not familiar with ssh-agent, you should learn about |
41 Pageant under Windows). If you're not familiar with ssh-agent, you should |
45 that before using this. |
42 learn about that before using this. |
46 |
43 |
47 In what follows, certain operations (eg installing mercurial-server itself) have |
44 In what follows, certain operations (eg installing mercurial-server itself) |
48 to be done on the repository server (which we call "repository-host"), but any |
45 have to be done on the repository server (which we call "repository-host"), |
49 operation that involves checking in or out of Mercurial can be done wherever is |
46 but any operation that involves checking in or out of Mercurial can be done |
50 most convenient to you; the most usual arrangment would be that you'd do these |
47 wherever is most convenient to you; the most usual arrangment would be that |
51 things at the machine you sit at, and on which you run ssh-agent, which is what |
48 you'd do these things at the machine you sit at, and on which you run |
52 authenticates you when you talk to the repository server. |
49 ssh-agent, which is what authenticates you when you talk to the repository |
53 |
50 server. |
54 Ensure there is no user called "hg" on the repository host, and run "./install". |
51 |
55 This installs the mercurial-server files and control files, and creates and sets |
52 Ensure there is no user called "hg" on the repository host, and run |
56 up the "hg" user. |
53 "./install". This installs the mercurial-server files and control files, and |
57 |
54 creates and sets up the "hg" user. |
58 Place your SSH public key in the directory "/etc/mercurial-server/keys/root". I |
55 |
59 suggest creating yourself a directory and naming the key after your hostname (ie |
56 Place your SSH public key in the directory "/etc/mercurial-server/keys/root". |
60 the file is called something like |
57 I suggest creating yourself a directory and naming the key after your hostname |
61 "/etc/mercurial-server/keys/root/yourname/yourhostname") so that you can easily |
58 (ie the file is called something like |
62 manage users who have a different key on each host they use. Then run |
59 "/etc/mercurial-server/keys/root/yourname/yourhostname") so that you can |
|
60 easily manage users who have a different key on each host they use. Then run |
63 "/etc/mercurial-server/refresh-auth". |
61 "/etc/mercurial-server/refresh-auth". |
64 |
62 |
65 The repository is now ready to use, and you are now the sole user able to change |
63 The repository is now ready to use, and you are now the sole user able to |
66 and create repositories on this repository host. |
64 change and create repositories on this repository host. |
67 |
65 |
68 CREATING REPOSITORIES |
66 CREATING REPOSITORIES |
69 |
67 |
70 To create a new repository, you clone a local repository onto the remote server. |
68 To create a new repository, you clone a local repository onto the remote |
71 So if you want a new empty repository called "myproject", you can do (as |
69 server. So if you want a new empty repository called "myproject", you can do |
72 yourself): |
70 (as yourself): |
73 |
71 |
74 hg init myproject |
72 hg init myproject |
75 hg clone myproject ssh://hg@repository-host/myproject |
73 hg clone myproject ssh://hg@repository-host/myproject |
76 |
74 |
77 ADDING OTHER USERS |
75 ADDING OTHER USERS |
78 |
76 |
79 Because your key is in the "keys/root" subdirectory, you have the equivalent of |
77 Because your key is in the "keys/root" subdirectory, you have the equivalent |
80 "root privileges" over mercurial-server (not the whole computer, just |
78 of "root privileges" over mercurial-server (not the whole computer, just |
81 mercurial-server). You can add other root users by putting their keys next to |
79 mercurial-server). You can add other root users by putting their keys next to |
82 yours, or you can make less privileged users by putting their keys in the |
80 yours, or you can make less privileged users by putting their keys in the |
83 "keys/users" subdirectory - these users will be able to read and write to any |
81 "keys/users" subdirectory - these users will be able to read and write to any |
84 repository (except one - see below) but will not be able to create new |
82 repository (except one - see below) but will not be able to create new |
85 repositories. As always, when you change "/etc/mercurial-server/keys" you need |
83 repositories. As always, when you change "/etc/mercurial-server/keys" you need |
108 hg push |
106 hg push |
109 |
107 |
110 In other words, hgadmin is a version controlled version of |
108 In other words, hgadmin is a version controlled version of |
111 "/etc/mercurial-server/keys", and changes to it take effect immediately. Only |
109 "/etc/mercurial-server/keys", and changes to it take effect immediately. Only |
112 "keys/root" users can act on "hgadmin" - those with keys in "keys/users" are |
110 "keys/root" users can act on "hgadmin" - those with keys in "keys/users" are |
113 locked out. Multiple admins can use Mercurial's version control to cooperate on |
111 locked out. Multiple admins can use Mercurial's version control to cooperate |
114 controlling access to the repository server in a natural way. You can also add |
112 on controlling access to the repository server in a natural way. You can also |
115 "root" users by putting their key in the "keys/root" directory in just the same |
113 add "root" users by putting their key in the "keys/root" directory in just the |
116 way - these users will now be able to control hgadmin and create new |
114 same way - these users will now be able to control hgadmin and create new |
117 repositories just as you can. |
115 repositories just as you can. |
118 |
116 |
119 Once you're working with "hgadmin", it can be convenient to remove all the keys |
117 Once you're working with "hgadmin", it can be convenient to remove all the |
120 in "/etc/mercurial-server/keys" and all the entries in |
118 keys in "/etc/mercurial-server/keys" and all the entries in |
121 "/etc/mercurial-server/access.conf" and use hgadmin to control everything. If |
119 "/etc/mercurial-server/access.conf" and use hgadmin to control everything. If |
122 you find yourself locked out, you can get back in again by restoring some of the |
120 you find yourself locked out, you can get back in again by restoring some of |
123 entries you removed from these files - remember, |
121 the entries you removed from these files - remember, |
124 "/etc/mercurial-server/access.conf" takes precedence over the "access.conf" in |
122 "/etc/mercurial-server/access.conf" takes precedence over the "access.conf" in |
125 "hgadmin". |
123 "hgadmin". |
126 |
124 |
127 ACCESS.CONF |
125 ACCESS.CONF |
128 |
126 |
129 Out of the box, there are just two kinds of users: the ones with keys in |
127 Out of the box, there are just two kinds of users: the ones with keys in |
130 "keys/root" and those in "keys/users". However, you can change this by editing |
128 "keys/root" and those in "keys/users". However, you can change this by editing |
131 "access.conf". There are two "access.conf" files, one in "/etc/mercurial-server" |
129 "access.conf". There are two "access.conf" files, one in |
132 and one in "hgadmin"; the two are simply concatenated before being read. |
130 "/etc/mercurial-server" and one in "hgadmin"; the two are simply concatenated |
|
131 before being read. |
133 |
132 |
134 Each line of access.conf has the following syntax: |
133 Each line of access.conf has the following syntax: |
135 |
134 |
136 <rule> <condition> <condition> ... |
135 <rule> <condition> <condition> ... |
137 |
136 |
174 - Whether to allow a changeset on a particular branch at all |
173 - Whether to allow a changeset on a particular branch at all |
175 - Whether to allow a changeset to change a particular file |
174 - Whether to allow a changeset to change a particular file |
176 |
175 |
177 When the first two of these decisions are being made, nothing is known about |
176 When the first two of these decisions are being made, nothing is known about |
178 what files might be changed, and so all file conditions automatically succeed |
177 what files might be changed, and so all file conditions automatically succeed |
179 for the purpose of such decisions. This means that doing tricky things with file |
178 for the purpose of such decisions. This means that doing tricky things with |
180 conditions can have counterintuitive consequences: |
179 file conditions can have counterintuitive consequences: |
181 |
180 |
182 - You cannot limit read access to a subset of a repository with a "read" rule |
181 - You cannot limit read access to a subset of a repository with a "read" rule |
183 and a file condition: any user who has access to a repository can read all of it |
182 and a file condition: any user who has access to a repository can read all of |
184 and its full history. Such a rule can only have the effect of masking a later |
183 it and its full history. Such a rule can only have the effect of masking a |
185 "write" rule, as in this example: |
184 later "write" rule, as in this example: |
186 |
185 |
187 read repo=specialrepo file=dontwritethis |
186 read repo=specialrepo file=dontwritethis |
188 write repo=specialrepo |
187 write repo=specialrepo |
189 |
188 |
190 allows all users to read specialrepo, and to write to all files *except* that |
189 allows all users to read specialrepo, and to write to all files *except* that |
191 any changeset which writes to "dontwritethis" will be rejected. |
190 any changeset which writes to "dontwritethis" will be rejected. |
192 |
191 |
193 - For similar reasons, don't give "init" rules file conditions. |
192 - For similar reasons, don't give "init" rules file conditions. |
194 |
193 |
195 - Don't try to deny write access to a particular file on a particular branch - a |
194 - Don't try to deny write access to a particular file on a particular branch - |
196 developer can write to the file on another branch and then merge it in. Either |
195 a developer can write to the file on another branch and then merge it in. |
197 deny all writes to the branch from that user, or allow them to write to all the |
196 Either deny all writes to the branch from that user, or allow them to write to |
198 files they can write to on any branch. In other words, something like this will |
197 all the files they can write to on any branch. In other words, something like |
199 have the intended effect: |
198 this will have the intended effect: |
200 |
199 |
201 write user=docs/* branch=docs file=docs/* |
200 write user=docs/* branch=docs file=docs/* |
202 |
201 |
203 But something like this will not have the intended effect; it will effectively |
202 But something like this will not have the intended effect; it will effectively |
204 allow these users to write to any file on any branch, by writing it to "docs" |
203 allow these users to write to any file on any branch, by writing it to "docs" |
210 |
209 |
211 HOW IT WORKS |
210 HOW IT WORKS |
212 |
211 |
213 When a developer attempts to connect to a repository via ssh, the SSH daemon |
212 When a developer attempts to connect to a repository via ssh, the SSH daemon |
214 searches for a match for that user's key in ~hg/.ssh/authorized_keys. If the |
213 searches for a match for that user's key in ~hg/.ssh/authorized_keys. If the |
215 developer is authorised to connect to the repository they will have an entry in |
214 developer is authorised to connect to the repository they will have an entry |
216 this file. The entry includes a "command" prefix which specifies that the |
215 in this file. The entry includes a "command" prefix which specifies that the |
217 restricted shell should be used; this shell is passed an argument identifying |
216 restricted shell should be used; this shell is passed an argument identifying |
218 the developer. The shell parses the command the developer is trying to execute, |
217 the developer. The shell parses the command the developer is trying to |
219 and consults a rules file to see if that developer is allowed to perform that |
218 execute, and consults a rules file to see if that developer is allowed to |
220 action on that repository. The bulk of the work of the restricted shell is done |
219 perform that action on that repository. The bulk of the work of the restricted |
221 by the Python program "hg-ssh", but the shell script "hg-ssh-wrapper" sets up |
220 shell is done by the Python program "hg-ssh", but the shell script |
222 some configuration so that you can change it to suit your local installation. |
221 "hg-ssh-wrapper" sets up some configuration so that you can change it to suit |
223 |
222 your local installation. |
224 The file ~hg/.ssh/authorized_keys is generated by "refresh-auth", which recurses |
223 |
225 through two directories of files containing SSH keys and generates an entry in |
224 The file ~hg/.ssh/authorized_keys is generated by "refresh-auth", which |
226 authorized_keys for each one, using the name of the key file as the identifier |
225 recurses through two directories of files containing SSH keys and generates an |
227 for the developer. These keys will live in the "keys" subdirectory |
226 entry in authorized_keys for each one, using the name of the key file as the |
|
227 identifier for the developer. These keys will live in the "keys" subdirectory |
228 "/etc/mercurial-server" and the "keys" subdirectory of a repository called |
228 "/etc/mercurial-server" and the "keys" subdirectory of a repository called |
229 "hgadmin". A hook in this repository re-runs "refresh-auth" on the most recent |
229 "hgadmin". A hook in this repository re-runs "refresh-auth" on the most recent |
230 version after every push. |
230 version after every push. |
231 |
231 |
232 Finally, hook in an extension is run for each changeset that is remotely |
232 Finally, hook in an extension is run for each changeset that is remotely |
234 changeset. |
234 changeset. |
235 |
235 |
236 SECURITY OF MERCURIAL-SERVER |
236 SECURITY OF MERCURIAL-SERVER |
237 |
237 |
238 mercurial-server relies entirely on sshd to grant access to remote users. As a |
238 mercurial-server relies entirely on sshd to grant access to remote users. As a |
239 result, it runs no daemons, installs no setuid programs, and no part of it runs |
239 result, it runs no daemons, installs no setuid programs, and no part of it |
240 as root except the install process: all programs run as the user hg. And any |
240 runs as root except the install process: all programs run as the user hg. And |
241 attack on mercurial-server can only be started if the Bad Guys already have a |
241 any attack on mercurial-server can only be started if the Bad Guys already |
242 public key in ~hg/.ssh/authorized_keys, otherwise sshd will bar the way. No |
242 have a public key in ~hg/.ssh/authorized_keys, otherwise sshd will bar the |
243 matter what command the user tries to run on the remote system via ssh, |
243 way. No matter what command the user tries to run on the remote system via |
244 mercurial-server is run. |
244 ssh, mercurial-server is run. |
245 |
245 |
246 It parses the command line the user asked for, and interprets and runs the |
246 It parses the command line the user asked for, and interprets and runs the |
247 corresponding hg operation itself if access is allowed, so users can only read |
247 corresponding hg operation itself if access is allowed, so users can only read |
248 and add to history within repositories; they cannot run any other hg command. In |
248 and add to history within repositories; they cannot run any other hg command. |
249 addition, every push and pull is logged with a datestamp, changeset ID and the |
249 In addition, every push and pull is logged with a datestamp, changeset ID and |
250 key that performed the operation. |
250 the key that performed the operation. |
251 |
251 |
252 However, while the first paragraph holds no matter what bugs mercurial-server |
252 However, while the first paragraph holds no matter what bugs mercurial-server |
253 contains, the second depends on the relevant code being correct; though the |
253 contains, the second depends on the relevant code being correct; though the |
254 entire codebase is currently only about twice as long as this README, |
254 entire codebase is currently only about twice as long as this README, |
255 mercurial-server is a fairly new program and may harbour bugs. Backups are |
255 mercurial-server is a fairly new program and may harbour bugs. Backups are |