Port Forwarding

Net::SSH provides a powerful and easy-to-use API for forwarding TCP/IP ports and Unix domain sockets through an SSH connection. This is often called "SSH tunneling."

The forwarding API is accessed via ssh.forward.

Local Port Forwarding

Local port forwarding allows you to forward connections from a port on your local machine to a port on a remote server. This is useful for accessing services on a remote machine's network that are not exposed to the internet.

Signature: ssh.forward.local(local_port, remote_host, remote_port)

  • local_port: The port to listen on your local machine.
  • remote_host: The destination host to connect to from the SSH server.
  • remote_port: The port on the destination host.

Important: After setting up a forwarded port, you must run the event loop (ssh.loop) to keep the connection alive and process forwarding requests.

Example: Accessing a Remote Database

Imagine a database server db.internal.network is only accessible from your SSH gateway ssh.example.com.

Net::SSH.start('ssh.example.com', 'user') do |ssh|
  # Forward local port 5432 to the remote database server's port 5432
  ssh.forward.local(5432, 'db.internal.network', 5432)

  puts "Forwarding local port 5432. Connect your DB client to localhost:5432."
  puts "Press Ctrl-C to stop."

  # Keep the script running to handle forwarded connections
  ssh.loop { true }
end

Now, any connection to localhost:5432 on your machine will be securely tunneled through ssh.example.com to db.internal.network:5432.

To stop forwarding, use ssh.forward.cancel_local(port, bind_address).

Remote Port Forwarding

Remote port forwarding is the reverse of local forwarding. It forwards connections from a port on the SSH server to a port on your local machine (or another machine accessible from your local machine).

Signature: ssh.forward.remote(local_port, local_host, remote_port, remote_host = '127.0.0.1')

  • local_port: The port on the destination host to connect to.
  • local_host: The destination host (from your client's perspective).
  • remote_port: The port to listen on the SSH server.
  • remote_host: The address to bind to on the SSH server (e.g., '0.0.0.0' for all interfaces).

Example: Exposing a Local Web Server

If you have a local web server running on port 3000, you can expose it to the world through a public SSH server.

Net::SSH.start('public_server.com', 'user') do |ssh|
  # Forward port 8080 on the remote server to port 3000 on your local machine
  ssh.forward.remote(3000, 'localhost', 8080, '0.0.0.0')

  puts "Forwarding remote port 8080. Access it via http://public_server.com:8080"
  puts "Press Ctrl-C to stop."

  ssh.loop { true }
end

Anyone connecting to http://public_server.com:8080 will have their traffic tunneled to localhost:3000 on your machine.

To stop, use ssh.forward.cancel_remote(port, host).

Unix Domain Socket Forwarding

Net::SSH also supports forwarding through Unix domain sockets, which is useful for services like Docker or database connections that use sockets.

Signature: ssh.forward.local_socket(local_socket_path, remote_socket_path)

This forwards connections from a local Unix socket to a remote Unix socket.

Example: Connecting to a Remote Docker Daemon

Net::SSH.start('docker-host', 'user') do |ssh|
  local_socket = '/tmp/docker.sock'
  remote_socket = '/var/run/docker.sock'

  ssh.forward.local_socket(local_socket, remote_socket)

  puts "Forwarding remote Docker socket to #{local_socket}"
  puts "Run: DOCKER_HOST=unix://#{local_socket} docker ps"
  puts "Press Ctrl-C to stop."

  ssh.loop { true }
end

To stop, use ssh.forward.cancel_local_socket(path).