0

I'm trying to create a service that starts a process.

I want the startup of the process to be able to do a few commands as root and then run the rest of the process as a less privileged non-human user.

For example: I know the apache user requires some root privileges to do some web server configuration with ports and starting various server processes but it does not run as root all the time. I can't find any code that does something similar to this and was wondering if there are any other examples I can use.

Essentially my question is, how do I grant a non-root user temporary root privileges to run specific root only processes?

Or am I thinking about this the wrong way: Do I not grant the user root privileges and instead have the root user run the root-required processes?

Wimateeka
  • 1,005
  • 2
    A process running as root can always drop its privileges by changing its GID and UID to a less privileged user. see man setgid, man setuid. Please add more details about how you want to start your service. – Bodo Mar 23 '20 at 13:08
  • A related question is https://unix.stackexchange.com/q/132663/5132 . – JdeBP Mar 23 '20 at 15:20

1 Answers1

0

There are quite a few options depending on exactly what you are doing as the root user. However for automated services ("like apache"), you usually start as root and the drop to a restricted user.

Debian style configuration (including Ubuntu and Mint) would then have a file in /etc/default/my-service-name setting which user is switched to. Other linux distributions have a different place to store the setting, but the technique ultimately the same.

If you are writing the service program yourself, you would use setuid(). Once you've called setuid, the process cannot go back to having root privilege. You will need to lookup the user by name and also set the groups for the user.

simplifying some code I wrote a long time ago:

int lockToUser(const char * user) {
    # Look up the username in /etc/passwd
    struct passwd * pwd = getpwnam(user);
    if (!pwd) {
        # Failed... Could not find user, see errno
        return 0;
    }
    # setuid sets the user
    # setgid sets the main group similar to the group in /etc/passwd
    # Sets the other groups which will grant additional permissions.
    #   similar to the groups in /etc/groups
    if (initgroups(user, pwd->pw_gid) || setgid(pwd->pw_gid) || setuid(pwd->pw_uid)) {
        # Failed... Could not lock down to user, see errno
        return 0;
    }
    return 1;
}
  • Cave! This does not always work in multithreaded programs on Linux, particularly in programming languages that do not use a C run-time library, such as Go. And sometimes what one thinks is a single-threaded program is not. – JdeBP Mar 23 '20 at 15:17
  • @JdeBP Okay yes, having read a bit more it seems go has a specific problem because it can spawn threads before your main() starts. That's not particularly common across languages. But it's a good shout that some languages do strange things. – Philip Couling Mar 23 '20 at 16:03
  • It's not because Go spawns threads. It's because Linux does not give a single set of credentials to all threads. Ironically, it is C implementations that do the strange things to try to hide this. – JdeBP Mar 23 '20 at 16:19
  • @JdeBP What I mean is your comment "sometimes what one thinks is single threaded is not". In most languages, the simple discipline in having a single-threaded startup phase gets round the issue. It's not so common for languages to be [dangerously] multi-threaded without the programmers explicit request to create threads. go-lang is a notable exception where it can spawn threads before the program has even got to main(). Yes the problem still exists in other languages where the developer has initiated multiple threads before calling setuid. – Philip Couling Mar 23 '20 at 16:41
  • @JdeBP see comments from https://github.com/golang/go/issues/1435#issuecomment-66054157 – Philip Couling Mar 23 '20 at 16:43