Monday, October 19, 2009

HTTPS Client Certificate Auth to Redmine and Subversion

Internally, we have been using Redmine (www.redmine.org) for managing development projects, and well as subversion for version control.

The time has come for providing access to both of these from the Internet. However, we do not want anyone who is not unauthorised to do so to be poking around the servers we make available.

The solution is to make the applications available over HTTPS, and to require the correct client certificates be installed on the client browsers, before a connection can be made.


A. Redmine Path.
Initially, Redmine was running through it's own webserver on http://server:3000/
However, we dont want to have Redmine available from a root url like this, but instead have to run from some sub-URI, e.g. .../redmine/, since other paths will refer to other applications.

Do do this, I did as was described in many other places on the Internet(http://www.redmine.org/wiki/1/HowTo_Install_Redmine_in_a_sub-URI), I added the following line to the end of \config\environment.rb

ActionController::AbstractRequest.relative_url_root = "/redmine"

Unfortunately, after restart the CSS and Javascript was not found, giving me an ugly, mostly white page. This was not something I found mentioned much, and the only solution I found was the following: to move everything inside Redmine's \public\ directory into \public\redmine\ to match the sub-URI path expected.

B. Apache and HTTPS

Below is a subset of the configuration directives used to enable HTTPS access. Anything not listed is a default setting in the example provided httpd-ssl.conf. The certificate files listed below were created in my previous post on using XCA to create various types of certificates.


<VirtualHost 192.168.1.250:443>

ServerName www.mydomain.com:443

SSLEngine on

# PEM encoded certificate
SSLCertificateFile "c:/Program Files/Apache Software Foundation/Apache2.2/conf/www.mydomain.com.pem"

# Where to find CA certificates for client authentication
SSLCACertificateFile "c:/Program Files/Apache Software Foundation/Apache2.2/conf/MyCA.crt"

# To force clients to use Client Certificates
SSLVerifyClient require
SSLVerifyDepth 2

...
...

</VirtualHost>


I added the following section to the above to provide access to Redmine:


<Location /redmine>
RequestHeader set X_FORWARDED_PROTO 'https'
ProxyPass http://localhost:3000/redmine
ProxyPassReverse http://localhost:3000/redmine
</Location>


Note: the above allows anyone with a valid trusted certificate (signed by 'MyCA' CA) to connect. Of course, they should not be able to get past Redmine's login page without a valid login. This is the same Basic Auth as we have been using in the office, except external access wraps it all inside a HTTPS connection.

We have allowed limited to our Redmine server to external testers, however they should have no access to our Subversion repository.

So the URL to our subversion, say /svn/ should be more restrictive than for Redmine - a trusted client certificate is still necessary, but it is not enough. The client certificate needs to have certain properties to be granted access to Subversion.

In our case, any client certificates that should be granted access to Subversion, must have 'OurCompany' set correctly in the Organisation field of the client certificates Distinguished Name. Any other client certs, e.g. those of external testers, must have the Organisation field set to something else.

The following snippet fulfils these requirements, where the subversion repository would be in c:\SVN\.


<Location /svn/>
SSLRequire %{SSL_CLIENT_S_DN_O} eq "OurCompany"

DAV svn
SVNPath C:/SVN/
AuthzSVNAccessFile "C:/Program Files/Apache Software Foundation/Apache2.2/conf/svn-acl"
AuthUserFile "C:/Program Files/Apache Software Foundation/Apache2.2/conf/svn-auth-file"

#SSLOptions +FakeBasicAuth +StrictRequire
#SSLUserName SSL_CLIENT_S_DN_CN

AuthName "Subversion"
AuthType Basic
AuthBasicProvider anon
Anonymous "*"
Require valid-user

<limitexcept>
Require valid-user
</limitexcept>

</Location>



The above snippet restricts access based on the particular client certificate in use, however once access have been granted, it still uses Basic Auth for Subversion authentication, with all subversion users configured in svn-acl. This is identical to the non-https internal access used in the office.

Ideally, the client certificate itself could be used to provide those authentication details, without needing a separate Basic Auth login after the HTTPS connection has already been negotiated.

However, this proved difficult. First, I enabling the following two lines (commented out in the above snippet):


SSLOptions +FakeBasicAuth +StrictRequire
SSLUserName SSL_CLIENT_S_DN_CN


The effect of these is to use the client certificate's Common Name as the username for subversion authentication. It then fakes the basic auth process with this username. Details of how to set up subversion with Basic Auth are only a quick search away.

The problem with the above is that the actual username coming from the client certificate is "CN=/bob" instead of "bob", so if I set the certificate Common Name to "Bob", and Bob has an internal username called "Bob", subversion logs for example will not have the same username when commits are made inside and outside the office by the same person.

1 comment:

  1. You are right that while making repositories available online major concern is security and for such functions https is the best way that require owning the certificate.Nice blog.
    pdf digital signature

    ReplyDelete