Change user (su) via Python script (pexpect, popen...) - python

I am running a Python script with user1 and in this script I need to move a file in a folder for which I don't have access. The folder is owned by user2.
What I want to do is :
- change from user1 to user2 using su - user2
- enter the password
- move the file
So far I tried with Popen :
p = subprocess.Popen(["su", "-", "user2"])
p.communicate("user2_password")
p.communicate("mv path/to/file other/path/to/file")
It doesn't work, I need to manually enter the password in the shell.
And I also tried with pexpect :
child = pexpect.spawn('su - user2')
child.expect ('Password:')
child.sendline('user2_password')
child.sendline('mv path/to/file other/path/to/file')
The script runs but nothing happens and after running the script I'm still user1.
Any idea why ? For what it's worth, it seems to be specific to su because if I write a script with sudo it works (I can feed the password).
Thank you for your help !

Your command fails as you do not expect anything after sending the password. Depending on your shell etc, you will get some kind of a command prompt after a successful su. My prompt is this:
klunssi#xyzzy:~$
In fact printing that prompt includes many special characters to control tty output, so I just want to grab the trailing $:
child = pexpect.spawn('su - klunssi')
child.expect ('Password:')
child.sendline('klunssi')
child.expect('\$')
child.sendline('mv /home/klunssi/foo /home/klunssi/bar')
This works and does the move.
It does not leave me with "klunssi" account as pexpect.spawn() creates a subprocess and su happens there.
If you have root access to the system, I suggest modifying /etc/sudoers and adding a passwordless sudo so that your user1 can run commands as user2. This is far more reliable as you do not need pexpect at all and can just
Popen(["sudo", "-u", "user2", "yourcommand"])
If this is not an option, then the above pexpect should do the trick.
Hannu

Related

Input text into a program opened through terminal, but fast

I have a program which needs to receive input very fast and I know what the input has to be, but there is a timer which I suppose expects no delay between opening the program and entering the input.
I've tried using bash script but it doesn't seem to work, and trying ./program; password also doesn't work (it returns that 'password' is not a command).
My bash script looks like this:
#! /bin/bash
cd ~/Downloads
./program
password
Perhaps it's working, but I'm not receiving any output from the program, which would usually display how long it took to get an input.
Well, first of all, change execution to ~/Downloads/program password. Also make sure program is executable ( chmod +x if it isn't) and that it takes arguments.
Furthermore, to refrain from mentioning the path every time, move program to ~/bin/ (create if it doesn't exist) and add that location to $PATH if it isn't there.
If the "program" does not expect the password as a command line argument, then you probably want to input it through stdin:
#! /bin/bash
cd ~/Downloads
echo "password" | ./program
or, if there is more input:
./program <<INPUT
password
moreInput
moreInput2
...
moreInputN
INPUT
The first variant uses simple piping, the second relies on HereDocs.
In the (improbable) case that the program expects the password as an argument, you have to pass it as follows:
./programm password
without line-breaks and semicolons in between.
I'm saying that this is "improbable", because if such an invokation is used from the shell, then the password will be saved as clear text in bash-history, which is obviously not very good.

How to automate shell interactive commands using Python pexpect module

I am trying to automate the setup of an application by performing SSH to the machine and goto /var/packages folder and execute the script.when the installation starts a set of interactive commands to be send based on the expected output.I found from google that pexpect can achieve this but i am unable to achieve the result that i wish. I am trying following code , can someone guide me how to achieve this as I am beginner to python.Any help would be appreciated. My application setup would look like this
[root#bits packages]# ./SHR_setup.bin -i console
Preparing to install...
Extracting the JRE from the installer archive...
Unpacking the JRE...
Extracting the installation resources from the installer archive...
Configuring the installer for this system's environment...
Launching installer...
===============================================================================
Choose Locale...
----------------
1- Deutsch
->2- English
3- Español
4- Français
5- Italiano
6- Nederlands
7- Português (Brasil)
CHOOSE LOCALE BY NUMBER: 2
I accept the terms of the License Agreement (Y/N): Y
Please hit Enter to continue:
Python Code
from pexpect import pxssh
import pexpect
try:
s = pxssh.pxssh()
hostname = '10.110.40.20'
username = 'admin'
password = 'admin123'
s.login(hostname, username, password)
s.sendline('cd /var/packages') # goto /var/packages folder
child = pexpect.spawn('./SHR_setup.bin -i console') # start the application setup in packages folder
child.expect('CHOOSE LOCALE BY NUMBER') # expect output like this
child.sendline('2')
s.prompt()
print s.before
except pxssh.ExceptionPxssh, e:
print 'pxssh failed on login'
print e
You should change
s.sendline('cd /var/packages')
child = pexpect.spawn('./SHR_setup.bin -i console')
to
s.sendline('cd /var/packages')
s.sendline('./SHR_setup.bin -i console')
spawn is supposed to run a program on the local host, not on the remote host.
You're on the right track with using the s.before log for debugging.
The app you're interacting with appears to be more screen-oriented than line-oriented, which can pose some difficulties, including ANSI escape sequences for color and position. Consider running child.expect('Something else'), some string which does reliably show up in before, then do a brief sleep(), then just "blindly" send "2" or "y" or whatever, pausing briefly between sends.

Trouble with masking password input in Python

I am using Python. I am making a script where the user has to enter the password in the terminal.
I have already found a solution on this website by using the getpass module.
new_password=getpass.getpass(prompt="Type new password: ")
The problem is I get a warning and the password input gets displayed as well.
Warning (from warnings module):
File "C:\Python34\lib\getpass.py", line 101
return fallback_getpass(prompt, stream)
GetPassWarning: Can not control echo on the terminal.
Warning: Password input may be echoed.
Use command prompt as admin to run this program.
Reason is because system environment where stdin, stdout and stderr are connected to /dev/tty, or another PTY-compliant device.
The IDLE REPL does not meet this requirement.
And change new_password=getpass.getpass(prompt="Type new password: ") to new_password=getpass.getpass("Type new password: ") if you are using Windows OS or new_password=getpass.getpass("Type new password: ", None) for Linux distributions.
This would help you for sure:
import getpass
pw = getpass.getpass("Enter Your Password Here: ")
if pw == "password":
print("You are Welcome...")
else:
print("Sorry! You're are not allowed.")
As per Python documentation:
getpass.getpass([prompt[, stream]])
Prompt the user for a password without echoing. The user is prompted using the string prompt, which defaults to 'Password: '. On Unix, the prompt is written to the file-like object stream. stream defaults to the controlling terminal (/dev/tty) or if that is unavailable to sys.stderr (this argument is ignored on Windows)
Changed in version 2.5: The stream parameter was added.
Note: If you call getpass from within IDLE, the input may be done in the terminal you launched IDLE from rather than the idle window itself.
Using a normal terminal with this code:
import getpass
new_password=getpass.getpass(prompt="Type new password: ")
print(new_password)
Will work alright, but if we try the same with IDLE we'll get the error you've exposed in your question.
Now, if we look at the docs here you'll see this is intended, it says:
Note If you call getpass from within IDLE, the input may be done in
the terminal you launched IDLE from rather than the idle window
itself.

Terminal messed up (not displaying new lines) after running Python script

I have a Python script I use to execute commands in parallel across multiple hosts using the Python subprocess module. It wraps SSH, and basically makes a call like this:
output = subprocess.Popen(["/bin/env", env, "/usr/bin/ssh", "-t", "%s#%s" % (user, host), "--", command], stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()
The effective command gets executed like this:
/bin/env TERM=$TERM:password /usr/bin/ssh -t "%s#%s" % (user, host), "--", command
It works fine, except I get an intermittent error where my terminal gets messed up (loses newlines) after running the script. A "reset" from the command line fixes it, but I'm not sure how this is happening, exactly. I noticed that sometimes there's a "\r\n" at the end of the the first item in the tuple's output, and sometimes it's not there. See the following, specifically "Permission denied\r\n":
**** Okay output ****
[user#/home/user]# ./command.py hosts.lists "grep root /etc/shadow"
Running command "grep root /etc/shadow" on hosts in file "hosts.test"
('grep: /etc/shadow: Permission denied\r\n', 'Connection to server1.example.com closed.\r\n')
('grep: /etc/shadow: Permission denied\r\n', 'Connection to server2.example.com closed.\r\n')
[user#/home/user]#
**** Output causes terminal to not display newlines ****
[user#/home/user]# ./command.py hosts.list "grep root /etc/shadow"
('grep: /etc/shadow: Permission denied\r\n', 'Connection to server1.example.com closed.\r\n')
('grep: /etc/shadow: Permission denied\n', 'Connection to server2.example.com closed.\r\n')
[user#/home/user]# [user#/home/user]# [user#/home/user]
The second output has been slightly modified, but shows the missing "\r", and how my prompt gets "wacked" after running the script.
I think this is related to using the "-t" option in my subprocess command. Somehow I'm losing the \r. If I remove the "-t" option, this issue goes away, but long story short, I need it for passing through environmental variables for use on the remote machine (I'm hackishly using the TERM variable to pass through the user's password for sudo purposes, because I can't assume AcceptEnv is allowing arbitrary variable passing on the remote sshd server; I'm doing this to avoid passing the password on the command line, which will show up in the process list on the remote machine).
Just wondering if anyone knows a way to get around this, without removing the "-t" option?
UPDATE:
It looks like my tty settings get altered after running the subprocess.Popen(...).communicate() command within my script, regardless of whether or not I actually print the output to screen. I find that really strange. Here are the before/after differences in my tty config (from stty -a):
-ignbrk brkint ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff
-ignbrk brkint ignpar -parmrk -inpck -istrip -inlcr -igncr -icrnl -ixon -ixoff
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
-opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon -iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt
-isig -icanon -iexten -echo -echoe -echok -echonl -noflsh -xcase -tostop -echoprt
I'm wondering how to stop communicate() from altering my terminal settings? Is it possible, or is this a bug?
I found that
stty sane
restores the console to how it was before.
I didn't really understand the other answer here so I this helps someone.
Found the answer here.
I had the same issue in a Perl script. To solve the problem I had to save the current settings of the local terminal (in order to restore it at the end of the script) and prepending "stty -raw" before executing the remote command.
So in Perl:
#Save current terminal settings (you may add the PID in the filename)
`stty -g > ~/tmp/.currentTtySettings`;
#Execute remote command prepending "stty -raw"
my #out=`ssh -t -q user#server1.example.com "stty -raw ; grep root /etc/shadow"`;
#Restore terminal settings
`stty \`cat ~/tmp/.currentTtySettings\``;
Hope it helps you!
Other very useful links:
-Detailed explanation of ssh and tty (-t option) https://unix.stackexchange.com/questions/151916/why-is-this-binary-file-being-changed
-Some Perl and ssh inspiration http://search.cpan.org/~bnegrao/Net-SSH-Expect-1.09/lib/Net/SSH/Expect.pod
-How to avoid "-t" for sudo https://unix.stackexchange.com/questions/122616/why-do-i-need-a-tty-to-run-sudo-if-i-can-sudo-without-a-password

Enter sudo password for lxc-start in pexpect.spawn process with pexpect.spawn.interact

I want to start an lxc with sudo lxc-start -n <lxc-name> --lxcpath=/some/custom/path in a python script and controll it with pexpect (login, install a package, add a user and logout/shutdown the lxc). An example would look like this (I'm pretty close to the solution, just need the right escape_character argument for pexpect.spawn.interact):
import pexpect
child = pexpect.spawn("sudo lxc-start --name=debian-wheezy-amd64 --lxcpath=/some/custom/path/")
child.expect("Password:") # the sudo password prompt (might be optional if the script has been invoked with sudo (let's keep in anyway as an exercise))
child.interact("??") # tried "\r" -> never returns, "\r\r" and "\n" -> accepts the sudo password, but doesn't give back the control to the python interpreter (stuck at lxc login)
child.expect("login")
child.sendline("root")
# etc. (tasks mentioned above)
child.expect("[#$][\\s]")
child.sendline("shutdown -h 0")
Everythings works well if I enter the password with getpassword, store it in a variable and pass it with child.expect("Password:"); child.sendline(mypassword), so the solution above is rather an exercise for better understanding of pexpect.
Is there a generic escape_character for all OSs/lxcs to enter the sudo password or do they differ (and if they do is there a generic way to determine them independent of the knowledge about the OS (e.g. with a shell variable)?)

Categories

Resources