Setting up Subversion with the Apache web server is pretty easy. Setting it up with SSL is still not too difficult. But I’ve been trying to do that plus require the Subversion client to authenticate to the server using an SSL client certificate. That’s not so easy. I’m not quite there yet, but I’m close and don’t want to forget what I’ve done so far, so I’m documenting it here. When I’m completely done I’ll update this post with the final steps.
First I needed a server.
I chose a Linux server on Amazon Web Services Elastic Compute Cloud. The one I chose is currently the second one in the list, “Basic 64-bit Amazon Linux AMI”, and I launched it as a micro instance since Subversion shouldn’t require much in the way of computing power. This kind of server costs only two cents an hour to run, or less than $15 per month.
Next, I logged on to the server, became root, and used yum to install the basic software needed:
yum -y install httpd subversion mod_ssl mod_dav_svn
And started the web server:
service httpd start
Now I had a working web server, both over HTTP and HTTPS (SSL). When I tried to connect to the SSL server my browser warned me that I shouldn’t trust it; that’s because it has a self-signed SSL certificate that mod_ssl installed by default. I may fix that later by buying a commercial certificate, but maybe not. I know the certificate is okay because I installed it myself.
Step 3 is to get Subversion working.
That’s a two part operation: set up an area for the repositories (and set up the repositories in it), and then configure Apache to know about that area.
Part 1: set up the area and the first repository (still as root):
mkdir /var/www/svn svnadmin create /var/www/svn/project1 chown -R apache:apache /var/www/svn/project1
I’m not a Linux sysadmin so I don’t know whether /var/www/svn is a good place for this, but it works. The chown command is important because the Subversion operations will be performed by the Apache web server, so it needs permission to operate on the repository. By default, these repositories are readable by anybody on the server machine but can only be written to by the Apache web server.
Part 2: configure Apache:
For the Linux flavor I’m using the web server configuration files are in /etc/httpd. The main configuration file is /etc/httpd/conf/httpd.conf and the SSL server configuration is /etc/httpd/conf.d/ssl.conf. There’s also a Subversion configuration file in /etc/httpd/conf.d/subversion.conf but it doesn’t seem to be up to date with changes in Apache web server version 2.2.
In any case, I thought I needed to add (or uncomment) a line in the LoadModule section of the main configuration file, but it was already included automatically as part of the subversion.conf file:
LoadModule dav_svn_module modules/mod_dav_svn.so
That line is the module that actually provides Subversion functionality to the Apache web server. There will be other lines needed for user authentication which will have to be added once I’m done getting client certificate authentication working right.
Next, I had to add a Location section to the SSL server configuration file. Let’s say I want my Subversion repositories to be accessed via https://my.host.name/svn/repositoryname. I added the following section the SSL configuration file:
<Location /svn> DAV svn SVNParentPath /var/www/svn </Location>
When I restarted the web server (with service httpd restart) I had a working Subversion server over SSL. I tried it from my PC:
svn checkout https://yourserver/svn/project1 project1
As with the web browser, I was warned about the server’s self-signed certificate, but can choose to connect anyway. Since I haven’t set up any authentication yet, the checkout didn’t ask who I was. I even added a file and checked it in for practice, then pointed my web browser to https://my.host.name/svn/project1 to see the file there. It worked fine. Now to mess that all up!
Step 4: Configure the server to demand a client certificate.
First I just want to get the web server to demand a good certificate from the client. Once that works I’ll look into using the identification in that certificate to control access to Subversion.
Inside the <Location> section for Subversion, I added three lines as follows:
<Location /svn> DAV svn SVNParentPath /var/www/svn SSLVerifyClient require SSLVerifyDepth 10 SSLCACertificateFile /etc/httpd/conf/myca.crt </Location>
The SSLVerifyClient line tells Apache to require the connecting client to authenticate the SSL connection using an acceptable certificate; the other two lines tell Apache how to make sure the certificate is acceptable. The SSLVerifyDepth directive says that it will follow a chain of certificates up to 10 deep if needed, and the SSLCACertificateFile is the location of the Certificate Authority root file you want the certificates to have been issued by. Following a chain 10 deep is probably overkill, but won’t hurt anything.
I’ve specified that my CA root certificate is in /etc/httpd/conf/myca.crt. That’s not a standard location for certificate authorities, but eventually I’ll want to use a private certificate authority just for this purpose, and not rely on public ones.
When I try to restart the web server with service httpd restart it fails because /etc/httpd/conf/myca.crt doesn’t exist. So, just to get past this step for the moment, I copied my server’s certificate there:
cp /etc/pki/tls/certs/localhost.crt /etc/httpd/conf/myca.crt
Now when I tried to start the server it works. But when I went to my working copy on my PC and tred to do an svn update I couldn’t. I was prompted for a client certificate. Which I don’t have yet.
I tried viewing the repository with a web browser, and that didn’t work, either. But at least Chrome gave me a hint:
So it was time to get a client certificate. Eventually I want my own private certificate authority, but first I just want to get this working in a minimal way today. I went to Verisign and got a free trial “Digital ID for Secure Email” at www.verisign.com/digital-id/index.html. (I used Internet Explorer to request and fetch the certificate.) Then I exported it (including the private key) to the file testcert.pfx. I had to make up a password for it to export it.
I tried an svn update in my working copy again, and this time when I was prompted for a certificate I gave it that file. And got a new error message:
C:\temp\project1> svn up Authentication realm: https://my.host.name:443 Client certificate filename: testcert.pfx Passphrase for 'C:/Temp/project1/testcert.pfx': ******** svn: OPTIONS of 'https://my.host.name/svn/project1': Could not read status line: SSL error: tlsv1 alert unknown ca (https://my.host.name)
Since the Subversion client is pretty complex in its own right, I decided to start debugging with a browser first. But it showed the same error message as before. I think I’m going to need to set up a correct CA root certificate.
I got the root certificate out of the Windows certificate store. When I examined the digital ID in that store via Control Panel / Internet Options / Content / Certificates / Personal, the certification path shows it is signed by “VeriSign Class 1 Individual Subscriber CA – G3”, which in turn is signed by “Verisign Class 1 Public Primary Certification Authority – G3”. I looked for the first one in the Intermediate Certification Authorities store and exported it (in the base 64 format) to C:\Temp\intermediateca.cer., uploaded it to the server and put it in /etc/httpd/conf/myca.cer. After restarting the server, I tried again in the browser.
Success! It asked me for a certificate, and showed me the one I had installed from Versign. I clicked okay, and… Failure! Same error message as before.
So I repeated the steps, but with the second, full root certificate from the Trust Authorities Windows certificate store. And this time, it worked! In Chrome. Not in Internet Explorer at first; I had to close all of its windows and start over. But the Subversion client still doesn’t work.
Step 5: Configure the Subversion client
The problem turns out to be that the Subversion client doesn’t trust the testcert.pfx certificate I provide. This doesn’t make any sense to me; why would I provide the certificate if the client shouldn’t trust it? It’s the server that needs to know it’s trustworthy, not the client.
Well, it doesn’t matter what I think, the Subversion client isn’t going to authenticate with the server unless is decides that the certificate was issued by a trusted certificate authority. So I had to provide a root certificate and tell the Subversion client where to find it.
I’ll cut to the chase. The configuration file that needs to be edited in C:\Users\myname\AppData\Roaming\Subversion\servers. There’s a commented out line in it like the following:
# ssl-authority-files = /path/to/CAcert.pem;/path/to/CAcert2.pem
I uncommented it and changed it to:
ssl-authority-files = /Temp/intermediateca.cer
(Recall that I exported that file from the Windows certificate store in the previous step.) Then I tried using the Subversion client to do an update, and it finally worked. Unlike the server, it wanted the immediate parent certificate of my client certificate, not the ultimate root.
I still think requiring the root certificate on the client software is odd, but it turns out that Firefox works the same way. If I want to browse my Subversion repository I need to import both the client certificate and the root certificate to Firefox first.
The client is authenticating with the certificate, but it will accept any certificate from VeriSign right now, not just the ones I specifically want. And the repository doesn’t know who is authenticating; the Subversion “blame” listing leaves the user blank. I’ll look into dealing with both those issues in a future post.