How to ssh forwarding with AllowTcpForwarding set to no?

As long as one can execute socat locally and on gateway (or even just bash and cat on gateway, see last example!) and is allowed to not use a pty to be 8bits clean, it's possible to establish a tunnel through ssh. Here are 4 examples, improving upon the previous:

Basic example working once

(having it fork would require one ssh connection per tunnel, not good). Having to escape the : for socat to accept the exec command:

term1:

$ socat tcp-listen:12345,reuseaddr exec:'ssh user1@gateway exec socat - tcp\:devbox\:22',nofork

term2:

$ ssh -p 12345 user2@localhost

term1:

user1@gateway's password:

term2:

user2@localhost's password: 

Reversing first and second addresses makes the socket immediately available

socat has to stay in charge, so no nofork:

term1:

    $ socat exec:'ssh user1@gateway exec socat - tcp\:devbox\:22' tcp-listen:12345,reuseaddr
    user1@gateway's password:

term2:

    $ ssh -p 12345 user2@localhost
    user2@localhost's password:

Using a ControlMaster ssh

allows to fork while using only a single ssh connection to the gateway, thus giving a behaviour similar to the usual port forwarding:

term1:

    $ ssh -N -o ControlMaster=yes -o ControlPath=~/mysshcontrolsocket user1@gateway
    user1@gateway's password:

term2:

    $ socat tcp-listen:12345,reuseaddr,fork exec:'ssh -o ControlPath=~/mysshcontrolsocket user1@gateway exec socat - tcp\:devbox\:22'

term3:

    $ ssh -p 12345 user2@localhost
    user2@localhost's password:

Having only bash and cat available on gateway

By using bash's built-in tcp redirection, and two half-duplex cat commands (for a full-duplex result) one doesn't even need a remote socat or netcat. Handling of multiple layers of nested and escaped quotes was a bit awkward and can perhaps be done better, or simplified by the use of a remote bash script. Care has to be taken to have the forked cat for output only:

term1 (no change):

$ ssh -N -o ControlMaster=yes -o ControlPath=~/mysshcontrolsocket user1@gateway
user1@gateway's password:

term2:

$ socat tcp-listen:12345,reuseaddr,fork 'exec:ssh -T -o ControlPath=~/mysshcontrolsocket user1@gateway '\''exec bash -c \'\''"exec 2>/dev/null 8<>/dev/tcp/devbox/22; cat <&8 & cat >&8"\'\'\'

term3:

$ ssh -p 12345 user2@localhost
user2@localhost's password:

Replace ProxyJump with Bash

The idea above is good! Here is my generic ssh_config version when ProxyJump is not working because AllowTcpForwarding set to no and my default shell is BASH:

ProxyCommand=ssh -T user1@gateway "exec 3<>/dev/tcp/%h/%p 2<&- ; cat <&3 & cat >&3 ; kill $!"
  • -T Disable pseudo-terminal allocation
  • exec No new process (bash) will be created
  • 3<> is simply redirection to an available file descriptor
  • /dev/tcp/... will ask bash to open the corresponding TCP socket.
  • %h and %p will be evaluated by your OpenSSH client as devbox and 22
  • 2<&- will close the STDERR (you could also redirect it to /dev/null)
  • cat <&3 & will read the selected file descriptor 3 in the background
  • cat >&3 will write our file descriptor in the foreground
  • kill $! will kill the "reading" cat <&3 command running in the background when you close/broke the connection. Otherwise it would keep running.

It could replace ProxyJump for me in situations when it was disabled on the jump server but I really didn't wanted to forward my private key there or enter any passwords without an extra level of encryption. Using others SSH_AUTH_SOCK as root or record terminal sessions completely with keystrokes are both real things.

But please, always make sure you don't violate any policies that apply to you!