Class: ElasticBeans::SSH

Inherits:
Object
  • Object
show all
Defined in:
lib/elastic_beans/ssh.rb

Overview

Opens an SSH connection, tunneling through a bastion host if specified.

Defined Under Namespace

Classes: BastionAuthenticationError, SSHFailedError

Instance Method Summary collapse

Constructor Details

#initialize(hostname:, username:, bastion_host: nil, bastion_username: nil, bastion_identity_file: nil, identity_file: nil, ssh_options: [], command: [], logger: Logger.new('/dev/null')) ⇒ SSH

Returns a new instance of SSH.



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# File 'lib/elastic_beans/ssh.rb', line 6

def initialize(
  hostname:,
  username:,
  bastion_host: nil,
  bastion_username: nil,
  bastion_identity_file: nil,
  identity_file: nil,
  ssh_options: [],
  command: [],
  logger: Logger.new('/dev/null')
)
  @hostname = hostname
  @username = username
  @bastion_host = bastion_host
  @bastion_username = bastion_username
  @bastion_identity_file = bastion_identity_file
  @identity_file = identity_file
  @ssh_options = ssh_options
  @command = command
  @logger = logger
end

Instance Method Details

#connectObject

Opens an SSH connection, tunneling through a bastion host if specified. Spawns a child process using the ssh command-line utility and waits for it to complete.

Raises an error if SSH exits with a non-zero exit status.



32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/elastic_beans/ssh.rb', line 32

def connect
  ssh_args = ["ssh", *ssh_options]

  if identity_file
    ssh_args << "-i"
    ssh_args << identity_file
  end

  gateway = nil
  if bastion_host
    ssh_args << "#{username}@localhost"
    ssh_args += ["-o", "UserKnownHostsFile=/dev/null", "-o", "StrictHostKeyChecking=no"]
    begin
      bastion_options = {auth_methods: ["publickey"]}
      if bastion_identity_file
        bastion_options[:key_data] = File.read(bastion_identity_file)
      end

      logger.info("Connecting to '#{bastion_username}@#{bastion_host}'...")
      gateway = Net::SSH::Gateway.new(
        bastion_host,
        bastion_username,
        bastion_options,
      )
      port = gateway.open(hostname, 22)
      logger.debug { "Opened gateway to '#{hostname}' on port '#{port}'" }
      ssh_args += ["-p", port.to_s]
    rescue Net::SSH::Exception => e
      raise BastionAuthenticationError.new(cause: e)
    end
  else
    ssh_args << "#{username}@#{hostname}"
  end

  logger.debug { "Connecting: `#{ssh_args.join(' ')}'..." }
  pid = Process.spawn(*ssh_args, *command)
  _, status = Process.waitpid2(pid)
  unless status.success?
    raise SSHFailedError
  end
ensure
  gateway.close(port) if gateway
end