I am trying to automate the setup of generating self-signed SSL certificate. This is my code:
#!/usr/bin/env python
import subprocess
pass_phrase = 'example'
common_name = 'example.com'
webmaster_email = 'webmaster#example.com'
proc = subprocess.Popen(['openssl', 'req', '-x509', '-newkey', 'rsa:2048', '-rand', '/dev/urandom', '-keyout', '/etc/pki/tls/private/server.key', '-out', '/etc/pki/tls/certs/server.crt', '-days', '180'], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
for i in range(2):
proc.stdin.write(pass_phrase)
for i in range(5):
proc.stdin.write('.')
proc.stdin.write(common_name)
proc.stdin.write(webmaster_email)
proc.stdin.flush()
stdout, stderr = proc.communicate()
When I run it, it still prompts me for the PEM passphrase, then returns this error:
Country Name (2 letter code) [XX]:weird input :-(
problems making Certificate Request
It should feed in the passphrase above and not prompt me for anything. Any ideas what's going wrong?
PS. I know about pexpect. Please don't suggest it to me.
Edit: Upon further investigation, I've figured it out. If you don't specify -nodes, the private key will be encrypted. So, OpenSSL will prompt for a PEM passphrase immediately. This means the order of my stdin.write() gets messed up. I guess the alternative is to use -nodes and encrypt the private key later.
There are several errors in your code e.g., no newlines are sent to the child process.
The main issue is that openssl expects the pass phrase directly from the terminal (like getpass.getpass() in Python). See the first reason in Why not just use a pipe (popen())?:
First an application may bypass stdout and print directly to its
controlling TTY. Something like SSH will do this when it asks you for
a password. This is why you cannot redirect the password prompt
because it does not go through stdout or stderr.
pexpect that provides pseudo-tty works fine in this case:
#!/usr/bin/env python
import sys
from pexpect import spawn, EOF
pass_phrase = "dummy pass Phr6se"
common_name = "example.com"
email = "username#example.com"
keyname, certname = 'server.key', 'server.crt'
cmd = 'openssl req -x509 -newkey rsa:2048 -rand /dev/urandom '.split()
cmd += ['-keyout', keyname, '-out', certname, '-days', '180']
child = spawn(cmd[0], cmd[1:], timeout=10)
child.logfile_read = sys.stdout # show openssl output for debugging
for _ in range(2):
child.expect('pass phrase:')
child.sendline(pass_phrase)
for _ in range(5):
child.sendline('.')
child.sendline(common_name)
child.sendline(email)
child.expect(EOF)
child.close()
sys.exit(child.status)
An alternative is to try to use -passin option to instruct openssl to get the pass phrase from a different source (stdin, a file, pipe, envvar, command-line). I don't know whether it works with openssl req command.
Two problems:
You are not giving it the data it expects in the order it expects. At some point it is expecting a country code and you are giving it some other data instead.
The write() method of file objects does not automatically insert a newline. You need to add "\n" to your strings or write() separate "\n" strings out after each line of input you want to feed to the program. For example: proc.stdin.write(pass_phrase + "\n")
Related
I want to call a .sh file from a python script. This requires sudo permissions and I want to automatically pass the password without getting a prompt. I tried using subprocess.
(VAR1 is variable I want to pass, permissions.sh is the sh file I want to call from python script)
process = subprocess.Popen(['sudo', './permissions.sh', VAR1], stdin = subprocess.PIPE, stdout = subprocess.PIPE)
process.communicate(password)
Then I tried using pexpect
child = pexpect.spawn('sudo ./permissions.sh'+VAR1)
child.sendline(password)
In both cases it still prompts for password on the terminal. I want to pass the password automatically. I do not want to use os modules. How can this be done?
would use pexpect, but you need to tell it what to expect after the sudo as so:
#import the pexpect module
import pexpect
# here you issue the command with "sudo"
child = pexpect.spawn('sudo /usr/sbin/lsof')
# it will prompt something like: "[sudo] password for < generic_user >:"
# you "expect" to receive a string containing keyword "password"
child.expect('password')
# if it's found, send the password
child.sendline('S3crEt.P4Ss')
# read the output
print(child.read())
# the end
# use python3 for pexpect module e.g python3 myscript.py
import pexpect
# command with "sudo"
child = pexpect.spawn('sudo rm -f')
# it will prompt a line like "abhi#192.168.0.61's password:"
# as the word 'password' appears in the line pass it as argument to expect
child.expect('password')
# enter the password
child.sendline('mypassword')
# must be there
child.interact()
# output
print(child.read())
I want to execute a mysqldump in python and provide the password when it is requested from the mysqldump.
Adding the password in the command line is not an option, it must be provided via stdin.
This is what I've done so far:
command = [
'mysqldump',
'-h', mysqlhost,
'-P', mysqlport,
'-u', mysqluser,
'-p',
mysqldb
]
mysqlfile = mysqlpath + "/" + mysqldb + ".sql"
with open(mysqlfile, "w+") as file:
p = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=file)
p.communicate(input=mysqlpass)
p.wait()
But when I execute the code the terminal hangs requesting the password.
Thank you.
You can use pexpect for that. This is modified code as I had to test it, but you get the idea:
import pexpect
command2 = 'mysqldump -h localhost -u root -p xyzzy'
mysqlfile = "/tmp/foo.sql"
with open(mysqlfile, "w+") as file:
p = pexpect.spawn(command2)
p.expect("Enter password: ")
p.sendline("foobar")
q = p.read()
p.wait()
file.write(q)
here "foobar" is my database password.
Hannu
For me, the accepted answer did not solve the problem. Presumably it is related to the python version I am using, which is 3.5.
The difficulties I had:
p.read() was blocking the process (I always killed the script at some point)
The chunk-approach by David Rojo did not block, but .read(1024) returned integers, where strings where expected by file.write(...). I assume this is related to differences in the way unicode is handled in Python 2 and 3, since adding the parameter encoding='utf-8' to pexpect.spawn() gave me the proper results. However, then I had to adapt the writing of the file, s.t. it supports unicode as well.
Another problem with the for chunk in p.read(1024):-approach is, that I experienced the reading to finish before mysqldump finished writing the dump to stdout. I guess that in this case mysqldump was too slow to deliver. I changed my solution, s.t. it waits for EOF.
Note: I just started learning python a couple of days ago, please correct me if my assumptions or conclusions are wrong or misleading.
Code example
The script below is my minimal working example for calling mysqldump and providing the password when mysqldump asks for it:
#!/usr/bin/env python3
import pexpect
import io
cmd = 'mysqldump -u MYSQL_USER -p DATABASES(S)'
sqlfile = "/home/user/test-database-dump.sql"
password = 'secret'
with io.open(sqlfile, 'w', encoding="utf-8") as file:
print('Calling mysqldump...')
p = pexpect.spawn(cmd,encoding='utf-8')
p.expect("Enter password: ")
# Send password to mysqldump
p.sendline(password)
# Capture the dump
print('Reading dump from process and writing it to file...')
while not p.eof():
chunk = p.readline()
file.write(chunk)
print('Finished.')
p.close()
print(p.exitstatus, p.signalstatus)
I am looking for a way to capture (and verify) the user login output (i.e. the MOTD located in issues.net) from an ssh session using the subprocess module in Python. I’ve tried several variations of the following but have yet to find a way that traps the desired output without either hanging the session or returning only the passed (i.e. “ls –la”) command’s output. I’m using Python 2.6 and have a requirement to use only the native libraries available at this installation (Red Hat 6.5), so modules such as pexpect are currently unavailable to me.
The code below only returns the “ls –la” output, and not the desired ssh login message. NOTE: "testUser" utilizes a PKI, thus obviating the need for handling passwords.
loginStr = ['ssh', testUser#someHost, "ls -la"]
p = subprocess.Popen(loginStr, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while True:
line = p.stdout.readline()
if not line: break
print line
I’ve also tried this with similar outcomes:
loginStr = ['ssh', testUser#someHost, 'ls', '-la']
p = subprocess.Popen(loginStr, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(stdout, stderr) = p.communicate()
print stdout
Might queues and threads be a solution?. Any ideas would be greatly appreciated
You could be using python expect (pexpect)
Something as follows (replace, host, user, passwd appropriately):
(also, adjust according to the regular expression for the shell prompt)
import pexpect
cmd = "ssh -o StrictHostKeyChecking=no %s -l %s" % (<host>, <user>)
exp = pexpect.spawn(cmd, timeout=7)
idx = exp.expect('assword:')
nc = exp.sendline(<passwd>)
idx = exp.expect('[\n\r](#|\$) ')
if idx == 0:
before = exp.before
print before
I have command line program what prompts password:
> cwrsync root#NN.NN.NN.NN:/src /cygdrive/c/dst
Output (when i run it from cmd.exe command line):
root#NN.NN.NN.NN's password:
When i input password manually, all OK. Output:
skipping directory src
I want to provide password for it from command line or python script automatically.
I tried:
One. From command line:
> echo pass|cwrsync -r root#NN.NN.NN.NN:/src /cygdrive/c/dst
Not working. Output:
root#NN.NN.NN.NN's password:
Two. From python script. test.py:
import subprocess
cmd = "cwrsync -r root#NN.NN.NN.NN:/src /cygdrive/c/dst"
proc = subprocess.Popen(cmd1, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE, shell=True)
std1, std2 = proc.communicate("pass")
print std1print std2
Not workin. Output:
Permission denied, please try again.
Permission denied, please try again.
Permission denied (publickey,password).
rsync: connection unexpectedly closed (0 bytes received so far) [Receiver]
rsync error: unexplained error (code 255) at io.c(235) [Receiver=3.1.1]
It is common that security oriented programs ask for password on direct io instead of reading stdin. And as :
echo pass|cwrsync -r root#NN.NN.NN.NN:/src /cygdrive/c/dst
did ask password, I presume that csrsync directly reads from console.
In that case you cannot automate it without some work and low level programming, because you will have to simulate keyboard actions. You should instead search the documentations, because as it looks like it uses an underlying ssh, it is likely to accept a public key pair. If it accept one without passphrase, you should be able to automate it.
Try sending a newline in your stdin string communicate call like so:
import subprocess
cmd = ['cwrsync', '-r', 'root#NN.NN.NN.NN:/src', '/cygdrive/c/dst']
proc = subprocess.Popen(cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
stdin=subprocess.PIPE,
shell=True)
std1, std2 = proc.communicate("pass\r\n\r\n")
print std1
print std2
You should also see if it works with shell=False (from subprocess docs):
Using shell=True can be a security hazard. See the warning under Frequently Used Arguments for details.
I am trying to do a CVS login from Python by calling the cvs.exe process.
When calling cvs.exe by hand, it prints a message to the console and then waits for the user to input the password.
When calling it with subprocess.Popen, I've noticed that the call blocks. The code is
subprocess.Popen(cvscmd, shell = True, stdin = subprocess.PIPE, stdout = subprocess.PIPE,
stderr = subprocess.PIPE)
I assume that it blocks because it's waiting for input, but my expectation was that calling Popen would return immediately and then I could call subprocess.communicate() to input the actual password. How can I achieve this behaviour and avoid blocking on Popen?
OS: Windows XP
Python: 2.6
cvs.exe: 1.11
Remove the shell=True part. Your shell has nothing to do with it. Using shell=True is a common cause of trouble.
Use a list of parameters for cmd.
Example:
cmd = ['cvs',
'-d:pserver:anonymous#bayonne.cvs.sourceforge.net:/cvsroot/bayonne',
'login']
p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
This won't block on my system (my script continues executing).
However since cvs reads the password directly from the terminal (not from standard input or output) you can't just write the password to the subprocess' stdin.
What you could do is pass the password as part of the CVSROOT specification instead, like this:
:pserver:<user>[:<passwd>]#<server>:/<path>
I.e. a function to login to a sourceforge project:
import subprocess
def login_to_sourceforge_cvs(project, username='anonymous', password=''):
host = '%s.cvs.sourceforge.net' % project
path = '/cvsroot/%s' % project
cmd = ['cvs',
'-d:pserver:%s:%s#%s:%s' % (username, password, host, path),
'login']
p = subprocess.Popen(cmd, stdin=subprocess.PIPE,
stdout=subprocess.PIPE
stderr=subprocess.STDOUT)
return p
This works for me. Calling
login_to_sourceforge_cvs('bayonne')
Will log in anonymously to the bayonne project's cvs.
If you are automating external programs that need input - like password - your best bet would probably be to use pexpect.