Silmor . de
Site Links:
Impressum / Publisher

Subversion behind an Apache Reverse Proxy

A reverse proxy looks to the outside like a normal web server, but redirects requests to other (possibly invisible from outside) servers.

There are several reasons why one might want to put subversion behind a reverse proxy. The subversion server might be located in the intranet of an organisation, but has to be accessable from the outside or the administrator wants to separate several web-applications for better security, but still wants them to be visible under one server-name, to name just a few.

This tutorial is for Apache 2.x. Subversion needs Apache2.x, but the proxy part might also work with Apache 1.3

Warning: to make this work a little bit of compilation is needed (don't worry, it is not much).

The DAV Protocol

Subversion uses the DAV (Distributed Authoring and Versioning) Protocol uses more methods than standard HTTP (eg. GET, COPY, MOVE) and more headers than standard HTTP. This must be taken into account for any Proxy configuration (be it reverse or standard).

Configuring Apache Proxy

The proxy side of Apache requires mod_proxy to work:

#load the module
LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so
#per default disallow all requests (for security)
ProxyRequests Off
<Proxy *>
  Order deny,allow
  Deny from all
</Proxy>
ProxyVia On

(normally this is already done in the preconfigured packages for your distribution)

Inside the VirtualHost directive for the proxying VHost you need to make known that you want requests for your subversion directory (we'll assume it is called svn) to be relayed to the real subversion server:

ProxyPass /svn/ http://realsvnserver/svn/
<Location /svn/ >
        ProxyPassReverse /svn/ http://realsvnserver/svn/
        <Limit OPTIONS PROPFIND GET REPORT MKACTIVITY PROPPATCH PUT CHECKOUT
               MKCOL MOVE COPY DELETE LOCK UNLOCK MERGE>
          Order Deny,Allow
          Allow from all
          Satisfy Any
        </Limit>
</Location>

The ProxyPass directive tells Apache to redirect requests below /svn to the subversion-Apache (http://realsvnserver/svn). The ProxyPassReverse directive tells it to alter the request headers (Location, Content-Location, and URI) to match the target server - depending on your version of Apache and its configuration you may need to leave out either /svn/ or http://realsvnserver/svn/. If possible the same path should be used on both servers (otherwise DAV might make trouble). The Limit directive tells Apache to let all DAV requests from all clients (Allow) through and let the real subversion server handle authentication (Satisfy).

No other transformations should be necessary (or indeed possible) at this stage - it should of course be avoided to change the body of requests or answers.

Subversion Apache

The Apache handling subversion itself should be set up the same way as you would set it up if it were a standalone server. You should make sure the subversion-path on this server is the same as the one configured for the proxy.

Make sure it works as a standalone server as you intend it to work.

Destination Header

Most subversion operations will now already work through the proxy. Except for commits which copy or move files/directories. This is because the DAV requests COPY and MOVE use the header Destination to contain the full target path of the operation.

Normally this is only a problem if your proxy exposes itself as HTTPS server, while your internal instance serves HTTP (or vice versa). Strangely enough mod_dav validates only the scheme (http vs. https) and not the host name.

You can find out whether you have this problem by creating a test repository and then accessing it through the proxy:

bash$ svn mkdir https://myproxy.tld/svn/test/foo
bash$ svn co https://myproxy.tld/svn/test/foo
bash$ cd foo
bash$ echo hallo > hallofile
bash$ svn add hallofile
bash$ svn commit -m hallo
bash$ svn copy hallofile hallofile2
bash$ svn commit -m hallo2

If the last commit fails with "502 Bad Proxy" then you have this problem - mod_dav does not like your Destination header.

Unfortunately mod_proxy does not handle this header. Even more unfortunately the mod_headers of Apache 2.0 and 2.2 is not flexible enough to handle this case either. So if you are using Apache 2.0 or 2.2 for your subversion server (which is very likely at the time of writing this article), you need to backport mod_headers from the HEAD revision of Apache. Fortunately this is not very difficult for this particular module.

Here is how:

1) Get the current snapshot of Apache trunk from http://cvs.apache.org/snapshots/httpd/ (I used snapshot httpd_20061229171811.tar.gz). Or get my copy.

2) Install the correct development packages for the Apache that you are running (Debian: apache2-prefork-dev) - if you have subversion installed on a production server, it might be a good idea to install these packages on a different machine.

3) untar the snapshot; cd httpd/modules/metadata
or ungzip my file

4) execute the command apxs2 -c mod_headers.c

5) copy .libs/mod_headers.so to the Apache modules directory (Debian: /usr/lib/apache2/modules) - preferably using a new name for it (like mod_headers_too.so)

6) Go into the configuration of your actual SVN-serving Apache (not the proxy - mod_proxy accesses the data stream before mod_headers has a chance to manipulate it) and add this line to add the module to Apache:

LoadModule headers_module /usr/lib/apache2/modules/mod_headers_too.so

(of course you should correct the path to your installation and make sure that the original mod_headers.so is not loaded)

7) in the same configuration, find the part where you configured SVN

<VirtualHost realsvnserver:80>
  Servername realsvnserver
  RequestHeader edit Destination ^https http early

  <Location /svn>
    DAV svn
  #...etc.pp.

The RequestHeader...early directive will exchange the https in the Destination header to http, so mod_dav will not be confused about the scheme.

8) restart the SVN-Apache

Changing https to http in 7) should normally be enough. If not, either change it to exchange "^https://myproxy.tld" for "http://myinternalsvnserv" or add a "ServerAlias myproxy.tld" to the same config file/VHost.

Notes

According to an email from Daniel L. Rall it is possible to set the Destination header using mod_rewrite:

RewriteCond %{HTTP:Destination} .+/(svn/.*$)
RewriteRule ^/svn/.* - [E=MyDestination:http://svn-host:81/%1,PT]
RequestHeader set Destination %{MyDestination}e env=MyDestination

The mod_headers functionality used here has been backported to Apache 2.2.4 - so all versions starting with this one should fully support my configuration without the need to recompile mod_headers.


Webmaster: webmaster AT silmor DOT de