Chaining SSH connections
NOTE: the author/maintainer of the tutorial(s) is no longer with the show, so the information below may be outdated or incorrect.
With people running BSD as firewalls and routers, many have sshd running on them. That means that they can be used as a gateway to machines behind them. If it's a one-off occurrence, then the connection information only needs to be specified on the command line. If it is done more frequently, it can be set in the user's or system's ssh_config. Doing so needs only a port open for SSH and no further per-destination modification of the firewall rules.
Passing through a gateway using netcat mode
As of OpenSSH 5.4, a "netcat mode" can connect stdio on the client to a single port forwarded on the server. This can also be used to connect using ssh, but it needs the ProxyCommand option either as a run time parameter or as part of ~/.ssh/config. However, it no longer needs netcat to be installed on the intermediary machine(s). Here is an example of using it in a run time parameter.
$ ssh -o ProxyCommand="ssh -W %h:%p jumphost.example.org" server.example.org
In that example, authentication will happen twice - first on the jump host, and then on the final host where it will bring up a shell. The syntax is the same if the gateway is identified in the configuration file. ssh expands the full name of the gateway and the destination from the configuration file. The following allows the destination host to be reached by entering ssh server in the terminal:
Host server Hostname server.example.org ProxyCommand ssh jumphost.example.org -W %h:%p
The same can be done for SFTP. Here the destination SFTP server can be reached by entering sftp jump and the configuration file takes care of the rest. If there is a mix up with the final host key, then it is necessary to add in HostKeyAlias to explicitly name which key will be used to identify the destination system.
Host sftpserver HostName sftpserver.example.org HostKeyAlias sftpserver.example.org ProxyCommand ssh jumphost.example.org -W %h:%p
It is possible to add the key for the gateway to the ssh-agent which you have running, or else specify it in the configuration file. The option "User" refers to the user name on the destination. If the user is the same on both the destination and the originating machine, then it does not need to be used. If the user name is different on the gateway, then the -l option can be used in the ProxyCommand option. Here, the user fred on the local machine logs into the gateway as fred2 and into the destination server as fred3.
Host server HostName server.example.org User fred3 ProxyCommand ssh -l fred2 -i /home/fred/.ssh/rsa_key jumphost.example.org -W %h:%p
If both the gateway and destination are using keys, then the option "IdentityFile" is used to point to the destination's private key.
Host jump HostName server.example.org IdentityFile /home/fred/.ssh/rsa_key_2 ProxyCommand ssh -i /home/fred/.ssh/rsa_key jumphost.example.org -W %h:%p
It's also possible to do that more than one layer deep.
Recursively chaining gateways
It is possible to make the configuration more abstract and allow passing through an arbitrary number of gateways. This particular configuration only works if the user name is the same across all hosts involved. There are limitations resulting from using the slash as a separator, as there would be with other symbols. However, it allows use of dirname and hostname to process the host names.
Host * / * ProxyCommand ssh $(dirname %h) -W $(basename %h):%p
Do not put a space between the "* / *" - that's only there because of a technical problem with Markdown not displaying it correctly otherwise. In this way, hosts are separated with a slash (/) and can be arbitrary in number.
$ ssh host1/host2/host3/host4
If keys are to be used, then agent forwarding can be specified in the command given in the "ProxyCommand" option using -A and first loading the keys into the agent. The following configuration uses sed to allow different port numbers and user names using the plus sign (+) as the delimiter for hosts, a colon (:) for ports and an equal sign (=) for user names. The basic structure is "ssh $() -W $():$()" and where "%h" is substituted for the target host name.
Host *+* ProxyCommand ssh -v $(echo %h | sed -e 's/+[^+]*$//; s/\([^+=]*\)=\([^+]*\)$/\2 -l \1/; s/^\([^+:]*\):\([0-9]*\)+/-p \2 \1+/' ) -W $(echo %h | sed -e 's/^.*+//; s/:.*$//;'):$(echo %h | sed -e ' s/^.*+//; /:/!s/^.*/22/; s/^.*://' ;)
The port can be left off for the default of 22 or delimited with a colon (:) for non-standard values.
$ ssh host1+host2:2022+host3:2224
As is, the colons confound sftp, so the above configuration will only work with it using standard ports. If sftp is needed on non-standard ports then another delimiter, such as an underscore (_), can be configured. Any user name except the final one can be specified for a given host using the designated delimiter, in the above it is an equal sign (=). The destination host's user name is specified with -l and all others can be joined to their corresponding host name with the delimiter.
$ ssh -l user3 user1=host1+user2=host2+host3
If user names are specified, depending on the delimiter, ssh can be unable to match the final host to an IP number and the key fingerprint in known_hosts. In such cases, it will ask for verification each time the connection is established, but this should not be a problem if the equal sign (=) is used.