Journal Articles

CVu Journal Vol 17, #5 - Oct 2005 + Project Management
Browse in : All > Journals > CVu > 175 (15)
All > Topics > Management (95)
Any of these categories - All of these categories

Note: when you create a new publication type, the articles module will automatically use the templates user-display-[publicationtype].xt and user-summary-[publicationtype].xt. If those templates do not exist when you try to preview or display a new article, you'll get this warning :-) Please place your own templates in themes/yourtheme/modules/articles . The templates will get the extension .xt there.

Title: Setting up a Subversion Server for Remote Use

Author: Administrator

Date: 02 October 2005 06:00:00 +01:00 or Sun, 02 October 2005 06:00:00 +01:00

Summary: 

Revision control is a critical part of any significant development project. Having secure full time access to your repository from wherever you are can be important. In some environments, such as open source projects, it's absolutely crucial to the functioning of the development team.

Body: 

Revision control is a critical part of any significant development project. Having secure full time access to your repository from wherever you are can be important. In some environments, such as open source projects, it's absolutely crucial to the functioning of the development team.

My first exposure to the Subversion [subversion] revision control system was when I was searching for something to replace CVS for the Scribus project, whose CVS server I administer. There were a number of problems with our use of CVS over ssh, namely server security concerns, cross platform issues, and configuration complexity.

After some research, I decided that Subversion was a good candidate to replace CVS. Subversion is a fairly new revision control system with the stated goal of being a "better CVS than CVS." One particularly attractive feature is the use of HTTPs for a secure, fast, encrypted transport that eliminates the need for an SSH tunnel. Additionally, Subversion is becoming increasingly popular in the open source development community, so more and more useful tools and graphical clients are becoming available.

I'll be covering how to set up a Subversion server for your team, so they can work on your code wherever they are without introducing major security risks. Specifically, I'll be explaining how to set up a Subversion server configured for use with WebDAV over HTTPs, using SSL client certificates for an additional layer of authentication. The goal is revision control that's fast, secure, and easy to use from anywhere. I'll be assuming that you're using a UNIX variant, but it should be quite possible to set all this up on Mac OS X or even Windows NT/2k/2k3 as well.

This article won't try to explain how to use Subversion, why you might want revision control, basic UNIX command line use, or any related topics. We're going to focus specifically on setting up reasonably secure remote access to a subversion repository. As this article also avoids going into extreme depth on the use of subversion and other finer points of its configuration, it is strongly recommended that you examine the excellent Subversion book [svnbook] (freely available on-line) for more detail.

It is important to note that your author is not a security expert. I am an experienced system administrator who has operated Internet-accessible systems hosting public services for some time, but beyond that has no specific security qualifications. This article does not provide some magic recipe for a secure server configuration - but it should help you get started along the way.

Why use Client Certificates?

The use of client certificates provides an extra layer of authentication. A user can't even attempt to authenticate against your svn server until they've provided a client certificate that you can verify is signed by your CA and has not been revoked. For a bit of extra security, you can also store client certificates separately from the client host - on a USB key or potentially even a smart card - and remove them when not in use.

Client certificates are also useful for controlling access to more than your source code repository. They can be used to help secure SSL/TLS-based mail services such as encrypted IMAP, POP3, and SMTP. They are also useful if you wish to offer controlled HTTPs-based remote access to your organization's intranet web server for roaming users. You can also use the same client certificate infrastructure to permit users to encrypt and/or sign email within the organization using S/MIME (though recipients who have not imported your CA certificate won't be able to verify signed mail).

What You'll Need

First, you'll need a server with the Apache 2 web server, the mod_dav_svn Apache 2 server module, and the subversion tools installed. On Debian GNU/Linux 3.1, just apt-get install apache2 libapache2-svn subversion. For other platforms, if you're unsure how to go about installing Apache 2 or mod_dav_svn, the Subversion web site [subversion] has plenty of information.

OpenSSL [openssl] will be required to create certificates, so unless you have an existing CA and x.509 PKI scheme you'll need to install that.

If you want to use your repository from anywhere, you'll need an Internet-accessible IP, or a port forwarded through your firewall. You can use a VPN if you prefer to further limit the accessibility of your server.

Certificate Creation and Management

The creation and management of SSL certificates can be a complicated business. Once it's familiar you will find that it's not generally an issue, but the initial process can appear somewhat daunting.

If your organization has existing X.509 based PKI infrastructure, you may well be able to use your existing client certificates and CA certificate to control access to your Subversion server. Should you be so lucky, you can escape the need to deal with OpenSSL. Similarly, if you have an existing server certificate, you can use that rather than creating your own. It doesn't have to be signed by the same CA as your client certificates.

To create your certificate authority, client certificates, and your web server certificate, you can use the OpenSSL tools. There is not enough space to discuss this in the detail it demands, but I can provide some brief coverage of the procedure. Alas, OpenSSL is rather sparsely documented (especially on the broader scale), and does not ship with any suitable references for most tasks. Your author is no expert on SSL in general, or on OpenSSL in particular, being just a lowly system administrator and programmer. Errors are possible, so do be careful.

Some useful additional information can be found in the Apache mod_ssl FAQ [modssl], at pseudonym.org [pseudonym], and for Windows at stunnel.org [stunnel].

I'll be assuming that you have OpenSSL already installed, as per the requirements above.

Creating a Certificate Authority

Before you can create client certificates, or useful server certificates, you need to obtain a signing certificate. It is possible to buy such certificates from SSL certificate vendors, but this is unnecessary if you only intend to use the certificate scheme within your organization and with your own users. You can simply create your own certificate authority with a self-signed certificate. The only significant limitation of such a certificate is that it must be installed in clients before they will recognise the validity of certificates you have signed.

I suggest that you set up your certificate authority on a different host if possible, and in as secure a location as you can arrange. Removable external media may be worth considering.

First you need to create a suitable openssl.cnf, the configuration file that will drive your CA. The OpenSSL distribution ships with a sample file that you can customise. Alas, if you got your copy of OpenSSL through a vendor package library, this could be almost anywhere on your system. If you can't find it, you can download a sample config file from the OpenSSL project's public CVS browser [getfile].

Now we need to make a directory to store your CA in, and put a copy of openssl.cnf in it. I'm going to refer to this directory as CA hereafter. With that done, openssl.cnf needs to be customised to your site. Start with the section [ CA_default ], setting dir to the path to your CA directory. This tells OpenSSL where to put (and look for) the various files and directories used when managing your CA. I tend to use an absolute path, but if your CA will be stored on removable media where the path may not be constant, you can use . (the current directory) instead, then always work by changing into the CA directory.

Next, under [ req_distinguished_name ], adjust the _default parameters to suit your site. You can add _default parameters to options that do not have them, or remove them if you don't want to provide defaults for a parameter.

To finish our preparation, create the structure to store the CA's various information by creating the subdirectories certs, newcerts, crl and private within your CA directory, then create a file called serial (no extension) containing only the digits 01, and create an empty file called index.txt . Be sure to set the access permissions on private so that only the user who will be managing the CA can see its contents or modify it.

It's finally time to create the CA certificate that signs all your client certificates and lets you verify them later. Pick a good pass phrase to use on your CA's key, and record it somewhere secure and offline. Now create the self-signed certificate that you can use as your CA, supplying the pass phrase you decided on when prompted:

openssl req -new -x509 -keyout private/cakey.pem 
   -out cacert.pem -days 365 -config ./openssl.cnf

When prompted to enter details for the certificate, you should enter the details you wish to appear if a user queries the CA certificate (eg in a browser). As such, Common Name should generally by set to the organization name, not the name of the creator of the certificate. Note that the certificate was created with an expiry date one year from today. You can extend the expiry date of the CA certificate as it approaches expiry with:

mv cacert.pem old_cacert.pem
openssl x509 -in old_cacert.pem -days 365 
   -out cacert.pem -signkey private/cakey.pem

Note that clients will treat certificates signed by an expired root certificate as invalid, and must import the updated root certificate. As such, you may wish to choose a reasonably long validity period.

You can examine your CA certificate by dumping a human-readable version with:

openssl x509 -in cacert.pem -text

If that looks alright, you've created your CA. You're now ready to start creating and signing certificates. Make a backup of your CA directory somewhere secure, safe, and off-line, then copy your CA certificate file (but absolutely not the CA key) to somewhere that Apache 2 can access it. Apache needs the CA certificate to verify that client certificates were really signed by your CA.

For security reasons, it is crucial that you do not keep your CA key on the Subversion server. Put it somewhere safe, preferably on encrypted storage that's not connected to the Internet. I favour the use of a small old laptop that's kept in a safe when not in use, but some might accuse me of excessive paranoia. No matter where you store your CA, remember to keep a backup somewhere safe and secure, such as on CD or tape.

Creating a Server Certificate

If you don't have an existing SSL server certificate for the host you want to run your Subversion server on, you need to create one. It is necessary to first create a certificate request, then sign that request with your CA. Be sure to provide the DNS name of your Subversion server in the common name field of the request, otherwise clients will be warned every time they try to connect to your server. You must use the publicly visible DNS name of the server, rather than its internal host name. In the following, replace new with your host name, eg myhostname_req.pem for new_req.pem.

Here I show the creation of a key without a pass phrase. This means that the key can be used by anybody who obtains it. It is possible to use a key with a password on a web server, but with Apache the password must be entered interactively. This interacts rather poorly with log rotation scripts, and means that if your server ever goes down it won't come back up without manual intervention. I dislike the use of an unencrypted private key, but have found no viable alternative for my use. If you can afford the possible issues involved with using an encrypted key, then I encourage you to use one - simply add -des3 to the first command line below.

Create a private key (append -des3 to encrypt the key):

openssl genrsa -out new_key.pem

Create a certificate request using your key:

openssl req -new -key new_key.pem 
   -out new_req.pem -days 360 
   -config ./openssl.cnf

then sign it with your CA:

openssl ca -policy policy_anything 
   -out new_cert.pem -config openssl.cnf 
   -infiles new_req.pem

If all has gone well, you should have a server certificate. Check it with:

openssl x509 -in new_cert.pem -text

to ensure that it's correct, then copy new_cert.pem and new_key.pem to somewhere Apache can access them, and save backup copies somewhere secure, safe, and off-line. Be sure to set the permissions on newkey.pem so that only the Apache user can read the file, and nobody can modify it. You can now discard new_req.pem.

Creating the Client Certificates

With a working CA established, you're equipped to create and sign client certificates for use by your users. While it's possible to get users to make their own certificate requests, I'll assume you'll be doing that for them then supplying them with a pre-made certificate. The first stage of the procedure for making a client certificate request is actually the same as that for the server certificate described above, except that you should provide the user's name and email address for the common name and email fields, respectively. You only need the certificate and key files temporarily, so there is no need to save them anywhere.

Once you have the certificate for the user, created the same was as the server certificate above, you need to convert them to PKCS#12 format, a "packaged" certificate format that most clients understand. You can bundle the CA certificate into this package so that it's automatically imported by most client software when it loads the PKCS#12 certificate. I suggest you save the certificate with a suitable name that makes it easy to identify the owner of the certificate later, such as firstname.lastname_at_domain.p12.

To do this, assuming your user's temporary certificate and key files are in new_key.pem and new_cert.pem respectively, run:

openssl pkcs12 -in new_cert.pem 
  -inkey new_key.pem -certfile cacert.pem -out
  user_name.p12 -export -name "User's
  Subversion certificate for MyOrganization"

If you encrypted the user's key, you will be prompted for the password to decrypt it. You will then be prompted for a password to encrypt their new PKCS#12 file with. This is the password you will need to supply to the user for them to use their new client certificate.

Once the PKCS#12 file has been created you can discard new_cert.pem, new_key.pem, and new_req.pem.

Setting up a Test Repository

For the purposes of this article, it's best if you create a new subversion repository to work with. You should probably work on a dummy repository before going live with your server even if you have an existing one or plan to convert from CVS.

Setting up a new repository is simple. Assuming that you want it to live in /var/svn:

# mkdir /var/svn
# svnadmin create /var/svn fsfs

Now let's add a dummy module for testing. First, create the files to import in some temporary directory:

# mkdir testproject
# echo 'It worked!' > testproject/test.txt

then import the temporary testproject directory into the new repository:

# svn import testproject
   file:///var/svn/testproject/trunk 
   -m "first import"

You can now discard the testproject directory.

The Subversion website has detailed documentation on how to create a repository, import sources, or convert a CVS repository to Subversion using cvs2svn, so I'm not going to discuss it in any more detail here. You might want to look up the Subversion book [stunnel] if you're unsure how to proceed when it comes time to get your project's live code into svn.

Setting up the Web Server

When using client certificates, it is generally be best to set up your Subversion server in a separate Apache 2 virtual host running on a non-standard port. This is necessary because of limitations in the SSL implementation of common clients.

Apache installations and configuration specifics differ a huge amount across different OSes and even Linux distributions. Consequently I can discuss the general configuration approach to take, but not necessarily all of the specifics of what files to edit and what to put where. You may need to adapt the sample configuration discussed below to suit your system.

In the following configuration I make the assumption that your repository is in /var/svn, and you'll use /var/www/projectname for things like WebSVN[12]. You can use whatever paths you prefer, so long as you configure Apache accordingly. First, it may be necessary to add or uncomment the configuration directives to load mod_dav and mod_dav_svn:

LoadModule dav_module path/to/mod_dav.so
LoadModule dav_svn_module path/to/mod_dav_svn.so
LoadModule authz_svn_module path/to/mod_authz_svn.so

The exact paths to the modules will vary depending on your Apache and mod_dav_svn installation. If your Apache is not already configured for SSL, you may also need to uncomment or add a directive such as:

LoadModule ssl_module path/to/mod_ssl.so

To actually configure the Subversion server you'll need to add something like this to your Apache 2 configuration:

# Tell Apache to listen on port 4430 for connections
Listen 4430
# And set up a virtual host to handle connection on # that port:
NameVirtualHost *:4430
<VirtualHost *:4430>
  # Set up SSL for the virtual host. 
  SSLEngine on
  # The CA certificate file that you'll be
  # validating client certificates against:
  SSLCACertificateFile /path/to/your/cacert.pem
  # The server certificate the web server will
  # identify its self with. If you have an existing
  # SSL virtual host, you can use that certificate
  # for this virtual host too (just specify
  # the same path here). It need not be signed by
  # the CA specified above.
  SSLCertificateFile /path/to/your/servercert.pem
  # Only set this if you have a separate key file
  # for your server certificate:
  SSLCertificateKeyFile /path/to/your/serverkey
  # Require clients to have a certificate signed by
  # one of the CA certificates specified earlier:
  SSLVerifyClient require
  SSLVerifyDepth 1

  # Require a valid username and password to access
  # any part of this virtual host, and make the
  # default access control "deny".
  <Directory />
    # Only talk to clients using SSL
    SSLRequireSSL
    # Don't permit the use of .htaccess files to
    # override these settings
    AllowOverride None
    # This tells Apache to use BASIC password
    # authentication. You can rely purely on client
    # certificates if you wish to - look up 
    # FakeBasicAuth in the mod_ssl documentation.
    # You can also authenticate against a database,
    # or against LDAP, if you prefer - see the
    # Apache documentation. Here BASIC
    # authentication with a simple password is used.
    AuthType Basic
    AuthName "ProjectName SVN"
    # Your password file. Put this somewhere safe,
    # and outside the DocumentRoot configured below.
    AuthUserFile /path/to/apache2/config/
       projectname_svn_htpasswd
    # Reject access from users who give no / wrong
    # passwords.
    Require valid-user
    # and, for the root directory, reject all
    # access. This is overridden in later
    # subsections.
    Order deny,allow
    deny from all
  </Directory>

  # Tell Apache to look for files starting in
  # /var/www/projectname
  DocumentRoot /var/www/projectname
  # And permit any authenticated user access to the
  # files under it.
  <Directory /var/www/projectname>
    Order allow,deny
    Allow from all
  </Directory>

  # Set up the Subversion server, giving it the
  # virtual location "/svn" in URLs.
  <Location /svn>
    # Turn on the svn server
    Dav svn
    # Tell it our repository is in /var/svn
    SVNPath /var/svn
    # Uncomment the following line to enable Authz
    # Authentication
    # AuthzSVNAccessFile /etc/apache2/dav_svn.authz
    # Permit access to this location (still requires
    # valid user and client cert as specified
    # earlier).
    Order allow,deny
    Allow from all
  </Location>
  # Enable gzip compression if available
  <ifmodule mod_deflate.c>
  DeflateBufferSize 8096
  DeflateCompressionLevel 9
  SetOutputFilter DEFLATE
  SetInputFilter DEFLATE
  </ifmodule>
</VirtualHost>

You may need to edit the existing virtual host directives to explicitly specify the port they listen on (80 for HTTP, 443 for HTTPs) using the same form as shown above.

If you want to offer anonymous read-only access to your repository (common for Open Source projects) then you can add a section like this to your normal HTTP and/or HTTPs virtual host(s):

# Anonymous read-only access to the repository
<Location /svn>
  Dav svn
  SVNPath /var/svn
  <LimitExcept GET PROPFIND OPTIONS REPORT>
    Order deny,allow
    Deny from all
  </LimitExcept>
</Location>

You will probably want to enable gzip compression as well, as shown in the main configuration listing. Using gzip compression on the server saves bandwidth and results in faster checkouts, but at the cost of some server CPU time.

You should now be done configuring the web server. Test your configuration's syntax with apachectl -t (apache2ctl -t on some systems) and restart Apache 2.

Final Server Configuration

Before you can test the newly configured Subversion server, you must ensure that your repository is writeable by the web server. The exact procedure for this depends on if you plan to offer access to your repository using other methods too. Assuming that you'll only be using the repository via the Apache-based Subversion server and you're running under a modern UNIX, chown -R webserverid /var/svn (where webserverid is the user ID your apache2 is running under) should do the job. If you're not sure what user ID apache is running under, the first column of the output of ps aux | egrep '(http|apache)' should show you.

Red Hat Fedora users need to be aware of of SELinux, which can interfere with the operation of your Subversion server. If you find that you are getting "permission denied" or "403 Forbidden" errors that make no sense, the chances are good that SELinux is involved. Reconfiguring SELinux is beyond the scope of this article. For testing purposes only you can disable it using setenforce Permissive as root. Consider tweaking the SELinux configuration for Apache 2 rather than permanently disabling SELinux, since SELinux provides additional isolation between the various network services on your system.

Adding Users on the Server

Since you're using HTTP BASIC authentication with a plain password file, you need to add some users to the password file. To set up the password file, use this command:

# htpasswd -c /path/to/htpasswd_file username-to-add

where /path/to/htpasswd_file is the same as the path you gave in your Apache 2 configuration. Now ensure that the file is readable but not writeable by Apache:

# chgrp apachegroupid /path/to/htpasswd_file
# chmod 640 /path/to/htpasswd_file
and start adding more users with:
# htpasswd /path/to/htpasswd_file 
   username-to-add

Setting up the Client

If all has gone well, you're now ready to test out the new server by connecting with a subversion client.

First, you need to install your PKCS#12 format client certificate. Exactly how to do this depends on what subversion client you are using. With the command line client on UNIX, I tend to put the certificate file into $HOME/.subversion. I then edit $HOME/.subversion/servers, adding a line like mysvn = hostname.of.my.svn.server to the [groups] section, and add a new section for that server:

[mysvn]
ssl-client-cert-file = /path/to/client/cert/file.p12
# If you want to have svn remember the password to 
# your cert file, set this. Since you're using BASIC
# auth as well, this is generally fine. Many GUI svn
# clients don't seem to be able to prompt for a
# certificate password, so saving it here also helps
# to avoid confusing those clients.
ssl-client-cert-password = yourPasswordToSave

At the time of writing the Subversion client did not appear to read the CA certificate out of the PKCS#12 client certificate file. As such, you need to provide cacert.pem to your users if you created your SSL server certificate using your self-signed CA. This can be skipped if you bought a server certificate from one of the major certificate authorities.

To install the CA cert, copy it to:

$HOME/.subversion/myorganization.pem

then edit the [ global ] section of $HOME/.subversion/servers and add a line such as:

ssl-authority-files = /path/to/myorganization.pem

Testing

With that configuration done, you're ready to check out the module you created earlier. Using the command line subversion client:

svn checkout https://username@hostname:port/svn/
   testproject/trunk testproject

Naturally you'll need to adjust the URL above to use your user name in the Apache password file, svn server host name, and server port.

You should be prompted for your password, then the checkout should complete, leaving you with the same testproject/test.txt file you checked in earlier. You can now work within your checkout directory as if it was your usual local source tree, then commit changes back to the repository with svn commit. Subversion will remember the repository URL, so you don't need to specify it when working within a checked-out tree.

If you install your client certificate into your web browser (in Firefox: Edit->Preferences, Advanced, Manage Certificates, import, then edit the CA cert under Authorities and mark it as trusted), you should be able to explore the repository by visiting https://hostname:port/svn. This can be useful when troubleshooting configuration problems.

Locking it Down

In the opinion of the author, there's almost no such thing as enough paranoia when securing your source code repository. That's doubly true if you're making it accessible over the Internet. It's well worth looking into hosting the repository on a dedicated server or isolated OS instance, limiting access by IP address, putting the server behind a VPN gateway, storing client certificates on external media, etc. Naturally, you'll also want to keep the server and the clients up-to-the-minute with all security patches. Don't forget to make backups - and to periodically archive the backups.

Remember that your clients are important too. It's easier to clean up from an attacker committing to your repository through a client than it is from a server compromise, but if their changes are not recognised as from an impostor, you still risk shipping malicious code to users.

When it comes to server security, the more you can isolate the Apache 2 / Subversion service from whatever else might be running on the machine the better. An entirely separate Apache 2 instance running under a different user ID just for svn may be worth considering if your server also runs Apache for other public services. A physically separate server or OS a separate instance is even better. Linux users may wish to investigate Xen [xensource] or VMWare [vmware] for isolating OS instances on the same hardware; Solaris users may want to consider using zones [sun]. The better your svn server and repository are isolated from any other potential sources of attack, the less impact a service compromise is likely to have.

There's no such thing as a secure computer - especially when exposed to the Internet - and this article can't teach you even a fraction of how to truly secure things. Don't just follow what's described here, but try to go out and get help locking your server down as tight as possible.

There's little worse than discovering that the system hosting your source code repository has been compromised, so you should probably do everything you can to make sure that it doesn't happen, and that if it does you're prepared and able to quickly recover. "Sorry, we just shipped a rootkit/virus/trojan with our last product" isn't an announcement you ever want to have to make.

Subversion and the Scribus Project

The Scribus project is still using CVS+SSH. Our tests of Subversion were quite successful, but many of the team want graphical merge and history browser tools that they didn't feel to be sufficiently mature. As the KDE project has moved to Subversion, I'm hoping to see some improved svn clients available soon, after which I hope it will be possible to migrate the project to Subversion.

Notes: 

More fields may be available via dynamicdata ..