Thursday, February 6, 2014

Subversion write-through proxying using a ssh tunnel.

Subversion is an open source centralized version control system. Because of its centralized nature, all commits to slave repositories must flow up to the master server and then the slaves are synchronized with the latest commit. (Commits directly to the master will also trigger a sync to all of the slaves.) The configuration of the master and slaves as described by the Subversion manual is summarized as follows:
  1. Add add the special SVNMasterURI directive in the httpd.conf file on all the slave servers. This will trigger any commits to any of the slave servers to push the change to the master.
    <Location /svn>
      DAV svn
      SVNPath /var/svn/repos
      SVNMasterURI http://master.example.com/svn
      …
    </Location>
    
  2. Configure all the slave httpd.conf files to allow connections from the master on a different url without the special SVNMasterURI directive to avoid an infinite recursion of calls from the master to slave to master to slave to master to ... This allows unrestricted access to the repository. The only security measure is to block all IP addresses except calls coming from the master server’s IP address.
    <Location /svn-proxy-sync>
      DAV svn
      SVNPath /var/svn/repos
      Order deny,allow
      Deny from all
      # Only let the server's IP address access this Location:
      Allow from 10.20.30.40
      …
    </Location>
  3. Sync the repository on the slave server manually to build up the initial slave repositories.
  4. Configure the master server’s post-commit and post-revprop-change hook scripts to invoke svnsync on the master to sync each slave server through the “super secret” repository url only the admin is supposed to know about.
  5. Hope nobody ever want to do locking. Write resignation letter in anticipation and leave in upper desk drawer.
This sort of works securely and reliably if:
  • The IP address of the master never changes.
  • The IP address of the master server is not port-forwarded from behind a NAT.
  • Nobody finds a way to spoof the IP address from the master.
Those are some pretty big ifs.

A better option would be to create a secure tunnel from the master to the slaves so that the svnsync commands can be executed on the slave servers using the slave server’s svnsync binary. This can be accomplished through ssh.

Public Key SSH Login


The first step is to set up ssh so that the master can ssh to the slaves without entering a password.
  1. Generate public and private keys on the master. This command will generate two files in the ~/.ssh directory called id_dsa and id_dsa.pub. Do not enter a passphrase. Leave it blank.
    ssh-keygen -t dsa
  2. Append the contents of the master’s ~/.ssh/id_dsa.pub file into the file ~/.ssh/authorized_keys on the slaves. If the ~/.ssh/authorized_keys doesn’t exist on the slave, copy the master’s ~/.ssh/id_dsa.pub file to the slave and rename it to authorized_keys.
  3. Fix permissions on the slave’s authorized_keys file, if necessary.
    chmod 600 authorized_keys
  4.  Test that the user associated with Subversion can ssh from the master to the slaves without entering a password.

Modified Hook Scripts


The hook scripts specified in the Subversion manual will be altered to run the svnsync commands on the slave through the ssh tunnel instead of directly from the master server. This might have an added bonus of working better when a mismatch between the slave and master version of Subversion occurs. The post-commit hook would be:
#!/bin/sh
#
REPOS="$1"
REV="$2" 

ssh -l root slave1.example.com "/path-to-bin/svnsync  \
              sync --non-interactive \
              file://${REPOS} > /dev/null 2>&1"

ssh -l root slave2.example.com "/path-to-bin/svnsync  \
              sync --non-interactive \
              file://${REPOS} > /dev/null 2>&1"

ssh -l root slave3.example.com "/path-to-bin/svnsync  \
              sync --non-interactive \
              file://${REPOS} > /dev/null 2>&1"
and the post-revprop-change hook script would be the same but with copy-revprops swapped in for the sync parameter. In this example, I’m logging into the slaves as root, but it can be changed to whatever user is appropriate for your configuration.

The advantage of this method is that changes in the DNS for the master (or slaves) don’t require an update of the configuration files. The slaves don’t need to have a “security hole” configured into the slaves’ Apache web server in order for the synchronization to occur. All the connections are secure and require public key encryption.

2 comments:

  1. how to create proxy-sync params for repository in the "Location" directive as below
    "
    DAV svn
    SVNPath /var/svn/repos
    Order deny,allow
    Deny from all
    # Only let the server's IP address access this Location:
    Allow from 10.20.30.40

    "

    ReplyDelete
    Replies
    1. It somewhat depends on which Subversion build you're using. Collabnet?

      Delete