Today I faced a task which required first to establish an SSH tunnel in a background process and later use this tunnel for SSH connection. What seemed like a child’s play first actually had some fun inside.

A problem were hidden right between the moment you spawned ssh process in the background and the next moment you tried to use this tunnel. In other words, it takes literally no time to spawn a process in the background, but without checking that tunnel is ready, you will quite likely receive an error, since your next instructions will be executed immediately after.

Consequently, I needed a way to ensure that the SSH service is ready before I try to consume it.

scheme

But how do you check if there is a server behind some host:port and that this server is of SSH nature? In Ansible we could leverage wait_for module that can poke a socket and see if OpenSSH banner is there. But in my case Python & Paramiko was all I had.

It turned out that with Paramiko it is possible to achieve the goal with most straightforward and probably least elegant code:

I found it sufficient to setup a timer-driven while loop where Paramiko tries to open a connection without credentials. In order to detect if socket is opened I catch different type of exceptions that Paramiko emits:

  • if there is nothing listening on a particular socket, then Paramiko emits paramiko.ssh_exception.NoValidConnectionsError
  • if the socket is open, but the responding service is not SSH, then Paramiko emits paramiko.ssh_exception.SSHException with a particular message Error reading SSH protocol banner
  • if the socket is open and SSH service responding on the remote part - we are good to go! This time still paramiko.ssh_exception.SSHException is emitted, but the error message would be No authentication methods provided.

And that quite does the trick:

gif