On modern Linux you only need capability CAP_NET_BIND_SERVICE to bind to port 80, you DO NOT need to be root, not even at application startup. Capabilities is a POSIX standard, 1003.1e, that is a partitioning of the all powerful root privilege into a set of distinct privileges. See: python-cap-ng And /sbin/setcap, /sbin/getcap (these are equivalent to chmod setuid, and ls –l)
–
richardSep 16 '13 at 20:30

For Python2 and perhaps other interpreters, gaining capabilities is the part you want to be careful with -- libcap-ng can drop caps but it doesn't grant them. This answer to the question Ian referenced is a relatively safe way to dole out one cap at a time for specific projects: stackoverflow.com/a/21895123/1724577
–
duanevFeb 20 '14 at 21:34

6 Answers
6

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):

Keep in mind that the HOME directory will still be /root and not /home/uid_name, and that uid_name won't be able to do anything with ~/, which will then expand to /root/. This can affect modules like matplotlib which store configuration data in the HOME directory. authbind seems to be the correct way to handle this issue.
–
Daniel FMay 1 '14 at 20:29

And the HOME variable won't be the only one still thinking that the current user is root.
–
Daniel FMay 1 '14 at 20:37

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.

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.

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).