ssh config as a hosts file

I’m late adopter of bash completion. I tried it a few times but I always gave up because I found it slow. And I hate when it blocks my shell waiting for a command to finish. I’ve recently upgraded my computers and on recent hardware, it now runs pretty smoothly. So it’s time to give it another go.

One feature I use the most is ssh host completion. At work I work with a lot of VMs. There is no way I can remember all of their IPs but it’s pretty easy to put a name on each machine relevant enough to remember.

This has to be done in ~/.ssh/config, for example:

Host webserver
    User myuser
    Hostname xx.xx.xx.xx

with these lines, calling:

$ ssh webserver

is the same as calling:

$ ssh myuser@xx.xx.xx.xx

That hostname alias for ssh can be auto-completed hitting tab which makes it super useful. This trick also works for any ssh aware program like: rsync, sshfs, scp etc…

But what if I want to use that same hostname with a non ssh aware program ? I would need a way to convert that hostname into the corresponding IP address of the ~/.ssh/config file.

It seems to be possible to have a user /etc/hosts file using the environment variable HOSTALIASES as described in man gethostbyname. I’ve tried this approach but I never managed to have it working properly. And one problem with this solution is that you now have to maintain two files: ~/.ssh/config and that HOSTALIASES file.

So I came up with a small script: ssh-host.sh to output the Hostname corresponding to a Host in ~/.ssh/config.

$ ssh-host.sh webserver
xx.xx.xx.xx

With that script in your PATH it is now easy to write commands like:

$ curl https://$(ssh-host.sh webserver)

and have it using the IP address of the ~/.ssh/config file.

This is already cool but, of course, that trick wouldn’t be complete without a bit of auto-completion !

# Completion for ssh-host.sh
_complete()
{
  if [ "${#COMP_WORDS[@]}" != "2" ]; then
    return
  fi

  HOSTS=$(cat ~/.ssh/config | awk '/^Host / { print $2 }')
  COMPREPLY=($(compgen -W "$HOSTS" "${COMP_WORDS[1]}"))
}

complete -F _complete ssh-host.sh

Add these lines to your ~/.bashrc and the ssh-host.sh script now complete just like ssh does.

With this simple script, I can now use the ~/.ssh/config file as a /etc/hosts file at the cost of calling that intermediate script. And it is super handy in my workflow !

July 18, 2021