Imagine a situation when two servers are available. One of them is constantly sshing to another one and executing some commands. The process takes around 5-10 seconds for each session since ssh connection must be established every time.
The question: is it possible to have a ready, established ssh connection in order to reduce the time of executing commands on remote server?
Here is an example for you. It can work as a ssh client. No need to recreate session everytime.
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import os,sys
import paramiko
import threading
import platform
curr_ssh = None
curr_prompt = ">>"
#how to use it
def printUsage():
print " !ls :list sessions."
print " !session id :connect session."
print " !conn host user password:connect host with user."
print " !exit :exit."
#connect using paramiko
def conn(ip,username,passwd):
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(ip,22,username,passwd,timeout=5)
print "Connect to ",ip," with ",username
global curr_prompt
curr_prompt=username+"#"+ip+">>"
return ssh
except:
return None
#get former session
sessions=[]
def loadSessions():
global sessions
try:
f = open("sessions")
sessions = f.readlines()
f.close()
except:
pass
#main function
def exe_cmd_local(cmd):
if(cmd == "!ls"):
loadSessions()
global sessions
i=0
print "Sessions:"
for s in sessions:
print"[%d] %s" %(i,s)
i+=1
else:
vals = cmd.split(' ')
if(vals[0]=="!session"):
id = (int)(vals[1])
if(id<len(sessions)):
os_name = platform.system()
new_console_cmd = ""
if(os_name == "Linux"):
new_console_cmd="gnome-terminal -e \"./ssh.py " + sessions[id]+"\""
elif(os_name == "Windows"):
new_console_cmd="start ssh.py " + sessions[id]
os.system(new_console_cmd)
else:
print "Didn't hava sessoin ",vals[1]
elif(vals[0]=="!conn"):
global curr_ssh
curr_ssh = conn(vals[1],vals[2],vals[3])
f = open("sessions","a")
line = vals[1]+" "+vals[2]+" "+vals[3]+"\n"
f.write(line)
f.close()
#execute command remotely
def exe_cmd_ssh(ssh,cmd):
if(ssh == None):
print "Didn't connect to a server. Use '!conn' to connect please."
return
stdin, stdout, stderr = ssh.exec_command(cmd)
print stdout.read()
print stderr.read()
if __name__=='__main__':
loadSessions()
if(len(sys.argv)==4):
curr_ssh = conn(sys.argv[1],sys.argv[2],sys.argv[3])
else:
printUsage()
while True:
cmd = raw_input(curr_prompt)
if(len(cmd)==0):
continue
if(cmd == "!exit"):
if(curr_ssh != None):
curr_ssh.close();
break
else:
if(cmd[0] == '!'):
exe_cmd_local(cmd)
else:
exe_cmd_ssh(curr_ssh,cmd)
Hope it helps. :)
One could use an existing ssh connection, by configuring ssh to re-use an already established connection. Please see man ssh_config:
ControlMaster
Enables the sharing of multiple sessions over a single network connection. When set to “yes”, ssh(1) will ...
So your ~/.ssh/config could contain:
Host firstServer
ControlMaster auto
ControlPath ~/.ssh/tmp/controlPath_%h_%p_%r
This will create a socket as a file named by 'ControlPath'. The next ssh connection will go through that same socket and will be lightning fast.
Enjoy
Related
I am trying to connect to switch and redirect the output of command but it does not work. It only connect and disconnect from switch
#!/usr/pkg/bin/python
#importing modules
import paramiko
import sys
import time
# setting parameters like host IP, username, passwd and port
# to gather cmds
HOST = "10.50.170.21"
USER = "user"
PASS= "password"
PORT = 2024
# A function that logins and execute commands
def fn():
client1=paramiko.SSHClient()
#add missing client key
client1.set_missing_host_key_policy(paramiko.AutoAddPolicy())
#connect to switch
client1.connect(HOST,username=USER,password=PASS,port=2024)
print "SSH connection to %s established" %HOST
stdin,stdout,stderr =client1.exec_command('show-config \n')
print stdout.read()
fn()
and the printout like this :
root#cic-1:~# python test-con.py
SSH connection to 10.50.171.22 established
********************************************************************************
BSP 8100
This system is provided for authorized users only. If you are not
an authorized user, please exit IMMEDIATELY.
********************************************************************************
root#cic-1:~#
anybody knows what can be the issue here ?
I changed the Code as below and now it works fine:
#!/usr/pkg/bin/python
#importing modules
import paramiko
import sys
import time
import pdb
#pdb.set_trace()
# setting parameters like host IP, username, passwd and number of iteration
# to gather cmds
HOST = "10.50.171.22"
USER = "advanced"
PASS = "ett,30"
PORT = 2024
ITERATION = 3
# A function that logins and execute commands
def fn():
client1=paramiko.SSHClient()
#add missing client key
client1.set_missing_host_key_policy(paramiko.AutoAddPolicy())
#connect to switch
client1.connect(HOST,username=USER,password=PASS,port=PORT)
print "SSH connection to %s established" %HOST
remote_conn = client1.invoke_shell()
remote_conn.send("\n")
remote_conn.send("show \n")
time.sleep(2)
output = remote_conn.recv(10000)
print output
# stdin,stdout,stderr =client1.exec_command("config /n")
# stdin,stdout,stderr =client1.exec_command("show /n")
# print stdout.read()
client1.close()
fn()
Lately, I've developed an interest in penetration testing. I decided to try and learn how to write some scripts before investing in a full blown course. Currently I'm working my way through the book Black Hat Python book by Justin Seitz.
I'm in the section on SSH using Paramiko and two of the scripts have me stumped. They both run without errors but nothing gets shown on screen. In Windows and Linux the terminal (or DOS prompt) just returns immediately to the prompt. I have gone over the scripts several times and can't find the issue. The code for both scripts is shown in full below.
Script #1 bh_sshserver.py (The purpose of this script is to create an ssh server)
import socket
import paramiko
import threading
import sys
class Server (paramiko.ServerInterface):
def _init_(self):
self.event = threading.Event()
def check_channel_request(self, kind, chanid):
if kind == 'session':
return
paramiko.OPEN_SUCCEEDED
return
paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
def check_auth_password(self, username, password):
if (username == 'root') and (password == '12345'):
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
server = sys.argv[1]
ssh_port = sys.argv[2]
try:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((server, ssh_port))
sock.listen(100)
print '[+] Listening for connection...'
client, addr = sock.accept()
except Exception, e:
print ' [-] Listen Failed: ' + str(e)
sys.exit(1)
print '[+] Got a connection'
try:
bhSession = paramiko.Transport(client)
bhSession.add_server_key(host_key)
server = Server()
try:
bhSession.start_server(server=server)
except paramiko.SSHException, x:
print '[-] SSH Negotiation Failed'
chan = bhSession.accept(20)
print '[+] Authenticated!'
print chan.recv(1024)
chan.send ('Welcome to bh_ssh')
while True:
try:
command= raw_input("Enter command: ").strip('\n')
if command != 'exit':
chan.send(command)
print chan.recv(1024) + '\n'
else:
chan.send('exit')
print 'exiting'
bhSession.close()
raise Exception ('exit')
except KeyboardInterrupt:
bhSession.close()
except Exception, e:
print '[-] Caught exception: ' + str(e)
try:
bhSession.close()
except:
pass
sys.exit(1)
Script #2 bh_sshRcmd.py (The purpose of this script is to create a command receiver for the ssh server to connect to)
import threading
import paramiko
import subprocess
def ssh_command(ip, user, passwd, command):
client = paramiko.SSHClient()
#client.load host keys ('/home/root/.ssh/known_hosts')
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(ip, username=user, password=passwd)
ssh_session = client.get_transport().open_session()
if ssh_session.active:
ssh_session.exec_command(command)
print ssh_session.recv(1024)
# Read the banner
while True:
command = ssh_session.recv(1024)
# Get Command from SSH Server
try:
cmd_output = subprocess.check_output(command, shell=True)
ssh_session.send(cmd_output)
except Exception, e:
ssh_session.send(str(e))
client.close()
return
ssh_command('192.168.1.26', 'Admin', '12345', 'ClientConnected')
Both of these scripts were written in Windows and so do not need the shebang statement (ie #!/usr/bin/python) at the top. I copied them over to a Linux VM and added that statement, plus made them executable using chmod +x. Still, nothing shows on screen when the scripts run. The IP addresses are from a VMware virtual network which has never given me problems before.
It is likely that there is an error connecting to your server. Try adding more print statements to cover the conditionals like so:
import threading
import paramiko
import subprocess
def ssh_command(ip, user, passwd, command):
print 'running ssh_command with ip: {ip} user: {user} passwd: {passwd}, command: {command}'.format(ip=ip,user=user,passwd=passwd,command=command)
client = paramiko.SSHClient()
#client.load host keys ('/home/root/.ssh/known_hosts')
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
client.connect(ip, username=user, password=passwd)
ssh_session = client.get_transport().open_session()
if ssh_session.active:
print 'ssh_session is active'
ssh_session.exec_command(command)
print ssh_session.recv(1024)
# Read the banner
while True:
print 'recv-ing'
command = ssh_session.recv(1024)
# Get Command from SSH Server
try:
cmd_output = subprocess.check_output(command, shell=True)
ssh_session.send(cmd_output)
except Exception, e:
ssh_session.send(str(e))
client.close()
return
else:
print 'ssh_session is not active'
ssh_command('192.168.1.26', 'Admin', '12345', 'ClientConnected')
As for bh_sshserver.py, if you ran python bh_sshserver.py, nothing would happen. This is because you don't have any statements in the main scope. If you wanted to start the server you could add code to the bottom of the script with no indentation.
You should call your server from the terminal using command-line arguments like this:
python scriptname.py server_adress port
Change indentation on the last line of client script - it should call your function
Server adresses in the terminal and in the client function should be the same
That's pretty much all
I can provide you with these two scripts that are working for me, if you need it.
Thanks to everyone who replied. In the end I found some Paramiko demo files from github that included a sample SSH server. It turns out the script is much more complicated than the author makes it out to be. I was missing a ton of code, which is why the server was not working. As soon as I made my script a rough match to the sample it worked perfectly so did my client.
In case anyone comes across a similar problem, here is the link to the Paramiko demo files:
https://github.com/paramiko/paramiko/tree/master/demos
Is there any efficient way to check and report in a log file or on the console may be... when ever the VPN is disconnected?
import time
print time.asctime( time.localtime(time.time()) )
Can print the time but I do not know what is the code to recursively find whether the VPN is active or not. Pinging it in a while(1) would be a stupid way to check if the connection is active or not. Any way to achieve this?
This solution is system dependent, I do know that it works on Linux because I've done something similar, but not sure about Windows though. I don't know if you want a solution not involving ping, but I think this is a good solution.
import logging, os, time
PING_HOST='10.10.10.10' # some host on the other side of the VPN
while True:
retcode = os.system('ping -c 1 %s' % PING_HOST)
if retcode:
# perform action for lost connection
logging.warn('Lost visibility with %s' % PING_HOST)
time.sleep(10) # sleep 10 seconds
This works because ping returns a return code of 0 for success. All other return codes signify an error.
In case the IP of vpn changes, you can check if a tunnel has been established at all.
import psutil
import logging, os, time
import subprocess
import sys
procname = "yourprocess_name"
while True:
cmdout = subprocess.Popen(["ifconfig | grep tun"],stdout = subprocess.PIPE, shell=True).communicate()[0]
print "cmdout: "+str(cmdout)
time.sleep(2)
#-----
if "tun" in cmdout:
print "seems to be ok"
if not "tun" in cmdout:
# perform action for lost connection
print "killing "+str(procname)
for proc in psutil.process_iter():
# check whether the process name matches
print "Listing procname: "+str(proc.name())
if proc.name() == procname:
proc.kill()
sys.exit()
This method uses the NAME of the HOST "Connection-specific DNS Suffix" associated with your IP (Mostly the corporation's VPN):
import os
import platform
def check_ping():
hostname = "xyz.com" #hostname will be..Name under: "Connection-specific DNS Suffix" when you type "ipconfig" in cmd..
response = os.system("ping " + ("-n 1 " if platform.system().lower()=="windows" else "-c 1 ") + hostname)
# and then check the response...
if response == 0:
pingstatus = "Network Active: Connected"
else:
pingstatus = "Network Error: Not Connected"
return pingstatus
response = check_ping()
print(response)
def exec_command(self, command, bufsize=-1):
#print "Executing Command: "+command
chan = self._transport.open_session()
chan.exec_command(command)
stdin = chan.makefile('wb', bufsize)
stdout = chan.makefile('rb', bufsize)
stderr = chan.makefile_stderr('rb', bufsize)
return stdin, stdout, stderr
When executing a command in paramiko, it always resets the session when you run exec_command.
I want to able to execute sudo or su and still have those privileges when I run another exec_command.
Another example would be trying to exec_command("cd /") and then run exec_command again and have it be in the root directory. I know you can do something like exec_command("cd /; ls -l"), but I need to do it in separate function calls.
Non-Interactive use cases
This is a non-interactive example... it sends cd tmp, ls and then exit.
import sys
sys.stderr = open('/dev/null') # Silence silly warnings from paramiko
import paramiko as pm
sys.stderr = sys.__stderr__
import os
class AllowAllKeys(pm.MissingHostKeyPolicy):
def missing_host_key(self, client, hostname, key):
return
HOST = '127.0.0.1'
USER = ''
PASSWORD = ''
client = pm.SSHClient()
client.load_system_host_keys()
client.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
client.set_missing_host_key_policy(AllowAllKeys())
client.connect(HOST, username=USER, password=PASSWORD)
channel = client.invoke_shell()
stdin = channel.makefile('wb')
stdout = channel.makefile('rb')
stdin.write('''
cd tmp
ls
exit
''')
print stdout.read()
stdout.close()
stdin.close()
client.close()
Interactive use cases
If you have an interactive ssh use case, paramiko can handle it... I personally would drive interactive ssh sessions with scrapli.
Listing all the ways I can think of to use paramiko interactively:
See nagabhushan's answer which uses paramiko interactively
By default, ansible paramiko usage is configurable
By default, exscript ssh uses paramiko
By default, netmiko ssh uses paramiko
By default, scrapli ssh uses paramiko
I might have missed some libraries that use paramiko, but it should be clear that paramiko is used quite extensively by python libraries that control ssh sessions.
Try creating a command string separated by \n character. It worked for me.
For. e.g. ssh.exec_command("command_1 \n command_2 \n command_3")
Strictly speaking, you can't. According to the ssh spec:
A session is a remote execution of a program. The program may be a
shell, an application, a system command, or some built-in subsystem.
This means that, once the command has executed, the session is finished. You cannot execute multiple commands in one session. What you CAN do, however, is starting a remote shell (== one command), and interact with that shell through stdin etc... (think of executing a python script vs. running the interactive interpreter)
You can do that by invoking shell on the client and sending commands. Please refer here
The page has code for python 3.5. I have modified the code a bit to work for pythin 2.7. Adding code here for reference
import threading, paramiko
strdata=''
fulldata=''
class ssh:
shell = None
client = None
transport = None
def __init__(self, address, username, password):
print("Connecting to server on ip", str(address) + ".")
self.client = paramiko.client.SSHClient()
self.client.set_missing_host_key_policy(paramiko.client.AutoAddPolicy())
self.client.connect(address, username=username, password=password, look_for_keys=False)
self.transport = paramiko.Transport((address, 22))
self.transport.connect(username=username, password=password)
thread = threading.Thread(target=self.process)
thread.daemon = True
thread.start()
def close_connection(self):
if(self.client != None):
self.client.close()
self.transport.close()
def open_shell(self):
self.shell = self.client.invoke_shell()
def send_shell(self, command):
if(self.shell):
self.shell.send(command + "\n")
else:
print("Shell not opened.")
def process(self):
global strdata, fulldata
while True:
# Print data when available
if self.shell is not None and self.shell.recv_ready():
alldata = self.shell.recv(1024)
while self.shell.recv_ready():
alldata += self.shell.recv(1024)
strdata = strdata + str(alldata)
fulldata = fulldata + str(alldata)
strdata = self.print_lines(strdata) # print all received data except last line
def print_lines(self, data):
last_line = data
if '\n' in data:
lines = data.splitlines()
for i in range(0, len(lines)-1):
print(lines[i])
last_line = lines[len(lines) - 1]
if data.endswith('\n'):
print(last_line)
last_line = ''
return last_line
sshUsername = "SSH USERNAME"
sshPassword = "SSH PASSWORD"
sshServer = "SSH SERVER ADDRESS"
connection = ssh(sshServer, sshUsername, sshPassword)
connection.open_shell()
connection.send_shell('cmd1')
connection.send_shell('cmd2')
connection.send_shell('cmd3')
time.sleep(10)
print(strdata) # print the last line of received data
print('==========================')
print(fulldata) # This contains the complete data received.
print('==========================')
connection.close_connection()
cmd = 'ls /home/dir'
self.ssh_stdin, self.ssh_stdout, self.ssh_stderr = self.ssh.exec_command(cmd)
print self.ssh_stdout.read()
cmd2 = 'cat /home/dir/test.log'
self.ssh_stdin2, self.ssh_stdout2, self.ssh_stderr2 = self.ssh.exec_command(cmd2)
print self.ssh_stdout2.read()
You can run multiple command using the below technique. Use semicolon to separate the Linux commands
Eg:
chan.exec_command("date;ls;free -m")
If you wish each command to have an effect on the next command you should use:
stdin, stdout, stderr = client.exec_command("command1;command2;command3")
but in some cases, I found that when ";" doesn't work, using "&&" does work.
stdin, stdout, stderr = client.exec_command("command1 && command2 && command3")
You can execute an entire BASH script file for better use, here is the code for that:
import paramiko
hostname = "192.168.1.101"
username = "test"
password = "abc123"
# initialize the SSH client
client = paramiko.SSHClient()
# add to known hosts
client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
try:
client.connect(hostname=hostname, username=username, password=password)
except:
print("[!] Cannot connect to the SSH Server")
exit()
# read the BASH script content from the file
bash_script = open("script.sh").read()
# execute the BASH script
stdin, stdout, stderr = client.exec_command(bash_script)
# read the standard output and print it
print(stdout.read().decode())
# print errors if there are any
err = stderr.read().decode()
if err:
print(err)
# close the connection
client.close()
This will execute the local script.sh file on the remote 192.168.1.101 Linux machine.
script.sh (just an example):
cd Desktop
mkdir test_folder
cd test_folder
echo "$PATH" > path.txt
This tutorial explains this in detail: How to Execute BASH Commands in a Remote Machine in Python.
The code below runs grep in one machine through SSH and prints the results:
import sys, os, string
import paramiko
cmd = "grep -h 'king' /opt/data/horror_20100810*"
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect('10.10.3.10', username='xy', password='xy')
stdin, stdout, stderr = ssh.exec_command(cmd)
stdin.write('xy\n')
stdin.flush()
print stdout.readlines()
How can I grep five machines all at once (so that I don't have major delay), than put all that in five variables and print them all out.
You'll need to put the calls into separate threads (or processes, but that would be overkill) which in turn requires the code to be in a function (which is a good idea anyway: don't have substantial code at a module's top level).
For example:
import sys, os, string, threading
import paramiko
cmd = "grep -h 'king' /opt/data/horror_20100810*"
outlock = threading.Lock()
def workon(host):
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, username='xy', password='xy')
stdin, stdout, stderr = ssh.exec_command(cmd)
stdin.write('xy\n')
stdin.flush()
with outlock:
print stdout.readlines()
def main():
hosts = ['10.10.3.10', '10.10.4.12', '10.10.2.15', ] # etc
threads = []
for h in hosts:
t = threading.Thread(target=workon, args=(h,))
t.start()
threads.append(t)
for t in threads:
t.join()
main()
If you had many more than five hosts, I would recommend using instead a "thread pool" architecture and a queue of work units. But, for just five, it's simpler to stick to the "dedicated thread" model (especially since there is no thread pool in the standard library, so you'd need a third party package like threadpool... or a lot of subtle custom code of your own of course;-).
In my case i have to execute commands on server with one ip and port and after complete need to do sftp to other ip and different port.Condition is one connection should be live while doing sftp to another ip due to port forwarding.
Both connection are working separably but while combining both second sftp connection is not working.
#! /usr/bin/env python3
import sys, os, string, threading
try:
import paramiko
#import paramiko package
except:
im = input("Paramiko module is missing. Do you want to install[Y/N]:")
im = im.upper()
if im == "Y":
try:
try:
os.system("pip install paramiko")
except:
os.system("pip3 install paramiko")
except:
print("Please install paramiko package manually")
else:
print("Rerun and type 'y' to install")
#Running paramiko module with interactive password sending function
#this function helps to send password when sudo command is executed
def sudossh():
host = "type host ip"
port = 22
username = "type username"
password = "type password"
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, port, username, password)
ssh.get_transport()
#In this example we will run HTTP module on server in port 80
command = "sudo su -c 'sudo python -m SimpleHTTPServer 80'"
print(f"Running: {command}\n")
stdin, stdout, stderr = ssh.exec_command(command=command,get_pty=True)
stdin.write("password\n")
print("sent password\n")
print("HTTP service is running now\n")
stdin.flush()
if stderr.channel.recv_exit_status() != 0:
print(f"Error: {stderr.readlines()}")
else:
print(f"Output: \n{stdout.readlines()}")
ssh.close()
except Exception as err:
print(str(err));
print("Thanks for using my application");
#Running another paramiko module with interactive password sending function
def grepverification():
host = "type host ip"
port = 22
username = "type username"
password = "type password"
try:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(host, port, username, password)
ssh.get_transport()
#Open new session and check port 80 status on server
command = "sudo su -c 'netstat | grep 80'"
print(f"Running: {command}\n")
stdin, stdout, stderr = ssh.exec_command(command=command,get_pty=True)
stdin.write("password\n")
print("sent password\n")
print("Connection is established. Check below output\n")
stdin.flush()
if stderr.channel.recv_exit_status() != 0:
print(f"Error: {stderr.readlines()}")
else:
print(f"Output: \n{stdout.readlines()}")
ssh.close()
except Exception as err:
print(str(err));
print("Thanks for using my application");
def main():
#Multithreading helps to run both at a same time. Useful for verification.
# creating thread
th1 = threading.Thread(target=sudossh)
th2 = threading.Thread(target=grepverification)
# starting thread 1
th1.start()
# starting thread 2
th2.start()
# wait until thread 1 is completely executed
th1.join()
# wait until thread 2 is completely executed
th2.join()
# both threads completely executed
print("Completed!")
#you can use for loop to reduce lines but for understanding & smooth multithreading process will keep it as separate functions
#Comments are welcome. Thanks. Follow me on https://www.linkedin.com/in/dinesh-kumar-palanivelu-858441128/
#you need to change line - 23-26,36,51-54,64
if __name__=='__main__':
main()