doc/manual.docbook
changeset 120 16056a9015f3
parent 119 40a287c95661
child 121 62185dc7d0c9
--- a/doc/manual.docbook	Wed Oct 14 12:46:38 2009 +0100
+++ b/doc/manual.docbook	Wed Oct 14 14:29:39 2009 +0100
@@ -1,16 +1,20 @@
 <?xml version="1.0" encoding="utf-8"?>
-<article xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en">
+<article xmlns="http://docbook.org/ns/docbook" version="5.0" xml:lang="en"
+  xmlns:xlink="http://www.w3.org/1999/xlink">
 <info>
   <title>Sharing Mercurial repositories with mercurial-server</title>
   <author><firstname>Paul</firstname><surname>Crowley</surname></author>
-  <copyright><year>2009</year><holder>Paul Crowley</holder></copyright>
+  <copyright><year>2009</year><holder>Paul Crowley, LShift Ltd</holder></copyright>
 </info>
 <section>
 <title>About mercurial-server</title>
 <para>
+Home page: <link xlink:href="http://www.lshift.net/mercurial-server.html"/>
+</para>
+<para>
 mercurial-server is software for Debian and Ubuntu systems which gives your
-developers remote read/write access to <ulink
-url="http://hg-scm.org/">Mercurial</ulink> repositories using SSH public
+developers remote read/write access to <link
+xlink:href="http://hg-scm.org/">Mercurial</link> repositories using SSH public
 key authentication; it provides convenient and fine-grained key management
 and access control.
 </para>
@@ -18,9 +22,9 @@
 mercurial-server is the easiest and most secure way for several developers
 to have read/write access to a central repository, but that's not the only
 way for several people to work on the same project using Mercurial; you
-should be familiar with the <ulink
-url="http://mercurial.selenic.com/wiki/MultipleCommitters">other ways of
-handling multiple commiters</ulink> before deciding to use this.
+should be familiar with the <link
+xlink:href="http://mercurial.selenic.com/wiki/MultipleCommitters">other ways of
+handling multiple commiters</link> before deciding to use this.
 </para>
 <para>
 Though mercurial-server is currently targeted at Debian-based systems such
@@ -52,10 +56,26 @@
 <section>
 <title>Step by step</title>
 <para>
-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.
+mercurial-server authenticates users not using passwords but using <link
+xlink:href="http://sial.org/howto/openssh/publickey-auth/">SSH public
+keys</link>; 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 <link
+xlink:href="http://the.earth.li/~sgtatham/putty/0.60/htmldoc/Chapter9.html#pageant">Pageant</link>),
+this means that users will not need to type in a password to access the
+repository.
 </para>
+<section>
+<title>Creating a repository host</title>
 <para>
-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.
+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.
 </para>
 <screen>
 <computeroutput>user@my-workstation:~$ </computeroutput><userinput>ssh-keygen</userinput>
@@ -90,7 +110,7 @@
 Setting up mercurial-server (0.6.1) ...
 user@repository-host:~$ </computeroutput></screen>
 <para>
-mercurial-server is now installed on the repository host.  Next, we need to give you permission to see its repositories.
+mercurial-server is now installed on the repository host.  Next, we need to give you permission to access its repositories.
 </para>
 <screen>
 <computeroutput>user@repository-host:~$ </computeroutput><userinput>ssh-add -L > my-key</userinput>
@@ -101,8 +121,13 @@
 <computeroutput>Connection to shell closed.
 user@my-workstation:~$ </computeroutput></screen>
 <para>
-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.
+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.
 </para>
+</section>
+<section>
+<title>Creating repositories</title>
 <screen>
 <computeroutput>user@my-workstation:~$ </computeroutput><userinput>cd my-mercurial-project</userinput>
 <computeroutput>user@my-workstation:~/my-mercurial-project$ </computeroutput><userinput>hg clone . ssh://hg@repository-host/repository/name</userinput>
@@ -116,8 +141,19 @@
 searching for changes
 no changes found
 user@my-workstation:~/my-mercurial-project$ </computeroutput></screen>
+</section>
+<section>
+<title>Adding other users</title>
 <para>
-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.
+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; we'll assume you have that key in
+<filename>~/other-users-key.pub</filename>. You could give them access by
+logging into <systemitem class="systemname">repository-host</systemitem>,
+putting the key 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.
 </para>
 <screen>
 <computeroutput>user@my-workstation:~/my-mercurial-project$ </computeroutput><userinput>cd ..</userinput>
@@ -141,7 +177,159 @@
 remote: added 1 changesets with 1 changes to 1 files
 user@my-workstation:~/hgadmin$ </computeroutput></screen>
 <para>
-The new user can now read and write to your <literal>ssh://hg@repository-host/repository/name</literal> repository.
+The new user can now read and write to your
+<literal>ssh://hg@repository-host/repository/name</literal> repository.
+Most other changes to access control can be made simply by making and
+pushing changes to <literal>hgadmin</literal>, and you can use Mercurial to
+cooperate with other root users in the normal way.
+</para>
+</section>
+<section>
+<title>Basic access control</title>
+<para>
+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.
+</para>
+<para>
+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"/>.
+</para>
+</section>
+</section>
+<section>
+<title>How mercurial-server works</title>
+<para>
+All of the repositories controlled by mercurial-server are owned by a
+single user, the <literal>hg</literal> user, which is why all URLs for
+mercurial-server repositories start with <literal>ssh://hg@...</literal>.
+Each SSH key that has access to the repository has an entry in
+<filename>~hg/.ssh/authorized_keys</filename>; this is how the SSH daemon
+knows to give that key access. When the user connects over SSH, their
+commands are run in a specially crafted restricted shell; this shell knows
+which key was used to connect, determines what the user is trying to do,
+and checks the access rules to decide whether to allow it.  
+</para>
+<para>
+This restricted shell also ensures that certain Mercurial extensions are
+loaded when the user acts on a repository; these extensions check the
+access control rules for any changeset that the user tries to commit, and
+log all pushes and pulls into a per-repository access log.
+</para>
+<para>
+<command>refresh-auth</command> recurses through the <filename
+class='directory'>/etc/mercurial-server/keys</filename> and the <filename
+class='directory'>keys</filename> directory in the
+<literal>hgadmin</literal> repository, creating an entry in
+<filename>~hg/.ssh/authorized_keys</filename> for each one. This is redone
+automatically whenever a change is pushed to <literal>hgadmin</literal>.
+</para>
+</section>
+<section id="accesscontrol">
+<title>Access control</title>
+<para>
+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
+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
+class='directory'>keys/root</filename> or <filename
+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>
+<programlisting>
+    write repo=widget user=widget/**
+</programlisting>
+<para>
+Pat will have read and write access as soon as we add, commit, and push these files.
+</para>
+<para>
+Each line of access.conf has the following syntax:
+</para>
+<programlisting>
+<replaceable>rule</replaceable> <replaceable>condition</replaceable> <replaceable>condition...</replaceable>
+</programlisting>
+<para>
+Blank lines and lines that start with <literal>#</literal> are ignored. Rule is one of
+</para>
+<itemizedlist>
+<listitem>
+<literal>init</literal>: allow reads, writes, and the creation of new repositories
+</listitem>
+<listitem>
+<literal>write</literal>: allow reads and writes
+</listitem>
+<listitem>
+<literal>read</literal>: allow only read operations
+</listitem>
+<listitem>
+<literal>deny</literal>: deny all requests
+</listitem>
+</itemizedlist>
+<para>
+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.
+</para>
+<para>
+By default, <filename>/etc/mercurial-server/access.conf</filename> has the following rules:
+</para>
+<programlisting>
+    init user=root/**
+    deny repo=hgadmin
+    write user=users/**
+</programlisting>
+<para>
+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.
+</para>
+<para>
+A condition is a globpattern matched against a relative path. The two most
+important conditions are
+</para>
+<itemizedlist>
+<listitem>
+<code><literal>user=</literal><replaceable>globpattern</replaceable></code>: path to the user's key
+</listitem>
+<listitem>
+<code><literal>repo=</literal><replaceable>globpattern</replaceable></code>: path to the repository
+</listitem>
+</itemizedlist>
+<para>
+"*" only matches one directory level, where "**" matches as many as you
+want. More precisely, "*" matches zero or more characters not including "/"
+while "**" matches zero or more characters including "/".
+</para>
+<section>
+<title>File conditions</title>
+<para>
+Here be dragons...
+</para>
+</section>
+</section>
+<section>
+<title>Security</title>
+<para>
+mercurial-server relies entirely on sshd to grant access to remote users.
+As a result, it runs no daemons, installs no setuid programs, and no part
+of it runs as root except the install process: all programs run as the user
+hg. And any attack on mercurial-server can only be started if the Bad Guys
+already have a public key in <filename>~hg/.ssh/authorized_keys</filename>,
+otherwise sshd will bar the way.
+</para>
+<para>
+No matter what command the user tries to run on the remote system via SSH,
+mercurial-server is run. It parses the command line the user asked for, and
+interprets and runs the corresponding hg operation itself if access is
+allowed, so users can only read and add to history within repositories;
+they cannot run any other hg command. In addition, every push and pull is
+logged with a datestamp, changeset ID and the key that performed the
+operation.
+</para>
+<para>
+However, while the first paragraph holds no matter what bugs
+mercurial-server contains, the second depends on the relevant code being
+correct; though the entire codebase is short, mercurial-server is a fairly
+new program and may harbour bugs. Backups are essential!
+</para>
+</section>
+<section>
+<title>Thanks</title>
+<para>
+Thanks for reading this far. If you use mercurial-server, please tell me about
+it.
+</para>
+<para>
+Paul Crowley, <email>paul@lshift.net</email>, 2009
 </para>
 </section>
 </article>