How to close an ssh tunnel in python - python

I am writing a Python application that accesses a remote Mysql database using ssh tunneling.
I setup the tunnel with
os.system("ssh -fNg -L 3306:127.0.0.1:3306 username#host")
and everything works perfectly. The problem is:
what is the python code to close the ssh tunnel?
Thanks

os.system('exit') doesn't work
The process that creates the tunnel still runs in the background
>>> command = 'ssh -fNg vagrant#localhost -p2222 -L 8000:localhost:8000'
>>> os.system(command)
>>> os.system('exit')
ps -A | grep ssh
7144 ?? 0:00.04 ssh -fNg vagrant#localhost -p2222 -L 8000:localhost:8000
This shows the process is still running and the tunnel is still working, and os.system doesn't return the process ID so we can use it to terminate the process (it returns the exit code)
Use subprocess to return a handle to the process
import subprocess
proc = subprocess.Popen(command, shell=True)
proc.terminate() # this terminates the process

Its correct sys.exit() won't exist the script and keeps the tunnel open.
Here is how I close an SSH tunnel using Python and in turn allows me to terminate the
Python script.
In my close function I pass the tunnel connection I created:
def close(self, tunnel = None):
if tunnel is not None:
# close tunnel connection.
tunnel.close()
I created the SSH connection using Paramiko as
def connect(params):
# Create an SSH tunnel
tunnel = SSHTunnelForwarder(
(params['ssh_ip_address'], int(params['ssh_port'])),
ssh_username=params['ssh_username'],
ssh_private_key=params['ssh_key'],
ssh_private_key_password=params['ssh_key_file_password'],
remote_bind_address=('localhost', int(params['ssh_remote_forward_port'])),
local_bind_address=('localhost',int(params['ssh_local_forward_port']))
tunnel.start()
return tunnel

Related

open a putty window and run ssh commands - Python

I am new to python. I need to login to a server daily (Desktop -> 1.32 -> 0.20 -> 3.26). For this I need to open putty and using ssh connection i am logging in. To do all this I want to write a script using python.
By using google I thought subprocess.Popen will do that. But Its not working fine.
1st trail:
import subprocess
pid = subprocess.Popen("putty.exe user#xxx.xx.x.32 -pw password").pid
Its working fine (Opening window logging into .32). But cant able to give input. I came to know that to give input for the same process we need to use pipes.
2nd trail:
from subprocess import Popen, PIPE, STDOUT
p = Popen("putty.exe user#xxx.xx.x.32 -pw password", stdout=PIPE, stdin=PIPE, stderr=STDOUT)
grep_stdout = p.communicate(input=b'ssh xx.xx.x.20\n')[0]
print(grep_stdout.decode())
by using this i cant login for the first server also. After logging in to all servers I need the terminal as alive. how to do this???
Edit
I need to do this in a new putty window. After logging in dont close the window. I have some manual work to do.
use powershell to call putty in order to open a new window
from subprocess import Popen
Popen("powershell putty.exe user#host -pw mypassword")
Use paramiko library python
Establish a SSH connection using -
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname,username, password)
Check the status if connection is alive using -
status = ssh.get_transport().is_active()
#returns True if connection is alive/active
ssh.exec_command() is basically a single session. Use exec_command(command1;command2) to execute multiple commands in one session
Also, you can use this to execute multiple commands in single session
channel = ssh.invoke_shell()
stdin = channel.makefile('wb')
stdout = channel.makefile('rb')
stdin.write('''
Command 1
Command 2
''')
print stdout.read()
There is a SSHv2 protocol implementation for python: http://www.paramiko.org/. You can easily install it with pip:
pip install paramiko
Then you can create ssh client, connect to your host and execute commands:
import paramiko
ssh_client = paramiko.SSHClient()
ssh_client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh_client.connect('hostname', username='login', password='pwd')
stdin, stdout, stderr = ssh_client.exec_command('command')
I created a bat file on windows, which references putty and putty session-specific info. This bat file can run by itself on windows. To call from python, I used the subprocess.run() -- python 3.5+.
Example of bat file named putty.bat:
start c:\app\PuTTy\putty.exe -load 192.168.1.230-node1-logs -l <logon user> -pw <logon user password for putty session>
Breaking down the bat file:
It begins with window's command "start".
c:\app\PuTTy\putty.exe --> is the putty directory on Windows containing putty.exe.
-load --> tells putty to load a putty profile. The profile is the name you see on the putty client, under "Saved Sessions".
192.168.1.230-node1-logs --> my putty session specific profile.
-l for logon --> followed by the putty logon user.
-pw is the logon password --> followed by the putty logon password.
That concludes the contents of "putty.bat".
From within python, is used the subprocess.run() command.
Example:
import subprocess
...
...
try:
process = subprocess.run(["putty.bat"], check=True, stdout=subprocess.PIPE, universal_newlines=True)
print(process.stdout)
except Exception as e:
print("subprocess call error in open putty command")
print(str(e))
I hope you find this helpful

Python ssh - keep connection open after script terminates

I'm trying to write a script that will ssh into a box for me. I'm using Python and leveraging the paramiko library. I can successfully ssh on the box, but as soon as the script terminates, the ssh connection also terminates. I want to keep the connection open after the script has completed running.
Python:
self.ssh = paramiko.SSHClient()
self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.ssh.connect(host, username=self.username, password=self.password)
stdout = execute(self.ssh, 'pwd') # test command for now to verify i'm on box
print stdout
sys.exit()
Console:
$ ssh.py
[u'/home/myuser\n']
myuser#xxxx ~
$
I haven't been able to find similar examples online, so any help would be appreciated.
Try this:
import subprocess
subprocess.call(["ssh", "myuser#myserver"])

Executing reboot command over SSH using Paramiko

I use Paramiko for establishing SSH connection with some target device and I want to execute reboot command.
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(zip_hostname, username=username, password=password, timeout=1)
try:
stdin, stdout, stderr = ssh.exec_command("/sbin/reboot -f")
# .........
# some code
# .........
except AuthenticationException, e:
print ''
finally:
ssh.close()
But after executing ssh.exec_command("/sbin/reboot -f") "some code" does not execute because program is stuck in exec_command (the disconnection takes place caused by rebooting). What should I do to solve my problem?
Try this:
ssh.exec_command("/sbin/reboot -f > /dev/null 2>&1 &")
All the output of reboot is redirected to /dev/null to make it produce no output and it is started in the background thanks to the '&' sign in the end. Hopefully the program won't hang on that line this way, because the remote shell gives the prompt back.
Get the transport from the ssh and set the keepalive using:
transport = ssh.get_transport()
transport.set_keepalive(5)
This sets the keepalive to 5 seconds; mind you I would have expected the timeout=1 to have achieved the same thing.
All you need to do is to call channel.exec_command() instead of the high-level interface client.exec_command()
# exec fire and forget
timeout=0.5
transport = ssh.get_transport()
chan = ssh.get_transport().open_session(timeout=timeout)
chan.settimeout(timeout)
try:
chan.exec_command(command)
except socket.timeout:
pass
I was having this issue and managed to avoid it by switching to this command:
/sbin/shutdown -r now
Note this command does not result in any STDOUT or STDERR output
In case you or anyone else gets stuck trying to reboot host with sudo using forwarding agents (ssh keys) or in my case (yubikey)
If you look at this as bash you would reboot a host as non root user like this.
ssh -t -A user#hostname sudo /sbin/reboot
For the -A flag, from ssh man page
Enables forwarding of the authentication agent connection. This can also be specified on a per-host basis in a
configuration file.
Agent forwarding should be enabled with caution. Users with the ability to bypass file permissions on the
remote host (for the agent’s Unix-domain socket) can access the local agent through the forwarded connection.
An attacker cannot obtain key material from the agent, however they can perform operations on the keys that
enable them to authenticate using the identities loaded into the agent.*
For the -t flag, from ssh man page
Force pseudo-tty allocation. This can be used to execute arbitrary screen-based programs on a remote machine,
which can be very useful, e.g. when implementing menu services. Multiple -t options force tty allocation, even
if ssh has no local tty.*
So lets break this down into how you would do this in paramiko
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname=host, username=username)
s = ssh.get_transport().open_session()
paramiko.agent.AgentRequestHandler(s)
ssh.exec_command("sudo /sbin/reboot", get_pty=True)
For authentication forwarding (-A flag in bash ssh command) for paramiko
ssh = paramiko.SSHClient() #'ssh' is client variable
s = ssh.get_transport().open_session() #get 'ssh' transport and open sessions assigned to 's' variable
paramiko.agent.AgentRequestHandler(s) #call in 's' to the forwarding agent for current ssh session
Now for force pseudo-tty allocation (-t flag in bash ssh command) for paramiko
ssh.exec_command("sudo /sbin/reboot", get_pty=True)
Adding 'get_pty=True' to exec_command will allow you execute sudo /sbin/reboot
Hope this helps, everyone's environments are different but this should work as it the exact same thing as if you ran it as bash.

How to create a SSH tunnel using Python and Paramiko?

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.
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 sshtunnel
python -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 ssh
into “master” mode with confirmation required before slave connections are accepted. Refer to the
description of ControlMaster in ssh_config(5) for details.
-S ctl_path
Specifies the location of a control socket for connection sharing, or the string “none” to disable
connection 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, STDOUT
import os
DEVNULL = 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
) == 0
def stop():
return __control_ssh('exit') == 0
def status():
return __control_ssh('check') == 0
def __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 paramiko
ssh = 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.

How do I close an ssh Tunnel opened through Popen in python

I need to create an ssh tunnel, then do something, then tear the tunnel down.
I have been trying to do it like this:
def runCmd(self,cmd):
args = shlex.split(cmd)
return subprocess.Popen(args)
def openTunnel
cmd = 'ssh -f -N -L 1313:localhost:1313 userid#server.com'
self.TunnelObj = self.runCmd(cmd)
That creates my Tunnel.
I can then do the stuff I need to do. Now I want to tear down the tunnel.
def closeSocket(self):
print '\nClosing Tunnel\n'
if self.TunnelObj.returncode == None:
print '\nabout to kill\n'
self.TunnelObj.kill()
But the tunnel is still open. An ssh session still exists, and the port is still assigned.
How can I shut this tunnel down?
Part of the problem is that the tunnel process is a subprocess of self.TunnelObj. You can try to omit the -f flag so you hold the tunnel process directly.
Another option would be to look at the paramiko library and this question.

Categories

Resources