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)