Easier Capistrano Deployments from Github

A couple of weeks ago we at Edgecase decided that we would like to start using Github as our main repo for our git versioned projects. I really wanted to take advantage of the great user experience and the administration aspects that Github had to offer. As we started the process of moving our projects to Github we ran into a bit of an issue that took a little “tweaking” to get right, but in the end I think we came out with nice solution.

Github provides you the concept of “deploy keys”, SSH public keys which essentially provide the user of this key pair read-only access to your project. For simple deployments this is perfect. There’s a bit of a catch though: deploy keys must be unique from project-to-project and they must not collide with any user key on all of Github. At first this wasn’t a problem. We were initially using Github for some project specific gems and it worked fine. However when we started deploying applications we ran into a couple issues:

  1. For most of our projects, we work from one staging server running multiple applications. The project-to-project uniqueness of keys constraint is then a problem.
  2. Our current projects use gems/plugins that we’re hosting on Github. The same project key constraint applies here too.

Using Google as my guide, I managed to find a few ways that didn’t work.

  • Using a specially crafted .ssh/config on the server. Since the hosts aren’t unique per project, configuring your ssh client to respect different key files for each project seems impossible.
  • Being able to choose a specific public key to use with git at runtime. This seems like it should be possible, but I was unable to find any reference to it.

The solution I ended up with I had actually tried on the “specially crafted .ssh/config” attempt, but I was missing a key step when running on OS X Leopard. SSH Agent Forwarding1 to the rescue! This solution is actually really nice because it means that making the server deployable requires zero configuration because it’s likely that the person that is deploying the application already has public key access to the repo. There’s two ways to you can go about doing this. The first requires having each user that needs to deploy edit their .ssh/config like so:

Host serveriamdeployingto.com
    ForwardAgent yes

But that requires a lot of manual editing of files. With Capistrano, we can make this simple for everyone by adding the following line to your deploy config:

set :ssh_options, {:forward_agent => true}

Unfortunately, and I can’t speak for Linux or Windows, but for OS X Leopard there’s one more thing that needs to be done. For what I have to assume are “security purposes” the ssh-add command needs to be run every time that you reboot. For those of you trying to beat the record for longest uptime, it occurs to me that you may need to run ssh-add after a certain amount of time (the man page makes references to key lifetime), but I haven’t run into this issue personally. Luckily Capistrano 2.x’s event callbacks provide an easy way to ensure we have the key in our agent by adding the following to our deploy config:

on :start, :run_ssh_add
task :run_ssh_add do
    `ssh_add`
end

# or more simply

on :start do
    `ssh-add`
end

Badda bing! You’re deploying from Github hopefully with zero issues.

1 http://www.unixwiz.net/techtips/ssh-agent-forwarding.html#fwd – “An Illustrated Guide to SSH Agent Forwarding”