Dynamically generate SSH Host entries in ~/.ssh/config
I have to administer a whole pile of hosts over ssh. However I can only access them through a certain gateway ssh server.
I have the following in my ~/.ssh/config
:
Host mygateway-www
Hostname www
IdentityFile ~/.ssh/id_rsa
ProxyCommand ssh mygateway nc %h 22
However I have to connect to lots of these machines. Instead of putting dozens of entries in my ~/.ssh/config
, is there anyway I can have something like this:
Host mygateway-*
Hostname ???WHAT GOES HERE????
IdentityFile ~/.ssh/id_rsa
ProxyCommand ssh mygateway nc %h 22
I know you can use %h
in the Hostname
argument, but that would be the hostname. What I really need is some sort of string substitution, like bash's ${VAR%thingie}
. Is this possible?
Solution 1:
This can be done with the following SSH config file:
Host *
ServerAliveInterval 120
Host gateway.somewhere.com
User jdoe
Host gateway+*
User jdoe
ProxyCommand ssh -T -a $(echo %h |cut -d+ -f1).somewhere.com nc $(echo %h |cut -d+ -f2) %p 2>/dev/null
ControlMaster auto
ControlPath ~/.ssh/ssh-control_%r@%h:%p
You then access your internal hosts like so:
ssh gateway+internalhost01.somewhere.com
ssh gateway+internalhost02.somewhere.com
The name you choose for the right half should be resolvable by the jump host.
The User parameter is specified in case you need to manually map to different users on the different classes of hosts. ControlMaster and ControlPath are specified to allow SSH connection re-use.
Solution 2:
You shouldn't need to manually specify HostName as it will come from the command line.
Simply try:
Host *.domain
IdentityFile ~/.ssh/id_rsa
ProxyCommand ssh mygateway /usr/bin/nc %h 22
Solution 3:
Ignore specifying overriding the hostname directly via the Hostname
declaration and instead determine it at runtime. Do this by evaluating it as part of the ProxyCommand
, using %h
to reference it in the command (also use %p
instead of hardcoding port as 22) i.e.
Host mygateway-*
#Hostname ???WHAT GOES HERE????
IdentityFile ~/.ssh/id_rsa
ProxyCommand ssh mygateway nc $(echo %h|sed 's/^mygateway-//') %p
One could even have a more generic stanza, whereby you can specify any host without a -
to just be treated as is, or as per another matching stanza(s), but have a generic -
approach to specify any <gateway>-<target>
:
Host *-*
# Assume LHS of "-" is GW and RHS of "-" is target host
IdentityFile ~/.ssh/id_rsa
ProxyCommand ssh $(echo %h|cut -d - -f1) nc $(echo %h|cut -d - -f2-) %p
Additionally, newer versions of the SSH client support the [-W host:port]
option to directly perform the same function as nc
(netcat). As such, we can use the modified:
Host *-*
# Assume LHS of "-" is GW and RHS of "-" is target host
IdentityFile ~/.ssh/id_rsa
ProxyCommand ssh -W $(echo %h|cut -d - -f2-):%p $(echo %h|cut -d - -f1)
Of course, if you did have a finite list of hosts, you could always do:
Host host1 host2 host3 hostN
IdentityFile ~/.ssh/id_rsa
ProxyCommand ssh mygateway nc %h %p
Hope this helps!