Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Paramiko: Port Forwarding Around A NAT Router

Configuration

  • LOCAL: A local machine that will create an ssh connection and issue commands on a REMOTE box.
  • PROXY: An EC-2 instance with ssh access to both LOCAL and REMOTE.
  • REMOTE: A remote machine sitting behind a NAT Router (inaccessible by LOCAL, but will open a connection to PROXY and allow LOCAL to tunnel to it).

Port Forwarding Steps (via command line)

  1. Create an ssh connection from REMOTE to PROXY to forward ssh traffic on port 22 on the REMOTE machine to port 8000 on the PROXY server.

    # Run from the REMOTE machine
    ssh -N -R 0.0.0.0:8000:localhost:22 PROXY_USER@PROXY_HOSTNAME

  2. Create an ssh tunnel from LOCAL to PROXY and forward ssh traffic from LOCAL:1234 to PROXY:8000 (which then forwards to REMOTE:22).

    # Run from LOCAL machine
    ssh -L 1234:localhost:8000 PROXY_USER@PROXY_HOSTNAME

  3. Create the forwarded ssh connection from LOCAL to REMOTE (via PROXY).

    # Run from LOCAL machine in a new terminal window
    ssh -p 1234 REMOTE_USER@localhost

    # I have now ssh'd to the REMOTE box and can run commands

Paramiko Research

I have looked at a handful of questions related to port forwarding using Paramiko, but they don't seem to address this specific situation.

My Question

How can I use Paramiko to run steps 2 and 3 above? I essentially would like to run:

import paramiko

# Create the tunnel connection
tunnel_cli = paramiko.SSHClient()
tunnel_cli.connect(PROXY_HOSTNAME, PROXY_PORT, PROXY_USER)

# Create the forwarded connection and issue commands from LOCAL on the REMOTE box
fwd_cli = paramiko.SSHClient()
fwd_cli.connect('localhost', LOCAL_PORT, REMOTE_USER)
fwd_cli.exec_command('pwd')
like image 262
cooncesean Avatar asked Sep 23 '13 20:09

cooncesean


People also ask

Does SSH support port forwarding?

SSH is a secure shell and it offers a private connection between hosts. SSH port forwarding is one method that is used to tunnel traffic through an SSH connection. This can be done either locally or remotely if you are not close by to the target machine. Port 22 is used by default for establishing SSH connections.

Does Paramiko use OpenSSH?

Paramiko does not itself leverage OpenSSH-style config file directives, but it does implement a parser for the format, which users can honor themselves (and is used by higher-level libraries, such as Fabric).

How does SSH port forwarding work?

Remote Port Forwarding In this forwarding type, the SSH server listens on a given port and tunnels any connection to that port to the specified port on the local SSH client, which then connects to a port on the destination machine. The destination machine can be the local or any other machine.


1 Answers

A detailed explanation of what Paramiko is doing "under the hood" can be found at @bitprohet's blog here.

Assuming the configuration above, the code I have working looks something like this:

from paramiko import SSHClient

# Set up the proxy (forwarding server) credentials
proxy_hostname = 'your.proxy.hostname'
proxy_username = 'proxy-username'
proxy_port = 22

# Instantiate a client and connect to the proxy server
proxy_client = SSHClient()
proxy_client.load_host_keys('~/.ssh/known_hosts/')
proxy_client.connect(
    proxy_hostname,
    port=proxy_port,
    username=proxy_username,
    key_filename='/path/to/your/private/key/'
)

# Get the client's transport and open a `direct-tcpip` channel passing
# the destination hostname:port and the local hostname:port
transport = proxy_client.get_transport()
dest_addr = ('0.0.0.0', 8000)
local_addr = ('127.0.0.1', 1234)
channel = transport.open_channel("direct-tcpip", dest_addr, local_addr)

# Create a NEW client and pass this channel to it as the `sock` (along with
# whatever credentials you need to auth into your REMOTE box
remote_client = SSHClient()
remote_client.load_host_keys(hosts_file)
remote_client.connect('localhost', port=1234, username='remote_username', sock=channel)

# `remote_client` should now be able to issue commands to the REMOTE box
remote_client.exec_command('pwd')
like image 89
cooncesean Avatar answered Oct 13 '22 11:10

cooncesean