I need to create tunneling to read information from a database. I use Paramiko, but I have not worked with tunneling yet. Please provide an example of a simple code that creates and closes a tunnel.
Best Answer
At work we usually create ssh tunnels forwarding ports. The way we do that is, by using the standard command ssh -L port:addr:port addr
with subprocess running in a separate thread.I found this useful link: https://github.com/paramiko/paramiko/blob/master/demos/forward.py with an example of doing port forwarding with paramiko.
I used sshtunnel
for my projects. Example of the forwarding remote local MySQL port to the host local port:
pip install sshtunnelpython -m sshtunnel -U root -P password -L :3306 -R 127.0.0.1:3306 -p 2222 localhost
Even though this does not use paramiko, I believe it's a very clean solution to implement (similar to @dario's answer but without managing the thread in python).
There's this little-mentioned feature in openssh client that allows us to control a ssh process through a unix socket, quoting man ssh
:
-M Places the ssh client into “master” mode for connection sharing. Multiple -M options places sshinto “master” mode with confirmation required before slave connections are accepted. Refer to thedescription of ControlMaster in ssh_config(5) for details.-S ctl_pathSpecifies the location of a control socket for connection sharing, or the string “none” to disableconnection sharing. Refer to the description of ControlPath and ControlMaster in ssh_config(5)for details.
So you can start background process of ssh
(with -Nf
) and then check (or terminate) it with a another ssh
call.
I use this in a project that requires a reverse tunnel to be established
from subprocess import call, STDOUTimport osDEVNULL = open(os.devnull, 'wb')CONFIG = dict(SSH_SERVER='ssh.server.com',SSH_PORT=2222,SSH_USER='myuser',SSH_KEY='/path/to/user.key',REMOTE_PORT=62222,UNIX_SOCKET='/tmp/ssh_tunnel.sock',KNOWN_HOSTS='/path/to/specific_known_host_to_conflicts',)def start():return call(['ssh', CONFIG['SSH_SERVER'],'-Nfi', CONFIG['SSH_KEY'],'-MS', CONFIG['UNIX_SOCKET'],'-o', 'UserKnownHostsFile=%s' % CONFIG['KNOWN_HOSTS'],'-o', 'ExitOnForwardFailure=yes','-p', str(CONFIG['SSH_PORT']),'-l', CONFIG['SSH_USER'],'-R', '%d:localhost:22' % CONFIG['REMOTE_PORT']],stdout=DEVNULL,stderr=STDOUT) == 0def stop():return __control_ssh('exit') == 0def status():return __control_ssh('check') == 0def __control_ssh(command):return call(['ssh', '-S', CONFIG['UNIX_SOCKET'], '-O', command, 'x'],stdout=DEVNULL,stderr=STDOUT)
-o ExitOnForwardFailure=yes
makes sure the ssh command will fail if the tunnel cannot be established, otherwise it will not exit.
Might I suggest trying something like pyngrok
to programmatically manage an ngrok
tunnel for you? Full disclosure, I am the developer of it. SSH example here, but it's as easy as installing pyngrok
:
pip install pyngrok
and using it:
from pyngrok import ngrok# <NgrokTunnel: "tcp://0.tcp.ngrok.io:12345" -> "localhost:22">ssh_tunnel = ngrok.connect(22, "tcp")
I used paramiko
for some project I had a year ago, here is the part of my code where I connected with another computer/server and executed a simple python file:
import paramikossh = paramiko.SSHClient()ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())ssh.connect(hostname='...', username='...', password='...')stdin, stdout, stderr = ssh.exec_command('python hello.py')ssh.close()
stdin
, stdout
and sdterr
contain the inputs/outputs of the command you executed.
From here, I think you can make the connection with the database.
Here is some good information about paramiko.