Change to sudo user within a python script
Asked Answered
G

9

29

I have a problem. I am writing a piece of software, which is required to perform an operation which requires the user to be in sudo mode. running 'sudo python filename.py' isn't an option, which leads me to my question. Is there a way of changing to sudo half way through a python script, security isn't an issue as the user will know the sudo password the program should run in the following way to illustrate the issue

  1. program running as normal user
  2. ...... performing operations
  3. user enters sudo password
  4. user changed to sudo
  5. sub program requiring sudo permission is run
  6. on trigger even (end of sub program) user becomes normal user again
  7. ...... performing operations

My problem lies in step 3, any pointers or frameworks you could suggest would be of great help.

Cheers

Chris

Glue answered 4/3, 2011 at 9:25 Comment(1)
A perfect solution, I think, would have the following qualities, whether or not it's possible: 1. It does not involve restarting the script. 2. It does not involve calling a second Python script within a Python script. 3. The root privileges are only applicable for a specified portion of the script (and not the beginning, and not necessarily the end).Bewley
G
2

Use Tcl and Expect, plus subprocess to elevate yourself. So basically it's like this:

sudo.tcl

spawn sudo
expect {
    "Password:" {
        send "password"
    }
}

sudo.py

import subprocess
subprocess.call(['tclsh', 'sudo.tcl'])

And then run sudo.py.

Godding answered 12/9, 2014 at 20:5 Comment(3)
I get invalid command name "spawn" when I run the Python script (with everything exactly as you have it here). What should I do differently?Bewley
Maybe using Python's pexpect module and the pexpect.spawnu class will be helpful in creating an all-Python solution. If you know how to use it, feel free to edit that into your answer. (Not that I need an all-Python solution. So long as it works for me, that's great.)Bewley
I need an all python solution to this. Do you have one?Telemotor
S
19

It is better to run as little of the program as possible with elevated privileges. You can run the small part that needs more privilege via the subprocess.call() function, e.g.

import subprocess
returncode = subprocess.call(["/usr/bin/sudo", "/usr/bin/id"])
Squire answered 2/3, 2012 at 3:25 Comment(0)
R
10

Don't try and make yourself sudo just check if you are and error if your not

class NotSudo(Exception):
    pass

if os.getuid() != 0:
    raise NotSudo("This program is not run as sudo or elevated this it will not work")
Renaud answered 4/3, 2011 at 9:31 Comment(8)
Shouldn't this be os.geteuid() != 0?Nichol
This isn't a very good solution - what if only parts of the script require root?Righteousness
@Righteousness For one this was over a year ago. For another if a single part of the script requires sudo then the script will fail, better to just clearly error out.Renaud
The question specifically required to not run the entire program elevated.Tirade
@Tirade This question is 6 years old.Renaud
@JakobBowyer who knew sudo would still be around?Tirade
@jbo5112 Who knew that its better scope privilege to requirementRenaud
@Tirade Other sites are against posting in old threads but stack overflow has seen the light and encourages having questions with answers that will be useful to others that come later, which means that fixing or pointing out issues with old answers is important.Finbar
G
5

I've recently dealt with this problem while making a system installation script. To switch to superuser permissions, I used subprocess.call() with 'sudo':

#!/usr/bin/python

import subprocess
import shlex
import getpass

print "This script was called by: " + getpass.getuser()

print "Now do something as 'root'..."
subprocess.call(shlex.split('sudo id -nu'))

print "Now switch back to the calling user: " + getpass.getuser()

Note that you need to use shlex.split() to make your command usable for subprocess.call(). If you want to use the output from a command, you can use subprocess.check_output(). There is also a package called 'sh' (http://amoffat.github.com/sh/) that you can use for this purpose.

Geoff answered 26/11, 2012 at 3:25 Comment(1)
sudo: no tty present and no askpass program specifiedRoping
S
2

If you are able to encapsulate just the necessary functionality requiring elevated privileges in a separate executable, you could use the setuid bit on the executable program, and call it from your user-level python script.

In this way, only the activity in the setuid-executable run as root, however executing this does NOT require sudo, i.e., root privileges. Only creating/modifying the setuid-executable requires sudo.

There are a few security implications, such as ensuring that your setuid executable program properly sanitizes any user input (e.g., parameters), so that it cannot be tricked into doing something it should not (confused deputy problem).

ref: http://en.wikipedia.org/wiki/Setuid#setuid_on_executables

edit: setuid only seems to work for compiled executables (binaries), and not interpreted scripts, so you may need to use a compiled setuid wrapper.

Slippery answered 21/8, 2014 at 20:5 Comment(1)
Mmh the author is asking for python scripts, so - as you wrote - setuid is useless.Wavell
G
2

Use Tcl and Expect, plus subprocess to elevate yourself. So basically it's like this:

sudo.tcl

spawn sudo
expect {
    "Password:" {
        send "password"
    }
}

sudo.py

import subprocess
subprocess.call(['tclsh', 'sudo.tcl'])

And then run sudo.py.

Godding answered 12/9, 2014 at 20:5 Comment(3)
I get invalid command name "spawn" when I run the Python script (with everything exactly as you have it here). What should I do differently?Bewley
Maybe using Python's pexpect module and the pexpect.spawnu class will be helpful in creating an all-Python solution. If you know how to use it, feel free to edit that into your answer. (Not that I need an all-Python solution. So long as it works for me, that's great.)Bewley
I need an all python solution to this. Do you have one?Telemotor
R
0

You can use setuid to set the users uid. But for obvious security reasons you can only do this if you are root (or the program has suid root rights). Both of these are probably a bad idea.

In this case you need to sudo rights to run a specific program. In that case just sub to "sudo theprogram" instead.

Rolf answered 4/3, 2011 at 9:37 Comment(0)
A
0
import subprocess
subprocess.check_output("sudo -i -u " + str(username) + " ls -l", shell=True).decode("utf-8").strip()
Ardellearden answered 11/4, 2016 at 16:54 Comment(0)
B
0

Not sure how this would help you, and it does not answer the question, yet, it is a workaround to think about when you run into a needed "root" user problem and you need to be "root" only to read / write in a folder or file.

You can then change the permissions and also switch them back afterwards. I had this in a docker-compose file that started a Python script that deployed an application to a server. This workaround was the only way how I got it to run. I do not even need to change the permissions from the container bash, instead, the script does that, and only the password is needed twice.

Before this workaround, I tried to change to the root user and then execute large blocks of code with that root user, to no avail.

run("ls -ld /usr/local/my_project/")
run("sudo chmod o+wx /usr/local/my_project/")
run("ls -ld /usr/local/my_project/")
my_code_that_needed_root_rights_and_now_runs_without_root_user()
run("sudo chmod 774 /usr/local/my_project/")
run("ls -ld /usr/local/my_project/")

And the output:

[server_connection] run: ls -ld /usr/local/my_project/
[server_connection] Login password for 'my_user': 
[server_connection] out: drwxrwxr-- 45 root 100005 4096 Apr 25 13:52 /usr/local/my_project/
[server_connection] out: 

[server_connection] run: sudo chmod o+wx /usr/local/my_project/
[server_connection] out: [sudo] password for my_user: 
[server_connection] out: 

[server_connection] run: ls -ld /usr/local/my_project/
[server_connection] out: drwxrwxrwx 45 root 100005 4096 Apr 25 13:52 /usr/local/my_project/
[server_connection] out: 

[...]    

[server_connection] run: sudo chmod 774 /usr/local/my_project/
[server_connection] out: [sudo] password for my_user: 
[server_connection] out: 

[server_connection] run: ls -ld /usr/local/my_project/
[server_connection] out: drwxrwxr-- 46 root 100005 4096 Apr 25 14:02 /usr/local/my_project/
[server_connection] out: 

After this, the server folder had the same permissions as before, and the code did not need the root user to run through.

Browband answered 26/4, 2022 at 0:2 Comment(0)
I
-5

Are you talking about having the user input password half way through your execution? raw_input() can take a user input from console, but it will not mask the password.

>>>> y = raw_input()
somehting
>>> y
'somehting'
Immaterialism answered 10/9, 2014 at 22:26 Comment(1)
No. We're talking about permissions, although programs that grant permissions tend to ask for passwords (but that's not the key element). (Like using something sudo half-way through a program without using a second script, or such.)Bewley

© 2022 - 2024 — McMap. All rights reserved.