Question:
I am trying to execute a cmd which reads from a PostgreSQL db. I am able to manually switch to root, then switch to the postgre user and access the information I desire.
The problem I have is that when I run this, it just hangs and nothing happens.
I have the root password and will need this when switching from the current user But I am not being prompted to enter it.
How can I get this not to hang and the password be prompted?
The code below only executes 'ls' for simplicity.
Code:
def popen_cmd_shell(command):
print command
process = subprocess.Popen(command,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
shell=True)
proc_stdout = process.communicate()[0].strip()
return proc_stdout
if __name__ == '__main__':
querylist = popen_cmd_shell('su - root; ls;')
print querylist
Update One:
I am unable to use any library that dose not come with python 2.7 on Linux SUSE. I just need to execute a command and exit.
Update Two:
I am unable to run the script as root as I need to perform other tasks which require me not to be root.
Update Three:
As per LeBarton suggestions I have got the script to log into root, although the the ls command never gets executed as root, it gets executed as the user I originally was. When I run the command I get prompted to enter the root password and get transfered from "#host" to "host" who cannot execute any command other than exit. When I exit all the commands executed output appears.
I do not wish to store the user password in the code as LeBarton has it. How can I execute a command as root and return back and continue the rest of the script, without getting locked into the new users and needing to type 'exit'.
The "stderr=subprocess.STDOUT" seems to have been what was causing it to hang.
Code:
if __name__ == '__main__':
def subprocess_cmd(command):
process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=True)
proc_stdout = process.communicate()[0].strip()
print proc_stdout
subprocess_cmd('echo a; su - root; ls; cd;ls;')
...continue with rest of script where I execute commands as original user
Answer:
Thanks to tripleee for his excellent answer.
I Have achieved what I set out to do with the follwoing code:
if __name__ == '__main__':
def subprocess_cmd(command):
process = subprocess.Popen(command,stdout=subprocess.PIPE, shell=False)
proc_stdout = process.communicate()[0].strip()
print proc_stdout
subprocess_cmd(['su','-','root','-c','su -s /bin/sh postgres -c \'psql -U msa ..........])
I just needed to the place the command I was executing as root after -c. So it now switches to the postgres user and finds the data it needs from root returning to the normal user after.
You are misunderstanding what su does. su creates a privileged subprocess; it does not change the privileges of your current process. The commands after su will be executed after su finishes, with your normal privileges.
Instead, you want to pass a -c option to specify the commands you want to run with elevated privileges. (See also the su man page.)
popen_cmd_shell('su -c ls - root')
sudo was specifically designed to simplify this sort of thing, so you should probably be using that instead.
Scripted access to privileged commands is a sticky topic. One common approach is to have your command perform the privileged operation, then drop its privileges. Both from a security and a design point of view, this approach tends to simplify the overall logic. You need to make sure your privileged code is as simple and short as possible--no parsing in the privileged section, for example.
Once the privileged code is properly tested, audited, and frozen, bestowing the script with the required privileges should be a simple matter (although many organizations are paranoid, and basically unable to establish a workable policy for this sort of thing).
Regardless of which approach you take, you should definitely avoid anything with shell=True in any security-sensitive context, and instead pass any external commands as a list, not as a single string.
popen_cmd_shell(['su', '-c', 'ls', '-', 'root'])
(Maybe also rename the function, since you specifically do not want a shell. Obviously, also change the function to specify shell=False.)
Again, these security concerns hold whether you go with dropping privileges, or requesting privilege escalation via su or sudo.
Your command is this
su - root; ls;
The shell is interpretting it as this
"su -root; ls;"
You probably don't have an executable in your with that exact name with spaces.
Try separating it into as list with
['su', '-', 'root', ';', 'ls', ';' ]
EDIT
stdout=subprocess.PIPE, is causing the program to hang. If you are trying to pass the password in, using process.communicate('root password') works.
Do you just want to access the PostgreSQL database? If so, you don't need to use command line at all...
The Python library psycopg2 will allow to send commands to the PostgreSQL server, more on that here: https://wiki.postgresql.org/wiki/Psycopg2_Tutorial
However I recommend an ORM such as SQLAlchemy, they make communicating with a database a trivial task.
Related
I am building an email purge tool. The premise is that the .py needs to connect to the IPPSSession using Powershell. like so:
sp.run(f"Connect-IPPSSession -UserPrincipalName {Email}", shell=True)
However, when I go running the commands later in the program, it does not recognize the commands.
From what I have read, it appears (subprocess) sp.run is connecting and promptly disconnecting.
For the commands later in the program to be recognized, I need to maintain a connection.
Is there a way to have the IPPSSession run the entire length of the program? I guess I could rewrite the whole program in PowerShell exclusively....
After some stimulants and quite a bit of thinking. I found a better way to format the query. Behold:
Email_Purge = f"Connect-IPPSSession -UserPrincipalName {Email} ; New-ComplianceSearchAction -SearchName {Search_Name!r} -purge -PurgeType {Purge_Type}"
if Purge_Type == "SoftDelete" or Purge_Type == "HardDelete":
sp.run(Email_Purge, shell=True)
else:
print("Please enter [SoftDelete] or [HardDelete]")
The session runs the whole length of the Var. so all of the input happens first, and then it executes and breaks the session cleanly.
I have a python program that's run with pkexec and I guess because of that I'm having a really hard time getting os.environ.get('XDG_CURRENT_DESKTOP') or os.environ.get('DESKTOP_SESSION') to output anything. One of the program's function is to get the Linux Desktop environment, and that's mainly what I'm trying to accomplish right now. I've decided to use os.setupid('my_username') getting it from pwd to switch to my user and try to get the environment variables since would no longer be root but the problem is I can not go back and run the script as root for other functions. How can I go back to being root after all that?
In order to get the environment variables I'm trying this:
def getDesktopEnvironment(self):
os.seteuid(self.uidChange)
desktops = subprocess.Popen(['bash', 'desktopenv.sh'], stdout=subprocess.PIPE)
desktops.wait()
a = desktops.stdout.read()
print a
if a == "X-Cinnamon":
#do this
elif a == "Unity":
#do that
The bash script is below
#!/bin/bash
echo $XDG_CURRENT_DESKTOP
trying to go back to root get me this: os.seteuid(0)OSError: [Errno 1] Operation not permitted
I would recommend the following change:
Modify the code that sets the UID for the user that you want to the username corresponding to the UID that you want.
Modify your getDesktopEnvironment to look like the code below. NOTE: The script path doesn't have to be in the users home directory it just has to bereadable by the user specified by username.
def getDesktopEnvironment(self):
# You can set the script_path can be located anywhere you want.
# as long as the user you want to invoke the script has permission
# to read the file.
script_path = os.path.join('~', self.username, 'desktopenv.sh')
args = args = ['sudo', '--login', '-u', self.username, '/bin/bash', script_path]
desktops = subprocess.Popen(args, stdout=subprocess.PIPE)
desktops.wait()
a = desktops.stdout.read()
print a
if a == "X-Cinnamon":
#do this
elif a == "Unity":
#do that
Just because you change the UID of the current process doesn't mean that you inherit the environment associated with that UID. Using sudo with the -i option ensures that the user's startup scripts are run and ensures that all relevant environment variables are set.
Using sudo also ensures that you can continue the running the rest of your application as root.
I was trying to automate a system where we have a linux box but customized.. to go to shell we have to pass some input. like below:
tanuj$ ssh admin#10.10.10.10
Used by Tanuj
Password:
command line interface
app > en
app # config t
app (config) #
I have written a script using pexpect in python. I am able to login and execute the command
pexpect.sendline("ls -lrt")
pexpect.expect("#")
but when i am using pexpect.before() .. is getting nothing .. when the command output is long and also i could see the pexpect.before has also got the command in it.
any idea how to solve this.. or is there any other python module which i can use to automation a ssh session like i have here.
i also tried using paramiko but it did not work because we have a to execute some commands before we can reach to normal shell prompt.
I am also facing the similar problem. I was about to ask the question. You are using a # sign in your
pexpect.expect('#')
This # sign comments everything written after it. Further I guess you should create a child process to spawn a process, Like(I don't know if I'm right in your situation):
child=pexpect.spawn('ssh admin#10.10.10.10')
child.expect('prompt_expected_by_you') # put in the prompt you expect after sending SSH
child.sendline('your_password')
child.expect('Prompt_expected_by_you')
child.sendline('ls -ltr')
child.expect('Prompt_expected_by_you')
print child.before, # , will keep continue your newline print
print child.after
child.sendline('exit')
child.expect('Prompt_expected_by_you')
child.sendline('exit')
child.expect('Prompt_expected_by_you') # may not be required
child.close # close the child session
I have successfully used these commands in FTP, but not able to print result of 'LS -LTR' in SSH. I guess i'll have to initiate a shell, but not sure. Any progress on your side?
Could someone help???
I've searched around for quite a bit, finding pieces of what I wish to achieve but not fully. I'm making a sync-script to synchronize files between two machines. The script itself is somewhat more advanced than this question (it provides possibility for both sides to request for file deletion and so on, no "master side").
First question
The following bash-command works for me:
rsync -rlvptghe 'sshpass -p <password> ssh -p <port>' <source> <destination>
how can I translate it into a python command to be used with the subprocess object?
I've managed to get the following python to work:
pw = getpass.getpass("Password for remote host: ")
command = ['sshpass', '-p', pw, 'rsync', '-rlvptgh', source, destination]
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while p.poll() is None:
out = p.stdout.read(1)
sys.stdout.write(out)
sys.stdout.flush()
but it doesn't specify port (it uses standard 22, I want another one). To clarify, I wish to use similar code as this one but with the support for a specific port as well.
I have already tried to change the command to:
command = ['sshpass', '-p', pw, 'rsync', '-rlvptghe', 'ssh', '-p', '2222', source, destination]
which gives the following error:
ssh: illegal option -- r
and also many other variations such as for instance:
command = ['rsync', '-rlvptghe', 'sshpass', '-p', pw, 'ssh', '-p', '2222', source, destination]
Which gives the following error (where <source> is the remote host source host to sync from, ie variable source above command declaration):
Unexpected remote arg: <source>
How should I specify this command to nest them according to my first bash command?
Second question
When I've done all my searching I've found lots of frowning upon using a command containing the password for scp/rsync (ie ssh), which I use in my script. My reasoning is that I want to be prompted for a password when I do the synchronization. It is done manually since it gives feedback on filesystem modifications and other things. However, since I do 2 scp and 2 rsync calls I don't want to type the same password 4 times. That is why I use this approach and let python (the getpass module) collect the password one time and then use it for all the 4 logins.
If the script was planned for an automated setup I would of course use certificates instead, I would not save the password in clear text in a file.
Am I still reasoning the wrong way about this? Are there things I could do to strengthen the integrity of the password used? I've already realized that I should suppress errors coming from the subprocess module since it might display the command with the password.
Any light on the problem is highly appreciated!
EDIT:
I have updated question 1 with some more information as to what I'm after. I also corrected a minor copy + paste error in the python code.
Edit 2 explained further that I do have tried the exact same order as the first bash command. That was the first I tried. It doesn't work. The reason for changing the order was because it worked with another order (sshpass first) without specifying port.
I have found one way to solve this for my own needs. It includes invoking a shell to handle the command, which I avoided in the first place. It works for me though, but might not be satisfactory to others. It depends on the environment you want to run the command in. For me this is more or less an extension of the bash shell, I want to do some other things that are easier in python and at the same time run some bash commands (scp and rsync).
I'll wait for a while and if there's no better solution than this I will mark my answer as the answer.
A basic function for running rsync via python with password and port could be:
def syncFiles(pw, source, destination, port, excludeFile=None, dryRun=False, showProgress=False):
command = 'rsync -rlvptghe \'sshpass -p ' + pw + ' ssh -p ' + port + '\' ' + source + ' ' + destination
if excludeFile != None:
command += ' --exclude-from='+excludeFile
if dryRun:
command += ' --dry-run'
if showProgress:
command += ' --progress'
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
while p.poll() is None:
out = p.stdout.read(1)
sys.stdout.write(out)
sys.stdout.flush()
The reason this works is as I wrote because the invoked bash shell handles the command instead. This way I can write the command exactly as I would directly in a shell. I still don't know how to do this without shell=True.
Note that the password is collected from the user with the getpass module:
pw = getpass.getpass("Password for current user on remote host: ")
It is not recommended to store your password in the python file or any other file. If you are looking for an automated solution it is better to use private keys. Answers for such solutions can be found by searching.
To call the scp-command with password the following python should do:
subprocess.check_output(['sshpass', '-p', pw, 'scp', '-P', port, source, destination])
I hope this can be useful to someone who wants to achieve what I am doing.
I am creating a movie controller (Pause/Stop...) using python where I ssh into a remote computer, and issue commands into a named pipe like so
echo -n q > ~/pipes/pipename
I know this works if I ssh via the terminal and do it myself, so there is no problem with the setup of the named pipe redirection. My problem is that setting up an ssh session takes time (1-3 seconds), whereas I want the pause command to be instantaneous. Therefore, I thought of setting up a persistent pipe like so:
controller = subprocess.Popen ( "ssh -T -x <hostname>", shell = True, close_fds = True, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE )
Then issue commands to it like so
controller.stdin.write ( 'echo -n q > ~/pipes/pipename' )
I think the problem is that ssh is interactive so it expects a carriage return. This is where my problems begin, as nearly everyone who has asked this question has been told to use an existing module:
Vivek's answer
Chakib's Answer
shx2's Answer
Crafty Thumber's Answer
Artyom's Answer
Jon W's Answer
Which is fine, but I am so close. I just need to know how to include the carriage return, otherwise, I have to go learn all these other modules, which mind you is not trivial (for example, right now I can't figure out how pexpect uses either my /etc/hosts file or my ssh keyless authentications).
To add a newline to the command, you will need to add a newline to the string:
controller.stdin.write('\n')
You may also need to flush the pipe:
controller.stdin.flush()
And of course the controller has to be ready to receive new data, or you could block forever trying to send it data. (And if the reason it's not ready is that it's blocking forever waiting for you to read from its stdout, which is possible on some platforms, you're deadlocked unrecoverably.)
I'm not sure why it's not working the way you have it set up, but I'll take a stab at this. I think what I would do is change the Popen call to:
controller = subprocess.Popen("ssh -T -x <hostname> \"sh -c 'cat > ~/pipes/pipename'\"", ...
And then simply controller.stdin.write('q').