Tuesday, January 10, 2012

a ruby wrapper for 'svn switch'

A while ago our local network admins decided static IP numbers were a Bad Thing, causing chaos for me when trying to use a machine as a server for subversion (among other things).

I found myself trying to check changes in subversion repositories back in to the server, only to find that the server was no longer at the IP address from which the source had been checked out. The answer (apart from attacking the network support people) is the svn switch command. This is a multi-purpose tool with lots of options, so I wrote a wrapper to simplify the process of changing the repository's root URL and nothing else. It was also an excuse to learn a bit of ruby, which is way cool.

#!/usr/bin/env ruby

    new_server_address= ARGV[0]
    fail "Specify the new server name or IP number" unless new_server_address

    svn_info = %x{svn info}
    fail "'.' is not a working copy" if svn_info == ""

    url_regex = Regexp.new('^URL: (.*$)')
    repository_url = url_regex.match(svn_info)[1].to_s
    fail "Unable to find the URL" unless repository_url

    split_repository_url = repository_url.split('/')
    split_repository_url[2] = new_server_address
    new_repository_url = split_repository_url.join('/')
    puts "Switching from \n#{repository_url} \nto \n#{new_repository_url}"
    puts "Continue? [Y]"
    confirm = STDIN.gets.chomp
    if confirm.upcase == "Y" then
        system("svn switch --relocate #{repository_url} #{new_repository_url}")
    end

Subversion setup

Start by editing /etc/apache2/httpd.conf so that the httpd-subversion.conf file gets included.

Include /private/etc/apache2/extra/httpd-subversion.conf

There's no httpd-subversion.conf file by default, so we need to make one. On my system, it needs to contain the following:

LoadModule dav_svn_module libexec/apache2/mod_dav_svn.so
LoadModule authz_svn_module libexec/apache2/mod_authz_svn.so

<Location /svnrepos>
    DAV svn
    SVNParentPath /usr/local/svnrepos
    # the following svnindex.xsl (and svnindex.css) are located in the
    # server DocumentRoot dir, which is defined in httpd.conf with the following line:
    # DocumentRoot "/Library/WebServer/Documents"
    SVNIndexXSLT "/svnindex.xsl"
    # require SSL connection for password protection
    SSLRequireSSL

    # user authentication
    AuthType Basic
    AuthName "Subversion Repository"
    AuthUserFile /etc/apache2/subversion.auth

    # only authenticated users may access the repository
    Require valid-user
</Location>

The .xsl and .css files were copied from my old server. They're just to make browsing the subversion repo more nice.

The subversion.auth file is generated using htpasswd as follows, and stored in /etc/apache

sudo htpasswd -cm /etc/apache/subversion.auth my_user_name

After the command is issued, a password must be entered (twice).

Test the install by trying to access a subversion repository (presumably in /usr/local/svnrepos, according to the above config). You should be required to use a https connection, and be prompted for your username and password.

Generate SSL keys on Mac OS X Lion

I've done this successfully using a script I wrote a while back. One of the gotchas seems to be that the "Common Name" should be the server name (or static IP number if you don't have one). This must match the ServerName directive in httpd.conf.

#! /bin/bash
echo; echo -e "Generating Certificate Authority (CA) [1]:"
openssl genrsa -des3 -out ca.key 4096

echo; echo -e "Generating Certificate Authority (CA) [2]:"
openssl req -new -x509 -days 365 -key ca.key -out ca.crt

echo; echo -e "Generating Server Key:"
openssl genrsa -des3 -out server.key 4096

echo; echo -e "Generating certificate signing request (CSR):"
echo -e "       ** name entered for Common Name (CN) should match your server name **"
openssl req -new -key server.key -out server.csr

echo; echo -e "Signing CSR with CA:"
openssl x509 -req -days 365 -in server.csr -CA ca.crt \
        -CAkey ca.key -set_serial 01 -out server.crt

echo; echo -e "Making insecure version of server.key for apache startup"
openssl rsa -in server.key -out server.key.insecure

echo; echo -e "Renaming server secure/insecure keys"
mv server.key server.key.secure
mv server.key.insecure server.key

sudo cp server.key /etc/apache2/server.key
sudo cp server.crt /etc/apache2/server.crt

Still seems to work...

In order to get apache to use the ssl certificates it's necessary to change httpd.conf

First uncomment the LoadModule line to get ssl_module to load:

LoadModule ssl_module libexec/apache2/mod_ssl.so

Uncomment the line in httpd.conf to get the httpd-ssl.conf file loaded:

Include /private/etc/apache2/extra/httpd-ssl.conf

Now make some changes to httpd-ssl.conf. This file is in the extra/ directory.

Set the ServerName correctly (and the ServerAdmin email address, if you like)

ServerName name.of.your.server:443
ServerAdmin email.address@nowhere.com

Set the SSLCertificateFile and SSLCertificateKeyFile to the correct file paths:

SSLCertificateFile "/private/etc/apache2/server.crt"
SSLCertificateKeyFile "/private/etc/apache2/server.key"

And that should be it. Try loading https://localhost in a web browser. Check the logs if there's a problem.

Moving redmine to apache

This post follows the last one about installing or migrating redmine and mysql. Picking up from the point where redmine was working through WEBrick...

A couple of extra ruby gems are required to get apache to serve redmine to the world. Passenger does most of the hard work, and when it installs it provides it's own config instructions. Neato!

sudo gem install passenger
sudo passenger-install-apache2-module

The last command is really cool, it does lots of work compiling the apache module. I guess it's something from the rails community. The instructions tell us to add some lines to the apache configuration file. I'm putting them in /etc/apache2/other/passenger.conf. After I'm done, the passenger.conf file looks like this:

# settings from passenger-install-apache2-module
LoadModule passenger_module/Library/Ruby/Gems/1.8/gems/passenger-3.0.11/ext/apache2/mod_passenger.so
PassengerRoot /Library/Ruby/Gems/1.8/gems/passenger-3.0.11
PassengerRuby /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin/ruby

# additional settings
PassengerUserSwitching on
PassengerUser _www
PassengerGroup _www

Set user:group on /etc/apache2/conf/extra/passenger.conf

sudo chown _www:_www /etc/apache2/conf/extra/passenger.conf

And get apache to load the passenger.conf file by adding the following line to httpd.conf

Include /private/etc/apache2/other/*.conf

Redmine runs on apache using a virtual host. Include the httpd-vhosts.conf file by uncommenting the relevant line in httpd.conf:

# Include /private/etc/apache2/extra/httpd-vhosts.conf

Then edit the httpd-vhosts.conf file to get things working. Comment out the example configs (anything referring to *dummy-host*), and add the following config sections:

<VirtualHost *:80>
        DocumentRoot "/Library/WebServer/Documents"
        ServerName localhost
</VirtualHost>

<VirtualHost *:80>
        ServerName your.ip.number.here
        ServerAlias redmine.local
        ServerAlias your_server_name.local
        <Directory /Library/WebServer/redmine>
                Order allow,deny
                Deny from all
        </Directory>
        <Directory /Library/WebServer/redmine/public>
                AllowOverride All
                Options Indexes FollowSymLinks -MultiViews
                Order allow,deny
                Allow from all
        </Directory>
        RailsBaseURI /redmine/public
        RailsEnv production
</VirtualHost>

Restart apache in the terminal with sudo apachectl graceful, or in System Preferences->Sharing (toggle Web Sharing off and on).

Go to http://your.ip.number/redmine/public in a web browser to check if all is working. The first time you load redmine, the server needs to start ruby so it takes a moment, but the response should be quicker after that.

If things go wrong it might be helpful to open the Console to look at relevant logs.