Class: Net::SSH::Gateway

Inherits:
Object
  • Object
show all
Defined in:
lib/net/ssh/gateway.rb,
lib/net/ssh/gateway/version.rb

Overview

A Gateway is an object that allows you to tunnel network connections through a publicly visible host to a host hidden behind it. This is particularly useful when dealing with hosts behind a firewall. One host will generally be visible (and accessible) outside the firewall, while the others will all be behind the firewall, and the only way to access those restricted hosts is by first logging into the publicly visible host, and from thence logging into the restricted ones.

This class makes it easy to programmatically connect to these restricted hosts via SSH. You can either simply forward a port from the local host to the remote host, or you can open a new Net::SSH connection to the remote host via a forwarded port.

require 'net/ssh/gateway'

gateway = Net::SSH::Gateway.new('host.name', 'user')

gateway.open('hidden.host', 80) do |port|
  Net::HTTP.get_print '127.0.0.1', '/path', port
end

gateway.ssh('hidden.host', 'user') do |ssh|
  puts ssh.exec!("hostname")
end

gateway.shutdown!

Port numbers are allocated automatically, beginning at MAX_PORT and decrementing on each request for a new port until MIN_PORT is reached. If a port is already in use, this is detected and a different port will be assigned.

Constant Summary collapse

VERSION =
"2.0.0"
MAX_PORT =

The maximum port number that the gateway will attempt to use to forward connections from.

65535
MIN_PORT =

The minimum port number that the gateway will attempt to use to forward connections from.

1024

Instance Method Summary collapse

Constructor Details

#initialize(host, user, options = {}) ⇒ Gateway

Instantiate a new Gateway object, using the given remote host as the tunnel. The arguments here are identical to those for Net::SSH.start, and are passed as given to that method to start up the gateway connection.

gateway = Net::SSH::Gateway.new('host', 'user', :password => "password")

As of 1.1 there is an additional option to specify the wait time for the gateway thread. The default is 0.001 seconds and can be changed with the :loop_wait option.



55
56
57
58
59
60
61
62
# File 'lib/net/ssh/gateway.rb', line 55

def initialize(host, user, options={})
  @loop_wait = options.delete(:loop_wait) || 0.001
  @session = Net::SSH.start(host, user, options)
  @session_mutex = Mutex.new
  @port_mutex = Mutex.new
  @next_port = MAX_PORT
  initiate_event_loop!
end

Instance Method Details

#active?Boolean

Returns true if the gateway is currently open and accepting connections. This will be the case unless #shutdown! has been invoked.

Returns:

  • (Boolean)


66
67
68
# File 'lib/net/ssh/gateway.rb', line 66

def active?
  @active
end

#close(port) ⇒ Object

Cancels port-forwarding over an open port that was previously opened via #open.



128
129
130
131
132
133
134
# File 'lib/net/ssh/gateway.rb', line 128

def close(port)
  ensure_open!

  @session_mutex.synchronize do
    @session.forward.cancel_local(port)
  end
end

#open(host, port, local_port = nil) ⇒ Object

Opens a new port on the local host and forwards it to the given host/port via the gateway host. If a block is given, the newly allocated port number will be yielded to the block, and the port automatically closed (see #close) when the block finishes. Otherwise, the port number will be returned, and the caller is responsible for closing the port (#close).

gateway.open('host', 80) do |port|
  # ...
end

port = gateway.open('host', 80)
# ...
gateway.close(port)

If local_port is not specified, the next available port will be used.



103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
# File 'lib/net/ssh/gateway.rb', line 103

def open(host, port, local_port=nil)
  ensure_open!

  actual_local_port = local_port || next_port

  @session_mutex.synchronize do
    @session.forward.local(actual_local_port, host, port)
  end

  if block_given?
    begin
      yield actual_local_port
    ensure
      close(actual_local_port)
    end
  else
    return actual_local_port
  end
rescue Errno::EADDRINUSE
  raise if local_port # if a local port was explicitly requested, bubble the error up
  retry
end

#shutdown!Object

Shuts down the gateway by closing all forwarded ports and then closing the gateway’s SSH session.



72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
# File 'lib/net/ssh/gateway.rb', line 72

def shutdown!
  return unless active?

  @session_mutex.synchronize do
    # cancel all active forward channels
    @session.forward.active_locals.each do |lport, host, port|
      @session.forward.cancel_local(lport)
    end
  end

  @active = false

  @thread.join
  @session.close
end

#ssh(host, user, options = {}, &block) ⇒ Object

Forwards a new connection to the given host and opens a new Net::SSH connection to that host over the forwarded port. If a block is given, the new SSH connection will be yielded to the block, and autoclosed when the block terminates. The forwarded port will be autoclosed as well. If no block was given, the new SSH connection will be returned, and it is up to the caller to terminate both the connection and the forwarded port when done.

gateway.ssh('host', 'user') do |ssh|
  # ...
end

ssh = gateway.ssh('host', 'user')
# ...
ssh.close
gateway.close(ssh.transport.port)


152
153
154
155
156
157
158
159
160
# File 'lib/net/ssh/gateway.rb', line 152

def ssh(host, user, options={}, &block)
  local_port = open(host, options[:port] || 22)

  begin
    Net::SSH.start("127.0.0.1", user, options.merge(:port => local_port), &block)
  ensure
    close(local_port) if block || $!
  end
end