Running git daemon with read only user gives "fatal: cannot drop privileges"

554 Views Asked by At

I am not sure if this is the right section where to post, if it is not, please tell me where should I post the following issue:

I am on a LAN with two machines: one with OS X Yosemite on it which acts as a Git server and another laptop running Ubuntu which acts as a Git client accessing the read-only repository through git://.

I am reading the Git Book. Here https://git-scm.com/book/it/v2/Git-on-the-Server-Git-Daemon, Scott Chacon says:

For security reasons, it is strongly encouraged to have this daemon run as a user with read-only permissions to the repositories – you can easily do this by creating a new user git-ro and running the daemon as them. For the sake of simplicity we’ll simply run it as the same git user that Gitosis is running as.

The command to run the git daemon is:

/usr/bin/git daemon --base-path=/opt/git/ /opt/git/

Now, I can run the command beautifully without any problem with my current user on the OS X machine (my current user is also an admin) and the Git read only daemon starts, but as soon as I try to run it as an unprivileged user which has read only access to the repo (in my case the user git-ro, as suggested in the book), git daemon complains and doesn't start:

$ /usr/bin/git daemon \
    --user=git-ro --group=git-ro \
    --reuseaddr \
    --base-path=/opt/git/ \
    /opt/git/
fatal: cannot drop privileges

I am running the commands on the Terminal.app of OS X, I haven't set up the git daemon to start at startup yet, cause I just would like to see how it works before setting all up. What does cannot drop privileges mean and how could I resolve and run the daemon with an unprivileged user which has read only permissions to the repository?

Thanks for the attention!

Edit: here http://git.661346.n2.nabble.com/regression-quot-96b9e0e3-config-treat-user-and-xdg-config-permission-problems-as-errors-quot-busted-n-td7582202.html#d1365658927000-296 it seems that the problem is related to the HOME directory of whom is executing the command, isn't it? If so, how should I act in my case?

Edit 2: here is the command ran with sudo:

$ sudo git daemon --reuseaddr --user=git-ro --group=git-ro --base-path=/opt/git/ /opt/git/

The daemon starts, however I have three processes of it running, and two of them as root:

$ ps aux | grep "git-ro"
git-ro           1477   0.0  0.0  2471332   1424 s000  S+    7:34PM   0:00.01 git-daemon --reuseaddr --user=git-ro --group=git-ro --base-path=/opt/git/ /opt/git/
root             1476   0.0  0.0  2464108   1608 s000  S+    7:34PM   0:00.01 git daemon daemon --reuseaddr --user=git-ro --group=git-ro --base-path=/opt/git/ /opt/git/
root             1475   0.0  0.1  2452612   2612 s000  S+    7:34PM   0:00.01 sudo git daemon --reuseaddr --user=git-ro --group=git-ro --base-path=/opt/git/ /opt/git/

Why the daemon still runs as root with two processes? Is it the expected behaviour or should I improve further?

Edit 3: Further, why if instead I run lsof and check what is listening on port 9418, I see two lines held by git-ro with the same pid? How is that possible? Where have the git daemon processes ran as root gone?

$ sudo lsof -i :9418
COMMAND    PID   USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
git-daemo 1477 git-ro    5u  IPv4 0xce9b2f57e8d5af93      0t0  TCP *:git (LISTEN)
git-daemo 1477 git-ro    6u  IPv6 0xce9b2f57e60cacc3      0t0  TCP *:git (LISTEN)
1

There are 1 best solutions below

8
On BEST ANSWER

The message comes from the function drop_privileges in git's daemon.c:

static void drop_privileges(struct credentials *cred)
{
        if (cred && (initgroups(cred->pass->pw_name, cred->gid) ||
            setgid (cred->gid) || setuid(cred->pass->pw_uid)))
                die("cannot drop privileges");
}

Note that if cred is NULL, this function amounts to a big no-op. If cred is not NULL the program must be running as the super-user, and the initgroups, setgid, and setuid sequence is intended to (as the function name implies) drop its super-user privileges and continue running instead as the supplied user-and-group.

This function is called from serve in the same file, which is called from main when the daemon starts up:

int main(int argc, char **argv)
{
...
        return serve(&listen_addr, listen_port, cred);

The cred argument here is passed down through serve to drop_privileges and hence is of direct interest. So where does cred get set? Let's look just a bit more at main, where cred is initialized and then later modified:

        struct credentials *cred = NULL;
...
        if (user_name)
                cred = prepare_credentials(user_name, group_name);

So now we need to find where user_name is set and/or modified:

        const char *pid_file = NULL, *user_name = NULL, *group_name = NULL;
...
                if (skip_prefix(arg, "--user=", &v)) {
                        user_name = v;
                        continue;
                }

At this point, even without looking at skip_prefix, it should be pretty obvious that it comes from the --user=... parameter you supply, and if you don't supply one, user_name will remain NULL so that cred will remain NULL so that drop_privileges will do nothing and the command will run as whoever invoked it (i.e., yourself, rather than the supplied user-name).

In short, unless you're running this as the super-user (e.g., with sudo), don't give it a --user= option, as all that will do is make it fail at startup like this.

This and the issue with $HOME are both mentioned in the manual page as well (though the formatting below is from my local git help daemon output rather than the kernel.org link):

   --user=<user>, --group=<group>
       Change daemon's uid and gid before entering the service loop. When
       only --user is given without --group, the primary group ID for the
       user is used. The values of the option are given to getpwnam(3) and
       getgrnam(3) and numeric IDs are not supported.

       Giving these options is an error when used with --inetd; use the
       facility of inet daemon to achieve the same before spawning git
       daemon if needed.

       Like many programs that switch user id, the daemon does not reset
       environment variables such as $HOME when it runs git programs, e.g.
       upload-pack and receive-pack. When using this option, you may also
       want to set and export HOME to point at the home directory of
       <user> before starting the daemon, and make sure any Git
       configuration files in that directory are readable by <user>.