I have a funtion which builds a ssh sommand lines and passes this to popen so I can run commands on a remote box ( I know I could use Fabirc or paramiko but for interests sake I wanted to do this).
so the fuction call is a wrapper arround Poepn (partly)
def runcmd(cmd, host=None, stdout=None, stderr=None, both=None, shell=None, user=None)
I invoke it for test with
runcmd('cd ~/app-scripts; grep "below and commment" *',user="testuser")
and the command it build up is shown as
ssh -F /home/testuser/.ssh/config testuser#localhost 'cd ~/app-scripts; grep "below and commment" *'
This runs on the "remote" host fine and the stdout correctly show the results --
however the stderr also contains all the login messages from the remote host.
The internal fuction call to Popen is
p = Popen(sshcmd,stdout=PIPE,stderr=PIPE,shell=True)
stdout_data,stderr_data = p.communicate()
What am i missing?
Related
I am try to use Python to set up a reverse SSH tunnel. Some software that starts with the system is going to manage it and kill it or start it based on commands it receives.
I have written a class to manage the reverse tunnel as follows:
# imports omitted for brevity
class SshProcess():
def __init__(self):
self.process = None
def start(self, port):
if self.process is not None:
return None
command = [
# 'sudo',
'ssh',
'-R {port}:127.0.0.1:22'.format(port=port),
'{username}#{host}'.format(username=config.USERNAME, host=config.HOST),
'-o StrictHostKeyChecking=no'
]
def threaded_popen():
self.process = subprocess.Popen(
(' '.join(command)), # command, # shlex.split(command),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
shell=True
)
self.process.wait()
logger.info('Reverse SSH to {username}#{host} has exited'.format(username=config.USERNAME, host=config.HOST))
logger.debug('command raw: {command}'.format(command=command))
logger.debug('command joined: {command}'.format(command=(' '.join(command))))
self.thread = Thread(target=threaded_popen)
self.thread.start()
def stop(self):
if self.process is not None:
try:
self.process.communicate(input="exit\n")
self.process.terminate()
except (ValueError, OSError) as e:
logger.warning('Closing reverse SSH raised {error}'.format(error=e.__class__.__name__))
logger.warning(e)
self.process = None
if self.thread is not None:
self.thread.join()
Now whenever I call start I receive the following log statements:
2017-06-28 14:32:46,343 - module - DEBUG - command raw: ['ssh', '-R 4000:127.0.0.1:22', 'tich#192.168.0.88', '-o StrictHostKeyChecking=no']
2017-06-28 14:32:46,344 - module - DEBUG - command joined: ssh -R 4000:127.0.0.1:22 tich#192.168.0.88 -o StrictHostKeyChecking=no
2017-06-28 14:32:46,797 - module - INFO - Reverse SSH to tich#192.168.0.88 has exited
The issue is the ssh tunnel exits nearly instantly after starting. performing a simple pidof ssh in Linux gives no output as if the process does not even exist.
I have also tried using communicate() after starting the process and you can see it establishes the connection and receives output. However shortly after the function exits, the subprocess exits as well.
I have set up RSA keypairs for both the root and the regular user. Copying and pasting the command into a terminal does not produce the instant exit bug.
The purpose is setting up a reverse SSH session so a remote user can log in. But I currently have not found an existing packaged solution that offers this functionality.
You done some weird ssh connection.My advice is to use paramiko a great ssh package.
on the other hand, you are sub-processioning only for a linux commamd so if u like it like that use:
install sshpass (yum install or apt-get)
sshpass -p your_password ssh user#hostname
and change this setting instead of the flag u sent:
change ssh_config
vi /etc/ssh/ssh_config
change the below key from "ask" to "no"
StrictHostKeyChecking no
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
I'm trying to send command using ssh spawn to remote machine.
I'm sending the command using Popen() and I can see the command was done but after that I'm trying to use communicate() (to close the session and get a return code) and the program get stuck.
cmd = """expect -c 'spawn ssh -o LogLevel=quiet """ \
"""-o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -o PreferredAuthentications=publickey """ \
"""-o ConnectTimeout=10 -2 -o CheckHostIP=no -i {0} {1}#{2} ;\
"""expect "#";send "mkdir test9\n" ;interact'""".format(self.sshKey, SSHUser, self.hostname)
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
p_stdoutdata, p_stderrdata = process.communicate()
When I'm reaching the communicate the program gets stuck.
Don't use Popen to call ssh. Instead, use a library that does it all in-process in Python, such as the excellent Paramiko. In your case it might look something like this:
import paramiko
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.WarningPolicy())
client.connect(self.hostname, 22, SSHUser, password)
sftp = paramiko.SFTPClient.from_transport(client.get_transport())
sftp.mkdir("test9")
sftp.close()
client.close()
I need to ssh into the server and execute few commands and process the response using subprocess. Here's my code
command = 'ssh -t -t buildMachine.X.lan; sudo su - buildbot ; build-set sets/set123'
print "submitting command"
result = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stdin=subprocess.PIPE)
print "got response"
response,err = result.communicate()
print response
This is getting stuck. I have seen other threads talking about passing a list instead of string to subprocess and removing shell=True.. I did that too but didn't work.
Ultimately I need the result of last command i.e. build-set in order to extract some information out of it.. help?
I figured the solution by using univerio's comment
The command needs to be
command = 'ssh -t -t buildMachine.X.lan \'sudo su - buildbot \'build-set sets/set123\'\''
Individual commands are like argument to previous command. This works.
I am finding hard to run a process on a remote SSH server at background using Paramiko. I used :
stdin, stdout, stderr = ssh.exec_command('executefile.py &')
and found that no process of executefile.py was found running.
Then I tried using other way as including a backward slash:
stdin, stdout, stderr = ssh.exec_command('executefile.py \&')
This method worked. There was an instance running on machine but no surprise, it was not running at background. I could come to know as it is not running at background as when code stuck at second line after this code. It was
all_inf = stdout.readlines()
Now code was not going beyond above line unless the process of the script was killed.
I am learning Paramiko, any help is appreciated.
I've tried all the methods described here and here without success, and finally realized that you need to use channels instead of using the SSHClient directly for calling exec_command (this does not work in background):
client = paramiko.SSHClient()
client.connect(ip_address, username='root', pkey=paramiko_key, timeout=5)
client.exec_command('python script.py > /dev/null 2>&1 &')
You should create and use a channel, this works in background:
client = paramiko.SSHClient()
client.connect(ip_address, username='root', pkey=paramiko_key, timeout=5)
transport = client.get_transport()
channel = transport.open_session()
channel.exec_command('python script.py > /dev/null 2>&1 &')
So nohup, dtach, screen, etc, are actually not necessary.
You can try:
stdin, stdout, stderr = ssh.exec_command('nohup python executefile.py >/dev/null 2>&1 &')
exec_command isn't executing the command in an interactive shell, so "running a process in the background" doesn't really make sense.
If you really want to do this, you could use the command nohup to start your process, and keep it alive when the session exits. Remember that you can't get stdin, stdout, or stderr when you do this, since you are detaching the process from the shell, so redirect them accordingly.
I tried transport class and it was really great. Here's the code I used:
ssh = paramiko.SSHClient()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
ssh.connect(hostname = "host_ip", username = "un"], password = "up")
channel = ssh.get_transport().open_session()
pty = channel.get_pty()
shell = ssh.invoke_shell()
shell.send("cd /my/directory/; nohup ./exec_name > /dev/null 2>&1 &\n")
But I still don't know how to kill it using python scripts; I have an open question about it here.
EDIT 1:
I have solved my problem about killing the process somehow; you can check it.
You could try using screen
screen -d -m ping 8.8.8.8
This is would start a screen and ping 8.8.8.8. You can view this screen by using
screen -ls
and attach using
screen -D <<screen_name>>
Note that the screen will terminate after the command has finished executing.