<= Home
Enterprise Capistrano: Deploying to Servers With Unique Credentials
Capistrano makes an assumption that all servers in your application stack share the same credentials. As much logical sense as this makes, in large enterprise landscapes it is often not a reality. The nature of this world (Enterprise) is that trying to shift the mindset of all shareholders (infrastructure, support teams, security, etc) and then changing long running practices is not always feasible. This is especially true when doing so on numerous severs servicing numerous other systems that depend on the already in place mismatched credentials. For us...we have our normal application servers and database servers that DO share matching credentials. But along with them we use a separate server env to store and host our assets (images, css, js) with its own credentials.
Still desiring to leverage capistrano for its core automation benefits we had to monkey-patch capistrano so we could supply server specific username and passwords. Drop this in your lib folder or somewhere else more relevant. WARNING: This is basically cut-paste from the capistrano internals with some added sugar - I take no responsibility for the class design and multiple responsibilities in this rather big method.
#This is a core class in the capistrano library. it is overridden so that we can support servers that have different user names and passwords
module Capistrano
class SSH
class << self
alias :original_connect :connect
def connect(server, options={}, &block)
special_server_settings = options[:server_authentication] ? options[:server_authentication][server.to_s] : nil
if special_server_settings.nil?
return original_connect(server, options, &block)
else
methods = [ %w(publickey hostbased), %w(password keyboard-interactive) ]
password_value = nil
ssh_options = (options[:ssh_options] || {}).dup
ssh_options[:username] = special_server_settings[:user] || server.user || options[:user] || ssh_options[:username]
ssh_options[:port] = server.port || options[:port] || ssh_options[:port] || DEFAULT_PORT
begin
connection_options = ssh_options.merge(
:password => password_value,
:auth_methods => ssh_options[:auth_methods] || methods.shift
)
connection = Net::SSH.start(server.host, connection_options, &block)
Server.apply_to(connection, server)
rescue Net::SSH::AuthenticationFailed
raise if methods.empty? || ssh_options[:auth_methods]
password_value = special_server_settings[:password]
password_value = password_value.call if password_value.is_a? Proc
retry
end
end
end
end
end
end
Your capfile will look something similar to this:
role :app, 'thisapp.rocks.com'
role :web, 'thisapp.rocks.com'
role :db, 'thisdb.rocks.com'
role :assets, 'my.asset.server01.com'
set :user, 'the-primary-user'
set :server_authentication, { 'my.asset.server01.com' => {
:user => 'assetusr' ,
:password => 'password'}}
}
....