I've got 3 hosts:
- My computer.
- A Jump Host (aka bastion) with a static IP.
- A server with a dynamic IP; behind a NAT router/firewall, with no inbound ports open.
The server currently connects to the Jump Host, and establishes an SSH tunnel via -R "0:localhost:22"
, so the port is dynamically allocated by the Jump Host (this has been very reliable so far).
With some socket magic, I have the dynamically allocated port number recorded in a file on the Jump Host.
With this, I can SSH to the Jump Host, and run ssh -p $(cat /path/to/port-file) localhost
But is it possible to skip this extra step?
This would be useful for Ansible, where my inventory.yml
needs to have the port number updated:
server:
# /usr/bin/ssh jh cat /path/to/port-file
ansible_port: "34625"
ansible_host: "localhost"
ansible_ssh_common_args: "-o ProxyCommand='ssh -q -W %h:%p jh' -o StrictHostKeyChecking=no"
It might be possible to use ProxyCommand
:
It can (sort-of) use command substitution on my computers ~/.ssh/config
file:
Host server
ProxyCommand ssh -q -W localhost:$(echo "34625") jh
This works (even without echo -n
) where the substitution seems to happen on my local computer.
And despite the inefficiency of using SSH to get the port number first, this doesn't work:
Host server
ProxyCommand ssh -q -W localhost:$(ssh jh cat /path/to/port-file) jh
It results in:
Bad packet length 1349676916.
ssh_dispatch_run_fatal: Connection to UNKNOWN port 65535: message authentication code incorrect
And oddly, the server (at the end of the chain), does note this in its auth logs:
sshd[5558]: Bad protocol version identification '' from ::1 port 36048
Which implies that the port number is being returned. But no idea why it breaks at this point.
And ssh -vvv
shows that it identifies the hostkey in my ~/.ssh/known_hosts
.
I've also tried creating a custom "ssh_config" file on the Jump Host, using ssh -F /path/to/ssh_config
, with the contents:
Host tunnel.server
HostName localhost
Port 34625
But I don't think I can use this with the ProxyCommand
.
I also suspect StrictHostKeyChecking=no
might be need to be introduced at some point, when the port number changes.
Best Answer
Second partial solution, inspired by @anx...
Create a socket file
Then, to use this socket (from the Jump Host), I can use
socat
:The use of
socat
seems like an unnecessary step, where I'm sure there must be a way to get thessh
command to use the socket file directly, but I can't find it yet.I've also not found how to use this socket file from my computer (as ProxyCommand is run on localhost, not on the JumpHost).
I should also note; as the
tunnel
account (on the Jump Host) is very restricted (it's only there to establish these tunnel connections), I need to setStreamLocalBindMask=0111
so my account on the Jump Host can use this socket file. Likewise, the old socket file should be removed if a new connection is established, viaStreamLocalBindUnlink=yes
.Both of these options need to be set on the Jump Host, in "/etc/ssh/sshd_config":
Unfortunately
Match
rules are ignored in "/etc/ssh/sshd_config.d/tunnel.conf" before OpenSSH 8.4, released September 27 2020 (bug report), and this isn't currently available on Ubuntu 20.04.1 LTS.