Execute mysqldump with python and set the password via stdin - python

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)

Related

Python spur: how to read output of long-running command

I'm trying to use the spur library to launch a long-running command via ssh then read and process the output from it one line at a time. The documentation says you can pass a file object using stdout=f and run/spawn will call stdout.write for anything the subprocess writes to its stdout stream. I hit on the idea of creating an os.pipe() to make this work, but it doesn't. Can someone please suggest a fix.
NOTE: I've already got this working with paramiko.SSHClient.exec_command but the interface is a bit low-level for my needs, so I want to learn how to do it with spur. Thanks!
import spur
import os
HOST = "rocky.lan"
USER = "rocky"
CMD = "while sleep 1; do date; done"
r, w = os.pipe()
r = os.fdopen(r, 'rb')
w = os.fdopen(w, 'wb')
ssh = spur.SshShell(hostname=HOST, username=USER)
child = ssh.spawn(CMD, stdout=w)
for line in iter(r.readline, ""):
print(line, end="")
Since someone is bound to ask, the parakimo code looks like this:-
from paramiko import SSHClient
HOST = "rocky.lan"
USER = "rocky"
CMD = "while sleep 1; do date; done"
ssh = SSHClient()
ssh.load_system_host_keys()
ssh.connect(HOST, username=USER)
stdin, stdout, stderr = ssh.exec_command(CMD)
for line in iter(stdout.readline, ""):
print(line, end="")
I've discovered parallel-ssh which seems to have parted company from paramiko and gone for python-ssh/python-ssh2 instead. A 5-minute test suggests that it combines paramiko's power with spur's simplicity, but sadly still doesn't support ~/.ssh/config, so Perl's Net::OpenSSH is still my favourite :-) Here's the code I got working with pssh:-
from pssh.clients import SSHClient
HOST = "rocky.lan"
USER = "rocky"
CMD = "while sleep 1; do date; done"
ssh = SSHClient(host=HOST, user=USER)
cmd = ssh.run_command(CMD)
for line in cmd.stdout:
print(line)
So this is an alternative, but really I still need to know how to read the subprocess's stdout using spur.

Run program from command line what prompts password and automatically provide password for it (cmd.exe, python)

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.

Script cannot read password

Python script is designed to run with elevated credentials, unfortunately
it still prompts me for password
when I enter the correct password it doesn't work
Here is script1, which calls script2 with elevated credentials
import os
import sys, subprocess, socket, string
import wmi, win32api, win32con
import win32com.shell.shell as sh
ASADMIN = '/user:DOMAIN\username'
os.system('"runas /user:DOMAIN\username "D:/Python27/python.exe script2.py sender-ip=10.10.10.10 < password.txt""')
sys.exit(0)
if sys.argv[-1] != ASADMIN:
script = os.path.abspath(sys.argv[0])
params = ''.join([ASADMIN] + ['D:\Python27\python.exe',script] + sys.argv[1:])
sh.ShellExecuteEx(lpVerb='runas',lpFile=sys.executable,lpParameters=params)
sys.exit(0)
Here is script2
import sys, subprocess, socket, string
import wmi, win32api, win32con
for args in [item.strip('sender-ip=') for item in sys.argv[1:]]:
userIP = args
userloggedon = ""
# perform system lookup of IP address
userIP = "\\\\" + userIP
pst = subprocess.Popen(
["D:\pstools\psloggedon.exe", "-l", "-x", userIP],
stdout = subprocess.PIPE,
stderr = subprocess.PIPE
)
out, error = pst.communicate()
userLoggedOn = out.split('\n')[1].strip()
print 'userId={}'.format(userLoggedOn)
f = open('D:\SymantecDLP\Protect\plugins\output.txt', 'w')
f.write('userId={}'.format(userLoggedOn))
output.txt is not created
Any ideas?
EDIT
I also read this thread, How to supply password to runas command when executing it from java
but no matter what I try I keep getting the error
Attempting to start c:\test.bat as user "DOMAIN\username" ...
RUNAS ERROR: Unable to run - c:\test.bat
1326: Logon failure: unknown user name or bad password.
Let's talk about your problems one at the time.
1. It still prompts me for password
In the line
os.system('"runas /user:DOMAIN\username "D:/Python27/python.exe script2.py sender-ip=10.10.10.10 < password.txt""')
you're providing the password to script2. runas command still need a password since is trying to run a program as another user.
2. When I enter the correct password it doesn't work
Well ... The code does'n work that's clear. But, you have to be more specific when asking a question. Right now a look to your code and I can see that you're trying to do ping on a remote machine.
Might the remote machine has a firewall?
Have you tryed doing ping manually?
Edit: The output.txt file is not created, and running the script don't tell you nothing about error writting the file, obviously your code is hitting one of the sys.exit() lines.
You can use PsExec
https://learn.microsoft.com/en-us/sysinternals/downloads/psexec
You can supply a username and password and executing does not need to be elevated to admin:
psexec [\computer[,computer2[,...] | #file]]\ [-u user [-p psswd] [-n s][-r servicename][-h][-l][-s|-e][-x][-i [session]][-c [-f|-v]][-w directory][-d][-][-a n,n,...] cmd [arguments]
Use the -e switch to give the same results as Runas /netonly:
-e Does not load the specified account’s profile.

Automate stdin with Python using stdin.write()

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")

Run shell command with input redirections from python 2.4?

What I'd like to achieve is the launch of the following shell command:
mysql -h hostAddress -u userName -p userPassword
databaseName < fileName
From within a python 2.4 script with something not unlike:
cmd = ["mysql", "-h", ip, "-u", mysqlUser, dbName, "<", file]
subprocess.call(cmd)
This pukes due to the use of the redirect symbol (I believe) - mysql doesn't receive the input file.
I've also tried:
subprocess.call(cmd, stdin=subprocess.PIPE)
no go there ether
Can someone specify the syntax to make a shell call such that I can feed in a file redirection ?
Thanks in advance.
You have to feed the file into mysql stdin by yourself. This should do it.
import subprocess
...
filename = ...
cmd = ["mysql", "-h", ip, "-u", mysqlUser, dbName]
f = open(filename)
subprocess.call(cmd, stdin=f)
The symbol < has this meaning (i. e. reading a file to stdin) only in shell. In Python you should use either of the following:
1) Read file contents in your process and push it to stdin of the child process:
fd = open(filename, 'rb')
try:
subprocess.call(cmd, stdin=fd)
finally:
fd.close()
2) Read file contents via shell (as you mentioned), but redirect stdin of your process accordingly:
# In file myprocess.py
subprocess.call(cmd, stdin=subprocess.PIPE)
# In shell command line
$ python myprocess.py < filename
As Andrey correctly noticed, the < redirection operator is interpreted by shell. Hence another possible solution:
import os
os.system("mysql -h " + ip + " -u " + mysqlUser + " " + dbName)
It works because os.system passes its argument to the shell.
Note that I assumed that all used variables come from a trusted source, otherwise you need to validate them in order to prevent arbitrary code execution. Also those variables should not contain whitespace (default IFS value) or shell special characters.

Categories