I have a script that needs to be run as a super user:
$ sudo ./python-script.py
Within this script, I do some other things that do not require super user privileges.
$ os.mkdir('somefolder')
What is the best/most efficient way of creating the directory as non-root user? Should I let the script make the directory as root user, and then change permissions on it?
os.mkdir does allow you to specify the permissions explicitly:
os.mkdir(path [, mode=0777])
And you also have the option of running os.chown to set the user and group
os.chown(path, uid, gid)
You can probably get the original user like this (but it might be platform specific?)
import os
original_user = os.environ.get('SUDO_USER')
original_uid = os.environ.get('SUDO_UID')
original_gid = os.environ.get('SUDO_GID')
Related
How can i change to root user providing my password in the script?
I have this code
import os
# change to root user
# changing to root call this function
# example
os.sytem('reboot')
PS. not only this function but iptables too, so i need to change to root with out typing the password because i want to be automated.
If you don't want to do anything after the call to reboot, you can use os.exec*() to replace the current Python process with sudo, which will switch to the root user. Then, have sudo execute reboot as below.
import os
import shutil
SUDO_PATH = shutil.which('sudo')
if SUDO_PATH is None:
raise OSError('cannot find sudo executable')
os.execl(SUDO_PATH, SUDO_PATH, 'reboot')
For the cases where you wish do something after executing an external process, use subprocess.run().
I have a python script on raspberryi pi 3. I want to make it only executable for x user without having root permission. It can not be readable and writable. How can I do that? I gave only x(execute) permission to the file for x user. But when I execute the script, it wants root password.
If the user has access to the script, he can modify the content himself. However, just for the sake of the answer or method, we can do something like this:
You can restrict the access to the script by getting the username of the person on the operating the system:
import getpass
if getpass.getuser() in ['user1','user2'] # allowed user list:
main() # main function
else:
print("You are not authorised to run this script")
RELATED: Python multiprocessing: Permission denied
I want to use Python's multiprocessing.Pool
import multiprocessing as mp
pool = mp.Pool(3)
for i in range(num_to_run):
pool.apply_async(popen_wrapper, args=(i,), callback=log_result)
I get OSError
File "/usr/local/lib/python2.6/multiprocessing/__init__.py", line 178, in RLock
return RLock()
File "/usr/local/lib/python2.6/multiprocessing/synchronize.py", line 142, in __init__
SemLock.__init__(self, RECURSIVE_MUTEX, 1, 1)
File "/usr/local/lib/python2.6/multiprocessing/synchronize.py", line 49, in __init__
sl = self._semlock = _multiprocessing.SemLock(kind, value, maxvalue)
OSError: [Errno 13] Permission denied
I read in the related question that it's due to not having r/w to /dev/shm
Besides changing the permission in /dev/shm, is there a way to run as root in the code?
I initially thought you could do something like os.umask() but it didnt work
EDIT (rephrasing the question):
let's say a username A has r/w access to directory A
You are user B and your program needs access to directory A. how do you run a program as user A?
In order from the least dangerous to the most dangerous.
You can try dropping permissions as John Zwinck suggested.
Basically you would start the program with root level permissions,
immediately do what you need to do, and then switch to a non-root
user.
From this StackOverflow.
import os, pwd, grp
def drop_privileges(uid_name='nobody', gid_name='nogroup'):
if os.getuid() != 0:
# We're not root so, like, whatever dude
return
# Get the uid/gid from the name
running_uid = pwd.getpwnam(uid_name).pw_uid
running_gid = grp.getgrnam(gid_name).gr_gid
# Remove group privileges
os.setgroups([])
# Try setting the new uid/gid
os.setgid(running_gid)
os.setuid(running_uid)
# Ensure a very conservative umask
old_umask = os.umask(077)
You could also require the credentials for the root user to be
inputed into the script, and then only use them when they are
required.
subprocess.call("sudo python RunStuffWithElevatedPrivelages.py")
#From here, the main script will continue to run without root permissions
Or if you don't want the script to prompt the user for the password you can do
subprocess.call("echo getRootCredentials() | sudo -S python RunStuffWithElevatedPrivelages.py")
Or you could just run the entire program as a root user -- sudo python myScript.py.
As far as temporarily giving users root permission to /dev/shm only when they run your script, the only thing I could think of was having some script that runs in the background under the root user that can temporarily grant anyone who uses your script root privileges to /dev/shm. This could be done through using setuid to grant such permissions and then after a certain amount of time or if the script ends the privilege is taken away. My only concern would be if there is a way a user who has temporarily been given such permissions might be able to secure more permanent privileges.
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)?)
I'd like to have a Python program start listening on port 80, but after that execute without root permissions. Is there a way to drop root or to get port 80 without it?
You won't be able to open a server on port 80 without root privileges, this is a restriction on the OS level. So the only solution is to drop root privileges after you have opened the port.
Here is a possible solution to drop root privileges in Python: Dropping privileges in Python. This is a good solution in general, but you'll also have to add os.setgroups([]) to the function to ensure that the group membership of the root user is not retained.
I copied and cleaned up the code a little bit, and removed logging and the exception handlers so it is left up to you to handle OSError properly (it will be thrown when the process is not allowed to switch its effective UID or GID):
import os, pwd, grp
def drop_privileges(uid_name='nobody', gid_name='nogroup'):
if os.getuid() != 0:
# We're not root so, like, whatever dude
return
# Get the uid/gid from the name
running_uid = pwd.getpwnam(uid_name).pw_uid
running_gid = grp.getgrnam(gid_name).gr_gid
# Remove group privileges
os.setgroups([])
# Try setting the new uid/gid
os.setgid(running_gid)
os.setuid(running_uid)
# Ensure a very conservative umask
old_umask = os.umask(077)
I recommend using authbind to start your Python program, so none of it has to run as root.
https://en.wikipedia.org/wiki/Authbind
It is not a good idea to ask the user to enter his/her user-name and group whenever I need to drop privileges. Here is a slightly modified version of Tamás's code which will drop privileges and switch to the user who initiated the sudo command. I am assuming you are using sudo (if not, use Tamás's code).
#!/usr/bin/env python3
import os, pwd, grp
#Throws OSError exception (it will be thrown when the process is not allowed
#to switch its effective UID or GID):
def drop_privileges():
if os.getuid() != 0:
# We're not root so, like, whatever dude
return
# Get the uid/gid from the name
user_name = os.getenv("SUDO_USER")
pwnam = pwd.getpwnam(user_name)
# Remove group privileges
os.setgroups([])
# Try setting the new uid/gid
os.setgid(pwnam.pw_gid)
os.setuid(pwnam.pw_uid)
#Ensure a reasonable umask
old_umask = os.umask(0o22)
#Test by running...
#./drop_privileges
#sudo ./drop_privileges
if __name__ == '__main__':
print(os.getresuid())
drop_privileges()
print(os.getresuid())
systemd can do it for you, if you start your program through systemd, systemd can hand off the already-open listening socket to it, and it can also activate your program on first connection. and you don't even need to daemonize it.
If you are going to go with the standalone approach, you need the capability CAP_NET_BIND_SERVICE (check capabilities man page). This can be done on a program-by-program basis with the correct command line tool, or by making your application (1) be suid root (2) start up (3) listen to the port (4) drop privileges / capabilities immediately.
Remember that suid root programs come with lots of security considerations (clean and secure environment, umask, privileges, rlimits, all those things are things that your program is going to have to set up correctly). If you can use something like systemd, all the better then.
The following is a further adaptation of Tamás's answer, with the following changes:
Use the python-prctl module to drop Linux capabilities to a specified list of capabilities to preserve.
The user can optionally be passed as a parameter (it defaults to looking up the user who ran sudo).
It sets all the user's groups and HOME.
It optionally changes directory.
(I'm relatively new to using this functionality, however, so I may have missed something. It might not work on older kernels (<3.8) or kernels with filesystem capabilities disabled.)
def drop_privileges(user=None, rundir=None, caps=None):
import os
import pwd
if caps:
import prctl
if os.getuid() != 0:
# We're not root
raise PermissionError('Run with sudo or as root user')
if user is None:
user = os.getenv('SUDO_USER')
if user is None:
raise ValueError('Username not specified')
if rundir is None:
rundir = os.getcwd()
# Get the uid/gid from the name
pwnam = pwd.getpwnam(user)
if caps:
prctl.securebits.keep_caps=True
prctl.securebits.no_setuid_fixup=True
# Set user's group privileges
os.setgroups(os.getgrouplist(pwnam.pw_name, pwnam.pw_gid))
# Try setting the new uid/gid
os.setgid(pwnam.pw_gid)
os.setuid(pwnam.pw_uid)
os.environ['HOME'] = pwnam.pw_dir
os.chdir(os.path.expanduser(rundir))
if caps:
prctl.capbset.limit(*caps)
try:
prctl.cap_permitted.limit(*caps)
except PermissionError:
pass
prctl.cap_effective.limit(*caps)
#Ensure a reasonable umask
old_umask = os.umask(0o22)
It can be used as follows:
drop_privileges(user='www', rundir='~', caps=[prctl.CAP_NET_BIND_SERVICE])
Most of this works unless you need to request the socket after you do some other stuff that you don't want to be superuser.
I made a project called tradesocket a while ago. It allows you to pass back and forth sockets on a posix system between processes. What I do is spin off a process at the beginning that stays superuser, and the rest of the process drops down in permissions and then requests the socket from the other.