How to Set up SSHKit In Rails

A Primer on SSH Kit

SSHKit is a Ruby gem created by the wonderful folks behind the Capistrano project. SSHKit is designed to run arbitrary system commands on one or more servers in a structured way. It does this by providing a convenient Domain Specific Language (DSL) that allows end users to invoke commands on a server in Ruby code. This server automation is  powerful and avoids having to build custom implementations with the standard Ruby net/ssh package. Let's look at an example.

Suppose you have a fictional online pet food store that you needed to quickly trigger a rebuild for the static website. With SSHKit you could write this in only a few lines of Ruby code as:

# sshkit_pet_store_deploy.rb

require 'sshkit'
require 'sshkit/dsl'
include SSHKit::DSL ❶ 

SSHKit::Backend::Netssh.configure do |ssh| 
  ssh.ssh_options = { keys: %w(/home/user/.ssh/id_rsa) } ❷
end

# Task to update latest static websites for example servers

on ["server1.your-incredible-petstore.com", "server2.your-incredible-petstore.com"] do ❸ 
  within '/var/www/html' do ❹ 
   execute :git, :pull ❺
   execute :yarn :build 
  end
end

SSHKit::DSL is the Ruby module that gives access to the SSHKit's DSL. Once included into this script ❶, you can get access to the variety of helpers SSHKit provides such as "on", "within", "execute", etc. In the above script before actually SSH into the server, the SSH backend is configured with the correct SSH key ❷ that will authenticate the session. After the keys are configured, the script makes use of the DSL "on" method ❸, that receives a list of server host names to SSH into, and a codeblock containing the code to run on those hosts as arguments. Within the on codeblock the script invokes another DSL method within ❹ that changes the current working directory of the session to the provided path --- in this case /var/www/html (a common place for static websites). Inside the within codeblock and directory execute commands are issued ❺ to pull and build the new static website. All of this within 6 lines of execution logic – SSHKit certainly makes use of Ruby's eloquent and expressive style!

Adding SSHKit

With a bit better understanding on what SSHKit is, let's see how simple it is to add it to your Rails application. The first step is, as always, to add the gem to your Gemfile along with ed25519 and bcrypt_pbkdf and run bundle install afterwords:

# Gemfile 
source "<https://rubygems.org>"

# --- snipped --- 
gem "sshkit", "~>1.21.0"
gem "ed25519", "~> 1.2.4"
gem "bcrypt_pbkdf", "~> 1.0.1"
# --- snipped --- 

Without going too in-depth, one of the underlying backends of SSHKit is the Ruby library net/ssh which requires two cryptography libraries to perform its authentication procedures. One library is for the elliptic-curve cryptography Edwards-curve Digital Signature Algorithm (ed25519) and the other for the Bcrypt variant of Password-Based Key Derivation Function 2 (bcrypt_pbkdf). You don't need to worry about how these are used, but,  it is helpful to know what the acronyms mean.

Once you have  installed the gems, we will setup our initializer for SSHKit following the standard initializers pattern:

# config/initializers/ssh_kit.rb

SSHKit::Backend::Netssh.configure do |ssh|
  ssh.ssh_options = {
    keys: ENV["SSH_KEY"] ❶,
  }
end

This setup is nearly identical to the script we analyzed in the primer, the main difference being that we can rotate and change the SSH key through an environment variable ❶ rather than hard coding.

Configuring SSH Kit Environment Variables

We will now configure the environment variables for the Rails app. In specific, we will set up some environment variables in a .env file and inject them into the Rails application configuration so that it can be accessed anywhere in the app.

Note: for the purposes of this post I am assuming you are using something like dotenv to load your environment variables

To begin, add your environment variables to an .env file similar to below:

# .env

# Use your domain
HOST=yourdomain.com
# Use your server's user
USER=root
# Path to the SSH key on your machine to access the server
SSH_KEY=~/.ssh/id_rsa

Once you have the environment file set up you can easily add them to our application by extending the Rails config/application.rb as below:

# config/application.rb

# --- snipped ---

module RailsApp 
 class Application < Rails::Application
    # --- snipped ---
    config.sshkit_host = ENV["HOST"]
    config.sshkit_user = ENV["USER"]
 end
end

Now any application code can simply reference the Rails.application.config object to acquire the correct data from the environment. This creates a clean separation and central location to access environment variables.

At this point you are ready to use SSHKit in your application!