Python subprocess, execute shell command which prompts for password - python

I am using subprocess in python to execute a custom vpn command which excepts password to execute.
below is the command which asks password:
./vpn -u <user_id> -d "description" /var/tmp/1.txt
password: XXXX
below is vpn shell command which works perfectly for above command.
/usr/bin/expect -c 'spawn ./vpn -u <user_id> -d "description" /var/tmp/1.txt; expect "Password"; send "<Password here>\r"; interact'
In python I am trying to achieve the same with below subprocess module, where below script executes below is the output.
"(b"\x1b[31m\xe2\x9c\x97 Can't read 'user id' password from the console.\x1b[0m""
import subprocess
args = ["./vpn", "-u", "<user_id>", "-d", "description", "/var/tmp/1.txt"]
# args = ['sudo','cat', '/var/tmp/1.txt']
proc = subprocess.Popen(args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
proc.stdin.write('<passowrd>'.encode())
stdout, stderr = proc.communicate()
print(stdout)
Note: if I use time.sleep(2) before writing password, it prompts me for password in console without any error and my goal is to give password in the script.
Is there any better way in python to pass the shell command as this is pretty lengthy OR provide password in subprocess module execute seamlessly.

You are using Expect for Shell/Bash CLI command. When you use Python, just use pexpect to get the same functionality in Python.
Explore pexpect at https://github.com/pexpect/pexpect
Example passing password at executed program is here: https://github.com/pexpect/pexpect/blob/master/examples/passmass.py
Example:
import pexpect
child = pexpect.spawn('ftp ftp.openbsd.org')
child.expect('Name .*: ')
child.sendline('anonymous')
child.expect('Password:')
child.sendline('noah#example.com')

I tried in shell script and it works well: Just for anyone if it will be help full:
/usr/bin/expect<<vpn
spawn ./vpn -u <user_id> -d "description" /var/tmp/1.txt
expect "Password"
send "<password here>\r"
interact
sleep 5
vpn
echo "It's done"

Related

mailutils not working via subprocess.run in python

I am trying to send a mail via python's subprocess.run methode. Unfortunately, it is not working.
import subprocess
message = "Hello World"
process = subprocess.run(["mail", "-s", "Test, "xyz#xyz.com", "<<<", message],
stdout=subprocess.PIPE,
universal_newlines=True)
print (process.stdout)
I received the following Error:
mail: Cannot parse address <<<' (while expanding<<<'): Malformed email address
mail: Cannot parse address Hello World' (while expandingHello World'): Malformed email address
The command is working in the shell though (Linux Mint > 19.0).
The <<< syntax is a feature of bash. If you want to use that you need to run your command as an argument of the bash shell:
import subprocess
message = "Hello World"
command = "mail -s Test abc#def.com <<< "+message
process = subprocess.run(
["bash","-c",command],
stdout=subprocess.PIPE,
universal_newlines=True)
print (process.stdout)
However, using shell expansion on dynamic content can be a security issue. A better way is, to use the input feature of subprocess.run ( python3 only )
import subprocess
message = "Hello World"
command = ["mail", "-s", "Test", "abc#def.com"]
process = subprocess.run(
command,
input=message,
stdout=subprocess.PIPE,
universal_newlines=True)
print (process.stdout)
See also Python - How do I pass a string into subprocess.Popen (using the stdin argument)?

python subprocess and wget ask-password

I'm running the following python code:
import subprocess
host = "ftp://localhost:2121"
p = subprocess.Popen(
['wget', '-P', '/tmp/output', '-N', '-r', '-l', 'inf', '--ask-password', '--user', 'anonymous', host],
stdin=subprocess.PIPE)
p.communicate("password\n")
if p.returncode != 0:
raise RuntimeError('wget command failed with return code: %d' % p.returncode)
It appears that password is not sent to wget since the script will hang showing:
Password for user ‘anonymous’:
Pressing enter causes wget to exit with an error code of '1'
Pressing keys then pressing enter causes wget to begin downloading as expected.
The ftp server is local and has anonymous access enabled. Python version is 2.7.8
Try adding this before p.communicate:
p.stdin.write('password\n')
p.stdin.flush()
stdout, stderr = p.communicate()
It appears that wget reads the password from the tty rather than stdin. These are not the same!
The sudo command allows you to switch to stdin
also
Certain programs read directly from /dev/tty, not stdin. "passwd" for example. So it's difficult to script them. Expect is one way around that - it can trick the program by providing input to them:
One way to achieve reading the password from stdin is to construct a url containing the user & pass:
ftp://user:password#ftp.server.com/link.txt
And using the -l - switch to pass the link into wget via stdin. This is suggested here

How can I use Python to automate setting a password using the Unix pass command line program

I'm trying to automate setting new passwords using the Unix pass program.
I understand that there is a Python library, pexpect, that might help, but I would like to avoid using third-party libraries.
When using a terminal, the flow looks like this:
$ pass insert --force gmail
>> Enter password for gmail: <type in password using masked prompt>
>> Retype password for gmail: <reenter password>
What I would like my function to do:
Run the command pass insert --force {entry_name}
Capture the output (and echo it for testing)
Check output for the presence of 'password for gmail', and if True
write '{password}\n' to stdin
write '{password}\n' to stdin again
Echo any errors or messages for testing
Issues:
I'm stuck on step 2. The subprocess either hangs indefinitely, times out with an error, or produces no output.
Attempts:
I've tried configurations of Popen(), using both stdin.write() and communicate().
I've set wait() calls at various points.
I've tried both the shell=True and shell=False options (prefer False for security reasons)
Code:
def set_pass_password(entry_name, password):
from subprocess import Popen, PIPE
command = ['pass', 'insert', '--force', entry_name]
sub = Popen(command, stdin=PIPE, stdout=PIPE, stderr=PIPE, universal_newlines=True)
# At this point I assume that the command has run, and that there is an "Enter password..." message
message = sub.stdout.read() # also tried readline() and readlines()
print(message) # never happens, because process hangs on stdout.read()
if 'password for {}'.format(entry_name) in message:
err, msg = sub.communicate(input='{p}\n{p}\n'.format(p=password))
print('errors: {}\nmessage: {}'.format(err, msg))
Edit: the original answer was about passwd, which is what's used to set passwords. I noticed late that you use pass, which is a keystore (doesn't actually change the Unix password). The pass program works differently and will not print a prompt if stdin is not a tty. Therefore the following very simple program works:
def set_pass_password(entry_name, password):
from subprocess import Popen, PIPE
command = ['pass', 'insert', '--force', entry_name]
sub = Popen(command, bufsize=0, stdin=PIPE, stdout=PIPE, stderr=PIPE)
err, msg = sub.communicate(input='{p}\n{p}\n'.format(p=password))
print('errors: {}\nmessage: {}'.format(err, msg))
if __name__ == "__main__":
set_pass_password("ttt", "ttt123asdqwe")
(you will see that both stderr and stdout are empty, if the command succeeded).
For the passwd command:
FYI: the passwd command outputs the prompt to stderr, not stdout.
NOTE: rather than sending the password twice in the same 'write', you might need to wait for the second prompt before sending the password again.
For this simple case, code similar to yours should work, but in general you should use select on all the pipes and send/receive data when the other side is ready, so you don't get deadlocks.

using subprocess to ssh and execute commands

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.

subprocess timeout when exceeds a certain time limit

I'm using the subprocess module of python to run an ssh command over my servers for collecting their disk usage. The one thing on which i'm stuck is if the ssh is not configured in any server then subprocess prompt for the password input which makes my whole script stuck and then i have to voluntarily kill the script itself. I just want it to let go all the servers which asks for password prompt(where ssh is not configured) and continue processing the rest.
def MyFunction(server):
msg=""
ps = subprocess.Popen("ssh -l mygroup %s 'df -k /some/directory'" % server,stdout=subprocess.PIPE,shell=True)
out, err = ps.communicate()
if err != None:
msg += "\n"+err
else:
msg = out
return msg
server_list= ['server A','server B','server C','server D']
for server in server_list:
Final_msg+=MyFunction(server)
Any help would be appreciated! :)
If it is just the thing that you want to avoid ssh ask you for anything, then you can forbid it to do so.
You can use the SSH option
BatchMode
If set to “yes”, passphrase/password querying will be disabled.
This option is useful in scripts and other batch jobs where no user is present to supply the password.
The argument must be “yes” or “no”. The default is “no”.
So just add -o BatchMode=yes:
ps = subprocess.Popen("ssh -o BatchMode=yes -l mygroup %s 'df -k /some/directory'" % server, stdout=subprocess.PIPE, shell=True)
BTW, why do you need shell=True here? Better do
ps = subprocess.Popen(["ssh", "-o", "BatchMode=yes", "-l", "mygroup", server, "df -k /some/directory"], stdout=subprocess.PIPE)
as it is cleaner, safer and internally simpler.

Categories

Resources