1 <?xml version="1.0" encoding="utf-8"?> |
1 <?xml version="1.0" encoding="utf-8"?> |
2 <article xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en"> |
2 <article xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en" |
|
3 xmlns:xlink="http://www.w3.org/1999/xlink"> |
3 <info> |
4 <info> |
4 <title>Sharing Mercurial repositories with mercurial-server</title> |
5 <title>Sharing Mercurial repositories with mercurial-server</title> |
5 <author><firstname>Paul</firstname><surname>Crowley</surname></author> |
6 <author><firstname>Paul</firstname><surname>Crowley</surname></author> |
6 <copyright><year>2009</year><holder>Paul Crowley</holder></copyright> |
7 <copyright><year>2009</year><holder>Paul Crowley, LShift Ltd</holder></copyright> |
7 </info> |
8 </info> |
8 <section> |
9 <section> |
9 <title>About mercurial-server</title> |
10 <title>About mercurial-server</title> |
10 <para> |
11 <para> |
|
12 Home page: <link xlink:href="http://www.lshift.net/mercurial-server.html"/> |
|
13 </para> |
|
14 <para> |
11 mercurial-server is software for Debian and Ubuntu systems which gives your |
15 mercurial-server is software for Debian and Ubuntu systems which gives your |
12 developers remote read/write access to <ulink |
16 developers remote read/write access to <link |
13 url="http://hg-scm.org/">Mercurial</ulink> repositories using SSH public |
17 xlink:href="http://hg-scm.org/">Mercurial</link> repositories using SSH public |
14 key authentication; it provides convenient and fine-grained key management |
18 key authentication; it provides convenient and fine-grained key management |
15 and access control. |
19 and access control. |
16 </para> |
20 </para> |
17 <para> |
21 <para> |
18 mercurial-server is the easiest and most secure way for several developers |
22 mercurial-server is the easiest and most secure way for several developers |
19 to have read/write access to a central repository, but that's not the only |
23 to have read/write access to a central repository, but that's not the only |
20 way for several people to work on the same project using Mercurial; you |
24 way for several people to work on the same project using Mercurial; you |
21 should be familiar with the <ulink |
25 should be familiar with the <link |
22 url="http://mercurial.selenic.com/wiki/MultipleCommitters">other ways of |
26 xlink:href="http://mercurial.selenic.com/wiki/MultipleCommitters">other ways of |
23 handling multiple commiters</ulink> before deciding to use this. |
27 handling multiple commiters</link> before deciding to use this. |
24 </para> |
28 </para> |
25 <para> |
29 <para> |
26 Though mercurial-server is currently targeted at Debian-based systems such |
30 Though mercurial-server is currently targeted at Debian-based systems such |
27 as Ubuntu, other users have reported success getting it running on other |
31 as Ubuntu, other users have reported success getting it running on other |
28 Unix-based systems such as Red Hat. Running it on a non-Unix system such as |
32 Unix-based systems such as Red Hat. Running it on a non-Unix system such as |
50 </section> |
54 </section> |
51 </section> |
55 </section> |
52 <section> |
56 <section> |
53 <title>Step by step</title> |
57 <title>Step by step</title> |
54 <para> |
58 <para> |
55 mercurial-server authenticates users not using passwords but using <ulink url="http://sial.org/howto/openssh/publickey-auth/">SSH public keys</ulink>; everyone who wants access to a mercurial-server repository will need such a key, so you'll need to familiarize yourself with them before proceeding. In combination with <command>ssh-agent</command> (or equivalents such as the Windows program <ulink url="http://the.earth.li/~sgtatham/putty/0.60/htmldoc/Chapter9.html#pageant">Pageant</ulink>), this means that users will not need to type in a password to access the repository. |
59 mercurial-server authenticates users not using passwords but using <link |
56 </para> |
60 xlink:href="http://sial.org/howto/openssh/publickey-auth/">SSH public |
57 <para> |
61 keys</link>; everyone who wants access to a mercurial-server repository |
58 In what follows, we assume that you usually sit at a machine called <systemitem class="systemname">my-workstation</systemitem> and you wish to install mercurial-server on <systemitem class="systemname">repository-host</systemitem>. First, you'll need to create an SSH public key if you haven't already. You should consult your system documentation on how to do this, but it should look something like this. |
62 will need such a key, so you'll need to familiarize yourself with them |
|
63 before proceeding. In combination with <command>ssh-agent</command> (or |
|
64 equivalents such as the Windows program <link |
|
65 xlink:href="http://the.earth.li/~sgtatham/putty/0.60/htmldoc/Chapter9.html#pageant">Pageant</link>), |
|
66 this means that users will not need to type in a password to access the |
|
67 repository. |
|
68 </para> |
|
69 <section> |
|
70 <title>Creating a repository host</title> |
|
71 <para> |
|
72 In what follows, we assume that you usually sit at a machine called |
|
73 <systemitem class="systemname">my-workstation</systemitem> and you wish to |
|
74 install mercurial-server on <systemitem |
|
75 class="systemname">repository-host</systemitem>. First, you'll need to |
|
76 create an SSH public key if you haven't already. You should consult your |
|
77 system documentation on how to do this, but it should look something like |
|
78 this. |
59 </para> |
79 </para> |
60 <screen> |
80 <screen> |
61 <computeroutput>user@my-workstation:~$ </computeroutput><userinput>ssh-keygen</userinput> |
81 <computeroutput>user@my-workstation:~$ </computeroutput><userinput>ssh-keygen</userinput> |
62 <computeroutput>Generating public/private rsa key pair. |
82 <computeroutput>Generating public/private rsa key pair. |
63 Enter passphrase (empty for no passphrase): |
83 Enter passphrase (empty for no passphrase): |
88 (Reading database ... 144805 files and directories currently installed.) |
108 (Reading database ... 144805 files and directories currently installed.) |
89 Unpacking mercurial-server (from .../mercurial-server_0.6.1_amd64.deb) ... |
109 Unpacking mercurial-server (from .../mercurial-server_0.6.1_amd64.deb) ... |
90 Setting up mercurial-server (0.6.1) ... |
110 Setting up mercurial-server (0.6.1) ... |
91 user@repository-host:~$ </computeroutput></screen> |
111 user@repository-host:~$ </computeroutput></screen> |
92 <para> |
112 <para> |
93 mercurial-server is now installed on the repository host. Next, we need to give you permission to see its repositories. |
113 mercurial-server is now installed on the repository host. Next, we need to give you permission to access its repositories. |
94 </para> |
114 </para> |
95 <screen> |
115 <screen> |
96 <computeroutput>user@repository-host:~$ </computeroutput><userinput>ssh-add -L > my-key</userinput> |
116 <computeroutput>user@repository-host:~$ </computeroutput><userinput>ssh-add -L > my-key</userinput> |
97 <computeroutput>user@repository-host:~$ </computeroutput><userinput>sudo mkdir -p /etc/mercurial-server/keys/root/user</userinput> |
117 <computeroutput>user@repository-host:~$ </computeroutput><userinput>sudo mkdir -p /etc/mercurial-server/keys/root/user</userinput> |
98 <computeroutput>user@repository-host:~$ </computeroutput><userinput>sudo cp my-key /etc/mercurial-server/keys/root/user/my-workstation</userinput> |
118 <computeroutput>user@repository-host:~$ </computeroutput><userinput>sudo cp my-key /etc/mercurial-server/keys/root/user/my-workstation</userinput> |
99 <computeroutput>user@repository-host:~$ </computeroutput><userinput>sudo -u hg /usr/share/mercurial-server/refresh-auth</userinput> |
119 <computeroutput>user@repository-host:~$ </computeroutput><userinput>sudo -u hg /usr/share/mercurial-server/refresh-auth</userinput> |
100 <computeroutput>user@repository-host:~$ </computeroutput><userinput>exit</userinput> |
120 <computeroutput>user@repository-host:~$ </computeroutput><userinput>exit</userinput> |
101 <computeroutput>Connection to shell closed. |
121 <computeroutput>Connection to shell closed. |
102 user@my-workstation:~$ </computeroutput></screen> |
122 user@my-workstation:~$ </computeroutput></screen> |
103 <para> |
123 <para> |
104 You can now create repositories on the remote machine and have complete read-write access to all of them; you need never log on to <systemitem class="systemname">repository-host</systemitem> again. |
124 You can now create repositories on the remote machine and have complete |
105 </para> |
125 read-write access to all of them; you need never log on to <systemitem |
|
126 class="systemname">repository-host</systemitem> again. |
|
127 </para> |
|
128 </section> |
|
129 <section> |
|
130 <title>Creating repositories</title> |
106 <screen> |
131 <screen> |
107 <computeroutput>user@my-workstation:~$ </computeroutput><userinput>cd my-mercurial-project</userinput> |
132 <computeroutput>user@my-workstation:~$ </computeroutput><userinput>cd my-mercurial-project</userinput> |
108 <computeroutput>user@my-workstation:~/my-mercurial-project$ </computeroutput><userinput>hg clone . ssh://hg@repository-host/repository/name</userinput> |
133 <computeroutput>user@my-workstation:~/my-mercurial-project$ </computeroutput><userinput>hg clone . ssh://hg@repository-host/repository/name</userinput> |
109 <computeroutput>searching for changes |
134 <computeroutput>searching for changes |
110 remote: adding changesets |
135 remote: adding changesets |
114 user@my-workstation:~/my-mercurial-project$ </computeroutput><userinput>hg pull ssh://hg@repository-host/repository/name</userinput> |
139 user@my-workstation:~/my-mercurial-project$ </computeroutput><userinput>hg pull ssh://hg@repository-host/repository/name</userinput> |
115 <computeroutput>pulling from ssh://hg@repository-host/repository/name |
140 <computeroutput>pulling from ssh://hg@repository-host/repository/name |
116 searching for changes |
141 searching for changes |
117 no changes found |
142 no changes found |
118 user@my-workstation:~/my-mercurial-project$ </computeroutput></screen> |
143 user@my-workstation:~/my-mercurial-project$ </computeroutput></screen> |
119 <para> |
144 </section> |
120 As things stand, no-one but you has any access to any repositories you create on this system. In order to give anyone else access, you'll need a copy of their SSH public key. Once you have that key, you could give them access by logging into <systemitem class="systemname">repository-host</systemitem>, putting their keys in the right place under <filename class='directory'>/etc/mercurial-server/keys</filename>, and re-running <userinput>sudo -u hg /usr/share/mercurial-server/refresh-auth</userinput>. However, there's a more convenient way. |
145 <section> |
|
146 <title>Adding other users</title> |
|
147 <para> |
|
148 As things stand, no-one but you has any access to any repositories you |
|
149 create on this system. In order to give anyone else access, you'll need a |
|
150 copy of their SSH public key; we'll assume you have that key in |
|
151 <filename>~/other-users-key.pub</filename>. You could give them access by |
|
152 logging into <systemitem class="systemname">repository-host</systemitem>, |
|
153 putting the key in the right place under <filename |
|
154 class='directory'>/etc/mercurial-server/keys</filename>, and re-running |
|
155 <userinput>sudo -u hg /usr/share/mercurial-server/refresh-auth</userinput>. |
|
156 However, there's a more convenient way. |
121 </para> |
157 </para> |
122 <screen> |
158 <screen> |
123 <computeroutput>user@my-workstation:~/my-mercurial-project$ </computeroutput><userinput>cd ..</userinput> |
159 <computeroutput>user@my-workstation:~/my-mercurial-project$ </computeroutput><userinput>cd ..</userinput> |
124 <computeroutput>user@my-workstation:~$ </computeroutput><userinput>hg clone ssh://hg@repository-host/hgadmin</userinput> |
160 <computeroutput>user@my-workstation:~$ </computeroutput><userinput>hg clone ssh://hg@repository-host/hgadmin</userinput> |
125 <computeroutput>destination directory: hgadmin |
161 <computeroutput>destination directory: hgadmin |
139 remote: adding manifests |
175 remote: adding manifests |
140 remote: adding file changes |
176 remote: adding file changes |
141 remote: added 1 changesets with 1 changes to 1 files |
177 remote: added 1 changesets with 1 changes to 1 files |
142 user@my-workstation:~/hgadmin$ </computeroutput></screen> |
178 user@my-workstation:~/hgadmin$ </computeroutput></screen> |
143 <para> |
179 <para> |
144 The new user can now read and write to your <literal>ssh://hg@repository-host/repository/name</literal> repository. |
180 The new user can now read and write to your |
|
181 <literal>ssh://hg@repository-host/repository/name</literal> repository. |
|
182 Most other changes to access control can be made simply by making and |
|
183 pushing changes to <literal>hgadmin</literal>, and you can use Mercurial to |
|
184 cooperate with other root users in the normal way. |
|
185 </para> |
|
186 </section> |
|
187 <section> |
|
188 <title>Basic access control</title> |
|
189 <para> |
|
190 Out of the box, mercurial-server supports two kinds of users: "root" users and normal users. If you followed the steps above, you are a "root" user because your key is under <filename class='directory'>keys/root</filename>, while the other user you gave access to is a normal user since their key is under <filename class='directory'>keys/users</filename>. Keys that are not in either of these directories will by default have no access to anything. |
|
191 </para> |
|
192 <para> |
|
193 Root users can edit <literal>hgadmin</literal>, create new repositories and read and write to existing ones. Normal users cannot access <literal>hgadmin</literal> or create new repositories, but they can read and write to any other repository. This is only the default configuration; for more advanced configuration read <xref linkend="accesscontrol"/>. |
|
194 </para> |
|
195 </section> |
|
196 </section> |
|
197 <section> |
|
198 <title>How mercurial-server works</title> |
|
199 <para> |
|
200 All of the repositories controlled by mercurial-server are owned by a |
|
201 single user, the <literal>hg</literal> user, which is why all URLs for |
|
202 mercurial-server repositories start with <literal>ssh://hg@...</literal>. |
|
203 Each SSH key that has access to the repository has an entry in |
|
204 <filename>~hg/.ssh/authorized_keys</filename>; this is how the SSH daemon |
|
205 knows to give that key access. When the user connects over SSH, their |
|
206 commands are run in a specially crafted restricted shell; this shell knows |
|
207 which key was used to connect, determines what the user is trying to do, |
|
208 and checks the access rules to decide whether to allow it. |
|
209 </para> |
|
210 <para> |
|
211 This restricted shell also ensures that certain Mercurial extensions are |
|
212 loaded when the user acts on a repository; these extensions check the |
|
213 access control rules for any changeset that the user tries to commit, and |
|
214 log all pushes and pulls into a per-repository access log. |
|
215 </para> |
|
216 <para> |
|
217 <command>refresh-auth</command> recurses through the <filename |
|
218 class='directory'>/etc/mercurial-server/keys</filename> and the <filename |
|
219 class='directory'>keys</filename> directory in the |
|
220 <literal>hgadmin</literal> repository, creating an entry in |
|
221 <filename>~hg/.ssh/authorized_keys</filename> for each one. This is redone |
|
222 automatically whenever a change is pushed to <literal>hgadmin</literal>. |
|
223 </para> |
|
224 </section> |
|
225 <section id="accesscontrol"> |
|
226 <title>Access control</title> |
|
227 <para> |
|
228 mercurial-server offers much more fine-grained access control than this division into two classes of users. Let's suppose you wish to give Pat access to the <literal>widget</literal> repository, but no other. We first copy Pat's SSH public key into the <filename |
|
229 class='directory'>keys/widget/pat</filename> directory in <literal>hgadmin</literal>. Now mercurial-server knows about Pat's key, but will give Pat no access to anything because the key is not under either <filename |
|
230 class='directory'>keys/root</filename> or <filename |
|
231 class='directory'>keys/users</filename>. To grant this key access, we must give mercurial-server a new access rule, so we create a file in <literal>hgadmin</literal> called <filename>access.conf</filename>, with the following contents:</para> |
|
232 <programlisting> |
|
233 write repo=widget user=widget/** |
|
234 </programlisting> |
|
235 <para> |
|
236 Pat will have read and write access as soon as we add, commit, and push these files. |
|
237 </para> |
|
238 <para> |
|
239 Each line of access.conf has the following syntax: |
|
240 </para> |
|
241 <programlisting> |
|
242 <replaceable>rule</replaceable> <replaceable>condition</replaceable> <replaceable>condition...</replaceable> |
|
243 </programlisting> |
|
244 <para> |
|
245 Blank lines and lines that start with <literal>#</literal> are ignored. Rule is one of |
|
246 </para> |
|
247 <itemizedlist> |
|
248 <listitem> |
|
249 <literal>init</literal>: allow reads, writes, and the creation of new repositories |
|
250 </listitem> |
|
251 <listitem> |
|
252 <literal>write</literal>: allow reads and writes |
|
253 </listitem> |
|
254 <listitem> |
|
255 <literal>read</literal>: allow only read operations |
|
256 </listitem> |
|
257 <listitem> |
|
258 <literal>deny</literal>: deny all requests |
|
259 </listitem> |
|
260 </itemizedlist> |
|
261 <para> |
|
262 When considering a request, mercurial-server steps through all the rules in <filename>/etc/mercurial-server/access.conf</filename> and then all the rules in <filename>access.conf</filename> in <literal>hgadmin</literal> looking for a rule which matches on every condition. If it does not find such a rule, it denies the request; otherwise it checks whether the rule grants sufficient privilege to allow it. |
|
263 </para> |
|
264 <para> |
|
265 By default, <filename>/etc/mercurial-server/access.conf</filename> has the following rules: |
|
266 </para> |
|
267 <programlisting> |
|
268 init user=root/** |
|
269 deny repo=hgadmin |
|
270 write user=users/** |
|
271 </programlisting> |
|
272 <para> |
|
273 These rules ensure that root users can do any operation on any repository, that no other users can access the <literal>hgadmin</literal> repository, and that those with keys in <filename class='directory'>keys/users</filename> can read or write to any repository but not create repositories. |
|
274 </para> |
|
275 <para> |
|
276 A condition is a globpattern matched against a relative path. The two most |
|
277 important conditions are |
|
278 </para> |
|
279 <itemizedlist> |
|
280 <listitem> |
|
281 <code><literal>user=</literal><replaceable>globpattern</replaceable></code>: path to the user's key |
|
282 </listitem> |
|
283 <listitem> |
|
284 <code><literal>repo=</literal><replaceable>globpattern</replaceable></code>: path to the repository |
|
285 </listitem> |
|
286 </itemizedlist> |
|
287 <para> |
|
288 "*" only matches one directory level, where "**" matches as many as you |
|
289 want. More precisely, "*" matches zero or more characters not including "/" |
|
290 while "**" matches zero or more characters including "/". |
|
291 </para> |
|
292 <section> |
|
293 <title>File conditions</title> |
|
294 <para> |
|
295 Here be dragons... |
|
296 </para> |
|
297 </section> |
|
298 </section> |
|
299 <section> |
|
300 <title>Security</title> |
|
301 <para> |
|
302 mercurial-server relies entirely on sshd to grant access to remote users. |
|
303 As a result, it runs no daemons, installs no setuid programs, and no part |
|
304 of it runs as root except the install process: all programs run as the user |
|
305 hg. And any attack on mercurial-server can only be started if the Bad Guys |
|
306 already have a public key in <filename>~hg/.ssh/authorized_keys</filename>, |
|
307 otherwise sshd will bar the way. |
|
308 </para> |
|
309 <para> |
|
310 No matter what command the user tries to run on the remote system via SSH, |
|
311 mercurial-server is run. It parses the command line the user asked for, and |
|
312 interprets and runs the corresponding hg operation itself if access is |
|
313 allowed, so users can only read and add to history within repositories; |
|
314 they cannot run any other hg command. In addition, every push and pull is |
|
315 logged with a datestamp, changeset ID and the key that performed the |
|
316 operation. |
|
317 </para> |
|
318 <para> |
|
319 However, while the first paragraph holds no matter what bugs |
|
320 mercurial-server contains, the second depends on the relevant code being |
|
321 correct; though the entire codebase is short, mercurial-server is a fairly |
|
322 new program and may harbour bugs. Backups are essential! |
|
323 </para> |
|
324 </section> |
|
325 <section> |
|
326 <title>Thanks</title> |
|
327 <para> |
|
328 Thanks for reading this far. If you use mercurial-server, please tell me about |
|
329 it. |
|
330 </para> |
|
331 <para> |
|
332 Paul Crowley, <email>paul@lshift.net</email>, 2009 |
145 </para> |
333 </para> |
146 </section> |
334 </section> |
147 </article> |
335 </article> |
148 |
336 |