How to execute 'su' command using parallel-ssh - python

I want to log in to two hosts using parallel-ssh and execute su command. Then I want to confirm that I am the root user by printing out whoami
Code:
hosts = ['myHost1', 'myHost2']
client = ParallelSSHClient(hosts, user='myUser', password='myPassword')
output = client.run_command('su')
for host in output:
stdin = output[host].stdin
stdin.write('rootPassword\n')
stdin.flush()
client.join(output)
output = client.run_command('whoami')
for host, host_output in output.items():
for line in host_output.stdout:
print("Host [%s] - %s" % (host, line))
Result:
Host [myHost1] - myUser
Host [myHost2] - myUser
Obviously, I expect root in the output. I am following the documentation.
I've tried using all different line endings instead of \n and nothing has changed.
How can I execute su command using parallel-ssh?

Try this:
**def exec_command(hosts):
strr = ""
client = ParallelSSHClient(hosts, user='admin', password='admin_password')
cmd = 'echo root_password |su -c "commmand" root'
output = client.run_command(cmd)
client.join()
for host_out in output:
for line in host_out.stdout:
strr+=line+" "
return strr
**
'echo root_password |su -c "command" root'

try to put sudo=True at the end of run_command
output = client.run_command(<..>, sudo=True)
like in docs

It turns out that what I am trying to do is not achievable.
The first problem
I found in this post that all commands are in their own channel. That means that even if su would be successful it wouldn't affect the second command. The author of the post recommends running
su -c whoami - root
The second problem
I managed to debug the problem even further by changing host_output.stdout to host_output.stderr It turned out that I receive an error which previously was not being shown on the terminal:
standard in must be a tty
Possible solutions to this problem are here . They didn't work for me but might work for you.
For me workaround was to allow on all my hosts root login. And then in parallel-ssh I log in as a root already with all the rights in place.

Related

SSH Command using Popen() odd results

I've been using the following code for a while now, however recently (in the last month) it's been failing:
# Make sure WireGuard starts at boot
systemd_cmd = 'ssh %s %s#%s "systemctl enable wg-quick#wg0"' % (SSH_OPTS, SSH_USER, get_gw_ret['pub_ipv4_addr'])
p = subprocess.Popen(start_wg_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
systemd_out = p.communicate()[1]
systemd_out_message = str(systemd_out)[2:-1]
A successful run of systemctl enable wg-quick#wg0 is the goal. The expected output is something like;
Created symlink /etc/systemd/system/multi-user.target.wants/wg-quick#wg0.service → /lib/systemd/system/wg-quick#.service.
However these days it returns:
wg-quick: `wg0\' already exists\\n
I've narrowed it down to the above python code since executing this command directly in a bash shell is always successful. The complete commands looks like:
ssh -o StrictHostKeyChecking=no root#10.0.0.8 "systemctl enable wg-quick#wg0"
Any ideas what might be happening?
Someone pointed out my typo:
- p = subprocess.Popen(start_wg_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
+ p = subprocess.Popen(systemd_cmd,shell=True,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
Thanks for reading!

how to type sudo password when using subprocess.call?

i defined a function that switch my proxy settings every now and then,
problem is that i want it to run in a loop without manual intervention. But when i execute the program in sudo it gets called the first time en runs smoothly, second time it asks me for my sudo password. Here is the bit of code:
def ProxySetting(Proxy):
print "ProxyStetting(Proxy)"
call("networksetup -setwebproxy 'Wi-Fi' %s" "on" % Proxy, shell = True)
call("networksetup -setsecurewebproxy 'Wi-Fi' %s" "on" % Proxy, shell = True)
call("networksetup -setftpproxy 'Wi-Fi' %s" "on" %Proxy , shell=True)
I could use threading but am sure there is a way of doing it that wont cause problems. How can i hard code my sudo password so that it runs at the beginning of the function?
Here you can execute a command sudo without interactive prompt asking you to type your password :
from subprocess import call
pwd='my password'
cmd='ls'
call('echo {} | sudo -S {}'.format(pwd, cmd), shell=True)
Another method of passing your password to a shell command through python that wouldn't involve it showing up in any command history or ps output is:
p = subprocess.Popen(['sudo', self.resubscribe_script], stdin=subprocess.PIPE)
p.communicate('{}\n'.format(self.sudo_password))
Note that using communicate will only allow one input to be given to stdin; there are other methods for getting a reusable input.

Python subprocess stdout.readlines() getting stuck

I am trying to use to python subprocess module in order to ssh to a server and then switch to a super user and then ls and print the folders in the terminal.
My Code:
def sudo_Test():
HOST = 'Host'
PORT = '227'
USER = 'user'
cmd='sudo su - ec2-user;ls'
process = subprocess.Popen(['ssh','-tt','{}#{}'.format(USER, HOST),
'-p',PORT,cmd],
shell=False,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE)
process.stdin.write("my_password\r\n")
print "stuck here VVV"
result = process.stdout.readlines()
print "finished it"
if not result:
print "Im an error"
err = process.stderr.readlines()
print('ERROR: {}'.format(err))
else:
print "I'm a success"
print result
print sudo_Test()
Console output when I run this:
dredbounds-computer:folder dredbound$ python terminal_test.py
stuck here VVV
For some reason the code gets stuck on the line result = process.stdout.readlines(). I have to cntrl+c to exit out of the terminal session when this happens. It works fine if I just do cmd='sudo; ls' instead of cmd='sudo su - ec2-user;ls'.Anyone know what I'm doing wrong or how I can get this to work?
Update:
I changed cmd='sudo su - ec2-user;ls' -> cmd='sudo su - ec2-user ls' in the code above. Now I am getting the following error:
['password\r\n', '\r\n', '/bin/ls: /bin/ls: cannot execute binary file\r\n']
I'm not sure why it thinks ls is a binary file now, but is there anyway i can tell it that it is just a terminal command so it returns a list of directories?
The problem is here:
sudo su - ec2-user;ls
sudo su - ec2-user opens a shell and waits for input. You need to send the shell an exit command (or close its stdin) before the shell exits and the ls command is run.
If your goal was to run ls as user ec2-user, then try:
sudo -u ec2-user ls
In other words, replace
cmd='sudo su - ec2-user;ls'
With:
cmd='sudo -u ec2-user ls'

setting session variable for paramiko session

Does anyone know how to make environment variables registered for
exec_command calls when using SSHClient?
I'm using a basic script that instantiates the SSHClient class, connects to another computer using the connect method, then sends out commands using the exec_command method. However, none of the environment variables seem to be registered when I try to issue commands. I can do basic things like 'ls' and see the stdout, but when trying to run installed programs, the fact that the environment variables are missing makes it impossible to run them. Using ssh in the command line to do the same thing works, as the environment variables for the user are set.
#!/usr/bin/python
import paramiko
ssh.connect('mymachine',username='myname',password='pass')
stdin,stdout,stderr=ssh.exec_command('cd /myfolder/path')
stdin,stdout,stderr=ssh.exec_command('ls')
....
....
ssh.close()
Note: I can't change my directory in paramiko. I appended the cd command in the followup command in a single ssh.exec_command('cd /dddd/ddd;ls'). I have given ls as an example but my actual followup command is different.
Since release 2.1.0 2016-12-09 , you can add an environment variable dictionary to the exec_command:
import paramiko
paramiko.util.log_to_file("paramiko.log")
ssh = paramiko.SSHClient()
k = paramiko.RSAKey.from_private_key_file("<private_key_file>")
ssh.connect(<hostname>,username=<username>,pkey=k)
env_dict={"LC_TELEPHONE":"ET_HOME","LC_MEASUREMENT":"MILES_APART"}
stdin , stdout, stderr = ssh.exec_command('echo $LC_TELEPHONE; echo "..."; echo $LC_MEASUREMENT',environment=env_dict)
print stdout.read()
output:
ET_HOME
...
MILES_APART
But why did I choose LC_TELEPHONE and LC_MEASUREMENT? Because those are two of the few environments that the target host's ssh config allows me to set:
grep AcceptEnv /etc/ssh/sshd_config
output:
AcceptEnv LANG LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES
AcceptEnv LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT
AcceptEnv LC_IDENTIFICATION LC_ALL
In other words, this doesn't work:
env_dict={"HELLO":"WORLD","GOODBYE":"CRUEL_WORLD"}
stdin , stdout, stderr = ssh.exec_command("echo $HELLO; echo '...'; echo $GOODBYE")
print stdout.read()
output:
...
As the documentation warns, the environment variables are silently rejected
http://docs.paramiko.org/en/2.1/api/client.html
http://docs.paramiko.org/en/2.1/api/channel.html#paramiko.channel.Channel.set_environment_variable
If you cannot control the target server's sshd config, putting the environment variables into a file and sourcing it works:
stdin , stdout, stderr = ssh.exec_command("cat .set_env;source .set_env; echo $HELLO; echo '...'; echo $GOODBYE")
print stdout.read()
output:
# begin .set_env
HELLO="WORLD"
GOODBYE="CRUEL_WORLD"
# end .set_env
WORLD
...
CRUEL_WORLD
#!/usr/bin/python
import paramiko
client = paramiko.SSHClient()
client.load_system_host_keys()
client.set_missing_host_key_policy(paramiko.WarningPolicy)
client.connect(myhostname, theport, myuser, thepass)
stdin,stdout,stderr = client.exec_command('cd /tmp;pwd;ls -al')
#returns your output
print stdout.read()
which all works fine for me. If you have special environment Variables you might
have to set them on the remote command prompt. Maybe it helps if you write the
variables into a myENV file and then call
stdin,stdout,stderr = client.exec_command('source ./myEnv')
Did you tried something like that?
You can do: client.exec_command(..., get_pty=True).
This will make paramiko allocate a pseudo terminal, similar to ssh.
I found this problem too. And besides the above approaches, I also fixed the problem by using the following approach:
e.g.,
...
bin_paths = '/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin'
path_prefix = 'PATH=$PATH:%s && ' % bin_paths
command = path_prefix + command
ssh.exec_command(command=command)

Interface with remote computers using Python

I've just become the system admin for my research group's cluster and, in this respect, am a novice. I'm trying to make a few tools to monitor the network and need help getting started implementing them with python (my native tongue).
For example, I would like to view who is logged onto remote machines. By hand, I'd ssh and who, but how would I get this info into a script for manipulation? Something like,
import remote_info as ri
ri.open("foo05.bar.edu")
ri.who()
Out[1]:
hutchinson tty7 2009-08-19 13:32 (:0)
hutchinson pts/1 2009-08-19 13:33 (:0.0)
Similarly for things like cat /proc/cpuinfo to get the processor information of a node. A starting point would be really great. Thanks.
Here's a simple, cheap solution to get you started
from subprocess import *
p = Popen('ssh servername who', shell=True, stdout=PIPE)
p.wait()
print p.stdout.readlines()
returns (eg)
['usr pts/0 2009-08-19 16:03 (kakapo)\n',
'usr pts/1 2009-08-17 15:51 (kakapo)\n',
'usr pts/5 2009-08-17 17:00 (kakapo)\n']
and for cpuinfo:
p = Popen('ssh servername cat /proc/cpuinfo', shell=True, stdout=PIPE)
I've been using Pexpect, which let's you ssh into machines, send commands, read the output, and react to it, with success. I even started an open-source project around it, Proxpect - which haven't been updated in ages, but I digress...
The pexpect module can help you interface with ssh. More or less, here is what your example would look like.
child = pexpect.spawn('ssh servername')
child.expect('Password:')
child.sendline('ABCDEF')
(output,status) = child.sendline('who')
If your needs overgrow simple "ssh remote-host.example.org who" then there is an awesome python library, called RPyC. It has so called "classic" mode which allows to almost transparently execute Python code over the network with several lines of code. Very useful tool for trusted environments.
Here's an example from Wikipedia:
import rpyc
# assuming a classic server is running on 'hostname'
conn = rpyc.classic.connect("hostname")
# runs os.listdir() and os.stat() remotely, printing results locally
def remote_ls(path):
ros = conn.modules.os
for filename in ros.listdir(path):
stats = ros.stat(ros.path.join(path, filename))
print "%d\t%d\t%s" % (stats.st_size, stats.st_uid, filename)
remote_ls("/usr/bin")
If you're interested, there's a good tutorial on their wiki.
But, of course, if you're perfectly fine with ssh calls using Popen or just don't want to run separate "RPyC" daemon, then this is definitely an overkill.
This covers the bases. Notice the use of sudo for things that needed more privileges. We configured sudo to allow those commands for that user without needing a password typed.
Also, keep in mind that you should run ssh-agent to make this "make sense". But all in all, it works really well. Running deploy-control httpd configtest will check the apache configuration on all the remote servers.
#!/usr/local/bin/python
import subprocess
import sys
# The user#host: for the SourceURLs (NO TRAILING SLASH)
RemoteUsers = [
"deploy#host1.example.com",
"deploy#host2.appcove.net",
]
###################################################################################################
# Global Variables
Arg = None
# Implicitly verified below in if/else
Command = tuple(sys.argv[1:])
ResultList = []
###################################################################################################
for UH in RemoteUsers:
print "-"*80
print "Running %s command on: %s" % (Command, UH)
#----------------------------------------------------------------------------------------------
if Command == ('httpd', 'configtest'):
CommandResult = subprocess.call(('ssh', UH, 'sudo /sbin/service httpd configtest'))
#----------------------------------------------------------------------------------------------
elif Command == ('httpd', 'graceful'):
CommandResult = subprocess.call(('ssh', UH, 'sudo /sbin/service httpd graceful'))
#----------------------------------------------------------------------------------------------
elif Command == ('httpd', 'status'):
CommandResult = subprocess.call(('ssh', UH, 'sudo /sbin/service httpd status'))
#----------------------------------------------------------------------------------------------
elif Command == ('disk', 'usage'):
CommandResult = subprocess.call(('ssh', UH, 'df -h'))
#----------------------------------------------------------------------------------------------
elif Command == ('uptime',):
CommandResult = subprocess.call(('ssh', UH, 'uptime'))
#----------------------------------------------------------------------------------------------
else:
print
print "#"*80
print
print "Error: invalid command"
print
HelpAndExit()
#----------------------------------------------------------------------------------------------
ResultList.append(CommandResult)
print
###################################################################################################
if any(ResultList):
print "#"*80
print "#"*80
print "#"*80
print
print "ERRORS FOUND. SEE ABOVE"
print
sys.exit(0)
else:
print "-"*80
print
print "Looks OK!"
print
sys.exit(1)
Fabric is a simple way to automate some simple tasks like this, the version I'm currently using allows you to wrap up commands like so:
run('whoami', fail='ignore')
you can specify config options (config.fab_user, config.fab_password) for each machine you need (if you want to automate username password handling).
More info on Fabric here:
http://www.nongnu.org/fab/
There is a new version which is more Pythonic - I'm not sure whether that is going to be better for you int his case... works fine for me at present...

Categories