AuthenticatingHg » History » Version 20

Version 19 (Chris Cannam, 2010-08-13 02:01 PM) → Version 20/25 (Chris Cannam, 2010-08-13 02:04 PM)

h2. Authentication for Mercurial activity

h3. Requirements

# *Clone/pull from repo for public project*: Any user, no authentication required
# *Clone/pull from repo for private project*: Permitted users only
# *Push to repo for public project*: Permitted users only
# *Push to repo for private project*: Permitted users only
# *Create repo for public project*: User with manager role on project
# *Delete repo or carry out command-line admin tasks*: System admin only

What constitutes a permitted user for limited push or pull activity?

* A user who is a member of the project?
* A user who is identified in the [web] section of the repository?
* A user who is both a member and identified in the [web] section?
* A user who is either a member or identified in the [web] section?
* A user who is identified in the [web] section, if any, or is a member if there is no such section?

h3. Techniques

* Hg repository creation using "reposman.rb":/projects/soundsoftware-site/repository/entry/extra/svn/reposman.rb
* Apache authentication against Redmine user database using mod_auth_mysql (no support for LDAP-authenticated users?)
* Apache authentication against Redmine users using "the mod_perl module Redmine.pm":http://redmine.rubyforge.org/svn/trunk/extra/svn/Redmine.pm ("local copy":/projects/soundsoftware-site/repository/entry/extra/svn/Redmine.pm) or a variant thereof. Redmine.pm was designed for SVN access via WebDAV, but the code itself handles access and authentication only?
* Hg repository [web]-section authorisation using "hgwebdir.cgi":http://mercurial.selenic.com/wiki/PublishingRepositories#Setting_up_the_hgweb.cgi_script

Other links on this subject:

* "Separation between authentication and authorisation activities":http://markmail.org/message/xmav6qg3is3xptve#query:+page:1+mid:xmav6qg3is3xptve+state:results
* "hgrc [web] section":http://www.selenic.com/mercurial/hgrc.5.html#web
* "Using mod_auth_mysql to authenticate against the Redmine database directly":http://maff.ailoo.net/2009/03/authenticate-apache-against-redmine-with-authmysql/
* "Apache2 logging from mod_perl code":http://perl.apache.org/docs/2.0/api/Apache2/Log.html (for use in debugging Redmine.pm)

h3. Plan

We are currently using proxypass from a front-end server to the actual code server, so this complicates matters somewhat.

It seems reasonable-ish to assume that all hg traffic will use SSL. Apart from anything else, pushes and any other authenticated traffic really must be over SSL, so this avoids irritations when someone pulls from an http URL and then gets kicked out when attempting to push to the default target.

Therefore,

* Front-end server needs to have the relevant certificates
* Front-end server is currently running too old a version of Apache 2 to support SNI, so it can only support one https domain (question: does Mercurial's own Python client support SNI?) -- fortunately for us it is not currently running any other, but this suggests we really need to replace the front-end arch
* Front-end server redirects all http traffic on the hg domain to https and then proxypasses to the back-end server via http (connection is presumed trusted?)

At the back-end server,

* We will use hgwebdir.cgi instead of serving the hg directories directly as static HTTP
* The server's document root will be just a placeholder
* ScriptAlias is used to point all traffic to hgwebdir.cgi
* We will use our own "SoundSoftware.pm":/projects/soundsoftware-site/repository/entry/extra/svn/SoundSoftware.pm based on Redmine.pm to manage access
* We probably want to prevent people reading the list of repositories (so that per-project authentication is always in force)

The net result is that we should have hg over https with basic authentication based on the Redmine database and LDAP as appropriate, with access controls that we can define based on the project rules set out above.

Because of our front-end server limitations, we will use only a single domain name. That is, hg will be served through https://code.soundsoftware.ac.uk/hg/ rather than https://hg.soundsoftware.ac.uk/ as we might otherwise do. We can do this straightforwardly, because a ScriptAlias for /hg in the Apache config will take effect before mod_passenger gets to the request (no rewrite needed).

Thus Apache config:
<pre>
PerlLoadModule Apache::Authn::SoundSoftware

<VirtualHost *:80>
ServerName code.soundsoftware.ac.uk
ServerAdmin <me>

DocumentRoot /var/www/redmine/public
PassengerRestartDir restart_files
PassengerHighPerformance on
PassengerMaxRequests 5000
PassengerStatThrottleRate 10
RailsSpawnMethod smart
ExpiresDefault "access plus 1 minute"

<DirectoryMatch "^/.*/\.svn/">
Order allow,deny
Deny from all
Satisfy All
</DirectoryMatch>

<Directory /var/www/redmine/public>
Options -MultiViews
</Directory>

ScriptAlias /hg "/var/hg/index.cgi"

<Location /hg>
AuthName "Mercurial"
AuthType Basic
Require valid-user
PerlAccessHandler Apache::Authn::SoundSoftware::access_handler
PerlAuthenHandler Apache::Authn::SoundSoftware::authen_handler
SoundSoftwareDSN "DBI:mysql:database=<db>;host=localhost"
SoundSoftwareDbUser "<user>"
SoundSoftwareDbPass "<password>"
SoundSoftwareRepoPrefix "/var/hg/"
Options +ExecCGI
AddHandler cgi-script .cgi
ExpiresDefault now
</Location>

ErrorLog /var/log/apache2/error.log
LogLevel warn

ServerSignature Off
</VirtualHost>
</pre>

Note that while SoundSoftware.pm is substantially customised from Redmine.pm, /var/hg/index.cgi is a completely stock hgwebdir.cgi (with UTF-8 and, currently, traceback options uncommented).

*Problem:*
*Problem*: hgwebdir.cgi refuses to accept authentication if it is not running under SSL (it just spits out "ssl required"). That's reasonable enough, except that the user connection is under SSL, it's just the connection within the virtual host wrapper that is not. It can be fixed in @hgweb.config@, see below. We also want to set the config to permit anyone to push, because we are using the mod_perl authentication module rather than the repository configuration to determine who is permitted.

<pre>
[web]
push_ssl = false
allow_push = *
</pre>

*Now...* what about creating repositories?