Q:

Tunneling TCP ports through ssh without blocking

I'm trying setup a ssh tunnel via pexpect with following code:
#!/bin/env python2.4

import pexpect, sys
child = pexpect.spawn('ssh -CfNL 0.0.0.0:3306:127.0.0.1:3306 user@server.com')
child.logfile = sys.stdout
while True:
    code = child.expect([
        'Are you sure you want to continue connecting \(yes/no\)\?',
        'password:',
        pexpect.EOF,
        pexpect.TIMEOUT
    ])
    if code == 0:
        child.sendline('yes')
    elif code == 1:
        child.sendline('passwordhere')
    elif code == 2:
        print ".. EOF"
        break
    elif code == 3:
        print ".. Timeout"
        break

What I expect is after password was sent and ssh tunnel established, the while loop exits so that I can continue processing with other business logic.
But the code above block util timeout (about 30 seconds) if ssh tunnel established.
Could anyone please give me some advice on how to avoid the block?

A:

I think the simplest solution is to use ssh host-key authentication, combined with backgrounding ssh with &...  this is a very basic implementation, but you could enhance it to kill the process after you're done... also, note that I added -n to your ssh args, since we're backgrounding the process.
import subprocess

USER = 'user'
HOST = 'server.com'
cmd = r"""ssh -CfNnL 0.0.0.0:3306:127.0.0.1:3306 %s@%s &""" % (USER, HOST)
subcmd = cmd.split(' ')
retval = subprocess.Popen(subcmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stat = retval.poll()
while stat == None:
    stat = retval.poll()
print "ssh in background"

Finally, if you don't already have ServerAliveInterval in your ssh_config, consider calling ssh as ssh -o ServerAliveInterval=30 <other_options_and_args> to make sure you detect loss of the tunnel as soon as possible, and to keep it from aging out of any NAT implementations in the path (during inactivity).

