README
author Paul Crowley <paul@lshift.net>
Tue Apr 22 13:51:19 2008 +0100 (2008-04-22)
changeset 26 2c4f499ea12f
parent 20 f4daa224dc7e
child 28 583ed103e021
permissions -rw-r--r--
Explain limitations of branch/file rule combination
     1 hg-admin-tools
     2 
     3 A set of tools for managing authorization and access control for
     4 ssh-based Mercurial repositories
     5 
     6 Paul Crowley, paul@lshift.net, 2008
     7 
     8 This software may be used and distributed according to the terms
     9 of the GNU General Public License, incorporated herein by reference.
    10 
    11 WHAT IT GIVES YOU
    12 
    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.
    18 
    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.
    22 
    23 HOW IT WORKS
    24 
    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.
    32 
    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
    46 installation.
    47 
    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.
    55 
    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
    58 allow the changeset.
    59 
    60 GETTING STARTED
    61 
    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.
    64 
    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.
    70 
    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
    75 user.
    76 
    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
    81    sudo -u hg -H -s
    82    cd
    83    mkdir -p admin repos/hgadmin/keys/admin .ssh
    84    cd admin
    85    hg clone http://hg.opensource.lshift.net/hg-admin-tools
    86    cp hg-admin-tools/hg-ssh-wrapper hg-admin-tools/remote-hgrc ~
    87    cd ../repos/hgadmin
    88    hg init .
    89    echo "init user=admin/*" > hg-ssh-access.conf
    90    cp /tmp/my-ssh-public-key keys/admin/myname
    91    hg add
    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
    95    exit
    96    exit
    97 
    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:
   101 
   102    mkdir ~/hg
   103    cd ~/hg
   104    hg clone ssh://hg@repository-host/hgadmin
   105    cd hgadmin
   106 
   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".
   111 
   112 Users authorized to do so can now also create new repositories on this
   113 host with "clone":
   114 
   115   hg clone . ssh://hg@repository-host/my-project-name
   116 
   117 HG-SSH-ACCESS.CONF
   118 
   119 Each line of hg-ssh-access.conf has the following syntax:
   120 
   121 <rule> <condition> <condition> ...
   122 
   123 Rule is one of
   124 
   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
   129 
   130 A condition is a globpattern matched against a relative path, one of:
   131 
   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
   136 
   137 The first rule in the file which has all its conditions satisfied is
   138 used to determine whether an action is allowed.
   139 
   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 "/".
   144 
   145 Blank lines and lines that start with "#" are ignored.
   146 
   147 FILE CONDITIONS
   148 
   149 The rules file is used to make four decisions:
   150 
   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
   155 
   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:
   161 
   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
   166 example:
   167 
   168    read repo=specialrepo file=dontwritethis
   169    write repo=specialrepo
   170 
   171 allows all users to read specialrepo, and to write to all files
   172 *except* that any changeset which writes to "dontwritethis" will be
   173 rejected.
   174 
   175 - For similar reasons, don't give "init" rules file conditions.
   176 
   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
   182 
   183   write user=docs/* branch=docs file=docs/*
   184 
   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:
   188 
   189   write user=docs/* branch=docs
   190   write user=docs/* file=docs/*
   191   read user=docs/*
   192 
   193 LOCKING YOURSELF OUT
   194 
   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. 
   204 
   205 THANKS
   206 
   207 Thanks for reading this far.  If you use hg-admin-tools, please tell
   208 me about it.
   209 
   210 Paul Crowley, 2008