Networking

SSH connection gateways

Background

When connecting to well-secured networks whose internal hosts are not directly reachable from the Internet, a common pattern is “bouncing”, “gatewaying” or “proxying” SSH connections via an intermediate host (often called a “bastion”, “gateway” or “jump box”).

Gatewaying requires making an initial/outer SSH connection to the gateway system, then using that connection as a transport for the “real” connection to the final/internal host.

At a basic level, one could ssh gatewayhost, then ssh internalhost from the resulting shell. This works for individual long-running sessions, but becomes a burden when it must be done frequently.

There are two gateway solutions available in Fabric, mirroring the functionality of OpenSSH’s client: ProxyJump style (easier, less overhead, can be nested) or ProxyCommand style (more overhead, can’t be nested, sometimes more flexible). Both support the usual range of configuration sources: Fabric’s own config framework, SSH config files, or runtime parameters.

ProxyJump

This style of gateway uses the SSH protocol’s direct-tcpip channel type - a lightweight method of requesting that the gateway’s sshd open a connection on our behalf to another system. (This has been possible in OpenSSH server for a long time; support in OpenSSH’s client is new as of 7.3.)

Channel objects (instances of paramiko.channel.Channel) implement Python’s socket API and are thus usable in place of real operating system sockets for nearly any Python code.

ProxyJump style gatewaying is simple to use: create a new Connection object parameterized for the gateway, and supply it as the gateway parameter when creating your inner/real Connection:

from fabric import Connection

c = Connection('internalhost', gateway=Connection('gatewayhost'))

As with any other Connection, the gateway connection may be configured with its own username, port number, and so forth. (This includes gateway itself - they can be chained indefinitely!)

ProxyCommand

The traditional OpenSSH command-line client has long offered a ProxyCommand directive (see man ssh_config), which pipes the inner connection’s input and output through an arbitrary local subprocess.

Compared to ProxyJump style gateways, this adds overhead (the extra subprocess) and can’t easily be nested. In trade, it allows for advanced tricks like use of SOCKS proxies, or custom filtering/gatekeeping applications.

ProxyCommand subprocesses are typically another ssh command, such as ssh -W %h:%p gatewayhost; or (on SSH versions lacking -W) the widely available netcat, via ssh gatewayhost nc %h %p.

Fabric supports ProxyCommand by accepting command string objects in the gateway kwarg of Connection; this is used to populate a paramiko.proxy.ProxyCommand object at connection time.

Additional concerns

If you’re unsure which of the two approaches to use: use ProxyJump style. It performs better, uses fewer resources on your local system, and has an easier-to-use API.

Warning

Requesting both types of gateways simultaneously to the same host (i.e. supplying a Connection as the gateway via kwarg or config, and loading a config file containing ProxyCommand) is considered an error and will result in an exception.