What is the best-practice way to write a python application where the majority of the code can run as the normal, non-root user -- but where there is at least one function that requires root permissions to execute?
I'm writing a python program where 99% of it can run fine in user-space. But I'm now adding a function that requires root. One solution is to just run the whole application as root. But that seems unnecessarily dangerous.
I figured "the right thing to do" would be to spawn some child process (or thread?) that:
- Has root permissions
- Only includes the functions that absolutely require root privileges
- Accepts no user input (or at least as little as possible and carefully sanitizes it under a very strict allowlist regex)
But I'm not exactly sure how to do all of this. And maybe there's other best-practices that I'm not considering?
Consider the following example code.
Note I'm just using sockets because binding to a port >= 1024 can be done as a normal user, but binding to any port < 1024 requires root privileges. But if there's a simpler example, please use it.
#!/usr/bin/env python3
import socket
def main():
user_operation()
root_operation()
def user_operation():
sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
sock.bind( ('localhost', 2222) )
def root_operation():
sock = socket.socket( socket.AF_INET, socket.SOCK_STREAM )
sock.bind( ('localhost', 22) )
if __name__ == "__main__":
main()
If I run the above program as the normal, unprivliged user then I get a Permission denied error
user@host:~$ ./spawn_root_child.py
Traceback (most recent call last):
File "/home/user/tmp/python_spawn_root_child/./spawn_root_child.py", line 18, in <module>
main()
File "/home/user/tmp/python_spawn_root_child/./spawn_root_child.py", line 7, in main
root_operation()
File "/home/user/tmp/python_spawn_root_child/./spawn_root_child.py", line 15, in root_operation
sock.bind( ('localhost', 22) )
PermissionError: [Errno 13] Permission denied
user@host:~$
But if I run it as root, it runs fine
user@host:~$ sudo ./spawn_root_child.py
user@host:~$
How can I update the above code so that it follows security best-practices, including the principle of least privilege?
Edit I'm looking for something that is:
- Robust (it would apply to small CLI scripts as well as large GUI applications)
- Cross-Platform (it should work in Linux, Windows, MacOS, and my python-powered toaster)
- Pythonic (following Python's best-practices)
If you are running your Python program in Linux, you can write a bash script and execute your Python code from inside it.
You can run your script as a non-root user, then raise privileges to root via
sudo python3 script.pyUsing
sudoonly in front of commands that require root is the best way because Linux will switch back to the regular user this way. (The worst way in my opinion is usingsudo suin which you would need to executeexitto manually leave root!)I would suggest separating your code into separate scripts, ones that do not require root and the one that needs root. This way you do not need to unnecessarily need to run
sudo script.pyfor scripts that do not need root.