How to automate shell interactive commands using Python pexpect module - python

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.

Related

Launch child process as root (python, setuid, MacOS)

How can I launch a child process that has root privileges?
I have a python program in MacOS that can do most of its operations as a normal user. But occasionally, triggered by some user interaction, it will need root permissions to preform a task.
For security reasons, I don't want the entire GUI app to be started and left running as root. I want only a child process with a very minimal subset of functions to run as root.
For UX reasons, I don't want to have to tell the user "Sorry, please restart this app as Administrator". I want to be able to have them stay in the GUI, get presented with a pop-up that says "Uh, you need root to do that. Please enter your password."
Of course, if my unprivileged python process attempts to become root with
setuid(0)
...then I just get a permissions error
PermissionError: [Errno 1] Operation not permitted
What can I use as an alternate to setuid() so that I can launch a new child process on a MacOS system, after escalating privilege by getting authentication from the user in the GUI?
I want to be able to have them stay in the GUI, get presented with a pop-up that says "Uh, you need root to do that. Please enter your password."
This is exactly what the MacOS Security API's AuthorizationExecuteWithPrivileges() function was created for.
You can call AuthorizationExecuteWithPrivileges() directly with python's ctypes.
For example, consider your parent script running as your normal, non-root user. If you try to just run setuid(0), then it will fail with
PermissionError: [Errno 1] Operation not permitted
Instead, let's create another script named root_child.py, which we'll execute as root with AuthorizationExecuteWithPrivileges()
Child (root_child.py)
#!/usr/bin/env python3
import os
if __name__ == "__main__":
try:
os.setuid(9)
print( "I am root!" )
except Exception as e:
print( "I am not root :'(" )
Parent (spawn_root.py)
We can execute the above root_child.py script as root from our non-root script spawn_root.py:
import sys, ctypes, struct
import ctypes.util
from ctypes import byref
# import some C libraries for interacting via ctypes with the MacOS API
libc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("c"))
# https://developer.apple.com/documentation/security
sec = ctypes.cdll.LoadLibrary(ctypes.util.find_library("Security"))
kAuthorizationFlagDefaults = 0
auth = ctypes.c_void_p()
r_auth = byref(auth)
sec.AuthorizationCreate(None,None,kAuthorizationFlagDefaults,r_auth)
exe = [sys.executable,"root_child.py"]
args = (ctypes.c_char_p * len(exe))()
for i,arg in enumerate(exe[1:]):
args[i] = arg.encode('utf8')
io = ctypes.c_void_p()
print( "running root_child.py")
err = sec.AuthorizationExecuteWithPrivileges(auth,exe[0].encode('utf8'),0,args,byref(io))
print( "err:|" +str(err)+ "|" )
print( "root_child.py executed!")
Example Execution
Note that, because the credential challenge for AuthorizationExecuteWithPrivileges() comes via the GUI, you must execute this from within the GUI. If you attempt to execute the above scripts, for example, over a SSH in a tty, you'll get an error -60007, which is errAuthorizationInteractionNotAllowed and means:
The Security Server denied authorization because no user interaction is allowed.
user#host ~ % ./spawn_root.py
running root_child.py
err:|-60007|
root_child.py executed!
user#host ~ %
However, if executed from the Terminal app in the GUI, then it prompts the user for their password.
If the user successfully enters their credentials correctly, then the root_child.py script is executed with root privileges.
user#host ~ % ./spawn_root.py
running root_child.py
err:|0|
root_child.py executed!
Additional Information
Security
Note that AuthorizationExecuteWithPrivileges() has been deprecated by apple in-favor of an alternatve that requires you to pay them money. Unfortunately, there's some misinformation out there that AuthorizationExecuteWithPrivileges() is a huge security hole. While it's true that using AuthorizationExecuteWithPrivileges() incorrectly can cause security issues, it is not inherently insecure to use it.
Obviously, any time you run something as root, you need to be very careful!
AuthorizationExecuteWithPrivileges() is deprecated, but it can be used safely. But it can also be used unsafely!
It basically boils down to: do you actually know what you're running as root? If the script you're running as root is located in a Temp dir that has world-writeable permissions (as a lot of MacOS App installers have done historically), then any malicious process could gain root access.
To execute a process as root safely:
Make sure that the permissions on the process-to-be-launched are root:root 0400 (or writeable only by root)
Specify the absolute path to the process-to-be-launched, and don't allow any malicious modification of that path
Further Reading
AuthorizationExecuteWithPrivileges() Reference Documentation
https://github.com/cloudmatrix/esky/blob/master/esky/sudo/sudo_osx.py
https://github.com/BusKill/buskill-app/issues/14
https://www.jamf.com/blog/detecting-insecure-application-updates-on-macos/

How to write check_mk manual checks

I have a python scripts which checks for the processes
import subprocess
s = subprocess.check_output('tasklist', shell=True)
if "cmd.exe" in s:
if "java.exe" not in str(s):
print "selenium server is not up"
if "FreeSSHDService.exe" not in str(s):
print "SSH is not up"
else:
print "Everything is awesome"
I want to add a check on check_mk dashboard, what are the steps to add this check and where I have to up this script.
import subprocess
s = subprocess.check_output('tasklist', shell=True)
if "cmd.exe" in s:
if "java.exe" not in str(s):
return 2, "selenium server is not up")
if "FreeSSHDService.exe" not in str(s):
return 2, "SSH is not up"
else:
return 0, "Everything is awesome"
First of all I'm assuming the node you want to check is MS Windows based, in which case I cannot help you much because my expertise is about UNIX and Linux.
This web link will help you to check your Windows based nodes, especially the paragrah 10. Extending the Windows agent.
In Linux, once the check_mk_agent is installed, there are three ways according to how deep you want to get into the check_mk guts. In Windows I think there are the same methods.
As a local service: you copy your python code into the local folder, whatever it is located in Windows, and edits the [global] section of the check_mk.ini configuration file to make run the py and pyc file name extensions.
As a MRPE check: you make your python program print its output according to the Nagios output check format and edit the [mrpe] section of the check_ini configuration file according to the notes in the paragraph 10.2. As a disadvantage, the WARNING and CRITICAL values/ranges are fixed in the check_ini file -- they cannot be changed in WATO.
As a check_mk agent: you turn your python program into a check_mk agent. I think this is the most difficult way because each check_mk agent has to have a counterpart definition/declaration/inventory file in the check_mk server in order to be used in WATO and to configure its parameters. I never wrote down one, but if you are keen on, you should read this guidelines.
Best regards.
If you want to execute such script you just need to put it (with the correct rights, chmod +755) in the ~/local/lib/nagios/plugins directory.
Then you have to create a rule from the "host and services parameters -> active checks -> Classical active & passive checks"
Once done, you need to enter the command line "python ~/local/lib/nagios/plugins/nameofyourscript.py"
I am not sure about the output though, still working on it for python scripts.

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

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

Why doesn't my Python script open a text file when run via cron?

I have a Python program which is working properly when I run it normally from LXTerminal:
$ sudo python testcon.py
but when I run it with cron to start after reboot:
#reboot python /home/pi/testcon.py &
it stops at the line:
f = open('info.txt')
and doesn't do anything more. It's supposed to open the file /home/pi/info.txt.
Why does this happen? How can I fix this?
Here's a simpler version of my program that shows the problem:
import smbus
import time
bus = smbus.SMBus(1) # Rev 2 Pi uses 1
DEVICE = 0x23 # Device address (A0-A2)
IODIRA = 0x00 # Pin direction register
OLATA = 0x14 # Register for outputs
GPIOA = 0x12 # Register for inputs
bus.write_byte_data(DEVICE,IODIRA,0x00)
bus.write_byte_data(DEVICE,OLATA,0xFF) #set all of the outputs
time.sleep(3) #wait for 3 sec
f = open('info.txt') #should open the txt file
bus.write_byte_data(DEVICE,OLATA,0) #clear all of the outputs
f.close()
The #reboot option can only be used in root. It cannot be invoked in your user's cron. However, sometimes it depends upon your operating system. See here.
Looking at this rather similar question, and especially at this answer, I have two suggestions:
a) Does the raspberry support the #reboot syntax at all? Check by reading the manual page for cron and crontab.
b) From the linked answer it seems that you need to add the name of the user that should be executing the script:
#reboot username /usr/bin/python /home/pi/test.py &
If the program can only succeed when running as root, use root as the username; but otherwise it's always a good idea to avoid running things as root.

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