This class implements various port forwarding services for use by Net::SSH clients. The Forward class should never need to be instantiated directly; instead, it should be accessed via the singleton instance returned by Connection::Session#forward:
ssh.forward.local(1234, "www.capify.org", 80)
Methods
public class
public instance
Included modules
Attributes
session | [R] | The underlying connection service instance that the port-forwarding services employ. |
Public class methods
Instantiates a new Forward service instance atop the given connection service session. This will register new channel open handlers to handle the specialized channels that the SSH port forwarding protocols employ.
# File lib/net/ssh/service/forward.rb, line 24 24: def initialize(session) 25: @session = session 26: self.logger = session.logger 27: @remote_forwarded_ports = {} 28: @local_forwarded_ports = {} 29: @agent_forwarded = false 30: 31: session.on_open_channel('forwarded-tcpip', &method(:forwarded_tcpip)) 32: session.on_open_channel('auth-agent', &method(:auth_agent_channel)) 33: session.on_open_channel('auth-agent@openssh.com', &method(:auth_agent_channel)) 34: end
Public instance methods
Returns a list of all active locally forwarded ports. The returned value is an array of arrays, where each element is a two-element tuple consisting of the local port and bind address corresponding to the forwarding port.
# File lib/net/ssh/service/forward.rb, line 101 101: def active_locals 102: @local_forwarded_ports.keys 103: end
Returns all active forwarded remote ports. The returned value is an array of two-element tuples, where the first element is the port on the remote host and the second is the bind address.
# File lib/net/ssh/service/forward.rb, line 159 159: def active_remotes 160: @remote_forwarded_ports.keys 161: end
Enables SSH agent forwarding on the given channel. The forwarded agent will remain active even after the channel closes—the channel is only used as the transport for enabling the forwarded connection. You should never need to call this directly—it is called automatically the first time a session channel is opened, when the connection was created with :forward_agent set to true:
Net::SSH.start("remote.host", "me", :forwrd_agent => true) do |ssh| ssh.open_channel do |ch| # agent will be automatically forwarded by this point end ssh.loop end
# File lib/net/ssh/service/forward.rb, line 176 176: def agent(channel) 177: return if @agent_forwarded 178: @agent_forwarded = true 179: 180: channel.send_channel_request("auth-agent-req@openssh.com") do |achannel, success| 181: if success 182: debug { "authentication agent forwarding is active" } 183: else 184: achannel.send_channel_request("auth-agent-req") do |a2channel, success2| 185: if success2 186: debug { "authentication agent forwarding is active" } 187: else 188: error { "could not establish forwarding of authentication agent" } 189: end 190: end 191: end 192: end 193: end
Terminates an active local forwarded port. If no such forwarded port exists, this will raise an exception. Otherwise, the forwarded connection is terminated.
ssh.forward.cancel_local(1234) ssh.forward.cancel_local(1234, "0.0.0.0")
# File lib/net/ssh/service/forward.rb, line 90 90: def cancel_local(port, bind_address="127.0.0.1") 91: socket = @local_forwarded_ports.delete([port, bind_address]) 92: socket.shutdown rescue nil 93: socket.close rescue nil 94: session.stop_listening_to(socket) 95: end
Requests that a remote forwarded port be cancelled. The remote forwarded port on the remote host, bound to the given address on the remote host, will be terminated, but not immediately. This method returns immediately after queueing the request to be sent to the server. If for some reason the port cannot be cancelled, an exception will be raised (asynchronously).
If you want to know when the connection has been cancelled, it will no longer be present in the active_remotes list. If you want to block until the port is no longer active, you could do something like this:
ssh.forward.cancel_remote(1234, "0.0.0.0") ssh.loop { ssh.forward.active_remotes.include?([1234, "0.0.0.0"]) }
# File lib/net/ssh/service/forward.rb, line 146 146: def cancel_remote(port, host="127.0.0.1") 147: session.send_global_request("cancel-tcpip-forward", :string, host, :long, port) do |success, response| 148: if success 149: @remote_forwarded_ports.delete([port, host]) 150: else 151: raise Net::SSH::Exception, "could not cancel remote forward request on #{host}:#{port}" 152: end 153: end 154: end
Starts listening for connections on the local host, and forwards them to the specified remote host/port via the SSH connection. This method accepts either three or four arguments. When four arguments are given, they are:
- the local address to bind to
- the local port to listen on
- the remote host to forward connections to
- the port on the remote host to connect to
If three arguments are given, it is as if the local bind address is “127.0.0.1“, and the rest are applied as above.
ssh.forward.local(1234, "www.capify.org", 80) ssh.forward.local("0.0.0.0", 1234, "www.capify.org", 80)
# File lib/net/ssh/service/forward.rb, line 51 51: def local(*args) 52: if args.length < 3 || args.length > 4 53: raise ArgumentError, "expected 3 or 4 parameters, got #{args.length}" 54: end 55: 56: bind_address = "127.0.0.1" 57: bind_address = args.shift if args.first.is_a?(String) && args.first =~ /\D/ 58: 59: local_port = args.shift.to_i 60: remote_host = args.shift 61: remote_port = args.shift.to_i 62: 63: socket = TCPServer.new(bind_address, local_port) 64: 65: @local_forwarded_ports[[local_port, bind_address]] = socket 66: 67: session.listen_to(socket) do |server| 68: client = server.accept 69: debug { "received connection on #{bind_address}:#{local_port}" } 70: 71: channel = session.open_channel("direct-tcpip", :string, remote_host, :long, remote_port, :string, bind_address, :long, local_port) do |achannel| 72: achannel.info { "direct channel established" } 73: end 74: 75: prepare_client(client, channel, :local) 76: 77: channel.on_open_failed do |ch, code, description| 78: channel.error { "could not establish direct channel: #{description} (#{code})" } 79: channel[:socket].close 80: end 81: end 82: end
Requests that all connections on the given remote-port be forwarded via the local host to the given port/host. The last argument describes the bind address on the remote host, and defaults to 127.0.0.1.
This method will return immediately, but the port will not actually be forwarded immediately. If the remote server is not able to begin the listener for this request, an exception will be raised asynchronously.
If you want to know when the connection is active, it will show up in the active_remotes list. If you want to block until the port is active, you could do something like this:
ssh.forward.remote(80, "www.google.com", 1234, "0.0.0.0") ssh.loop { !ssh.forward.active_remotes.include?([1234, "0.0.0.0"]) }
# File lib/net/ssh/service/forward.rb, line 119 119: def remote(port, host, remote_port, remote_host="127.0.0.1") 120: session.send_global_request("tcpip-forward", :string, remote_host, :long, remote_port) do |success, response| 121: if success 122: debug { "remote forward from remote #{remote_host}:#{remote_port} to #{host}:#{port} established" } 123: @remote_forwarded_ports[[remote_port, remote_host]] = Remote.new(host, port) 124: else 125: error { "remote forwarding request failed" } 126: raise Net::SSH::Exception, "remote forwarding request failed" 127: end 128: end 129: end