Hello and welcome to LQ.
To me, numbers 1 and 3 are conflicting. If the program needs special privileges to do a certain task (for example, bind to a port number less than 1024), I would suggest there are a few ways to minimize possibility of privilege escalation within the program (and yes, I think it is the “responsibility” of any app that needs special privileges to minimize the chance that it would be used for privilege escalation):
- Run initially as root. Do what you need to do (e.g., bind to low port) as early as possible, and then change user.
- Run initially as root. Change to a “normal” user, while keeping whichever POSIX capabilities are required.
- Run initially as a non-root, dedicated user. From within your application, execute a helper application (i.e., fork()/exec()) through sudo (your dedicated user should be given permission to run this and only this helper application as root in the sudoers file). This helper application will have the sole purpose of granting your process a special POSIX capability.
Among those, number 1 is the most commonly used. This is probably because it is more “portable” (POSIX capabilities and sudo are not found everywhere). Number 3 is the most “secure” (IMHO), since the only application running as “root” is the helper, which should be small and easy to maintain/audit. Of course, some capabilities are more valuable than others, and perhaps hijacking the application with a certain capability is privilege escalation enough for some purposes. In that case, there might be an optional step for numbers 2 and 3 that involves dropping capabilities after “doing its thing” (perhaps with the help of a helper application).
P.S., notice the lack of an apostrophe in my use of “its”.