2

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)
tonix
  • 6,671
  • 13
  • 75
  • 136

1 Answers1

2

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>.
torek
  • 448,244
  • 59
  • 642
  • 775
  • Thanks for the explanation! I ran the daemon via sudo with the following command: `sudo git daemon --reuseaddr --user=git-ro --group=git-ro --base-path=/opt/git/ /opt/git/`, it works. However if I take a look with `ps aux | grep "git"` three different processes of git-daemon, with two of them running as __root__ and only one running as __git-ro__(please, check my edit). Does it mean that the deamon still run as root? Should it be considered the expected behaviour or should I only see one process running with `git-ro` as the owner? – tonix Aug 16 '15 at 16:52
  • When you run `sudo git daemon ...` you have one process that is the `sudo` command itself, a second that is `git` that runs `git-daemon` out of the executable path (try `git --exec-path`, just as yourself, to see where that is), and a third process that is `git-daemon` itself. That third process has done the `setuid`; the other two are obviously just waiting for it to finish (which generally never happens, it runs until killed). – torek Aug 16 '15 at 21:39
  • But in my case the `git-daemon` process is the process of `git-ro` and not `root`, how could it do the `setuid` if the process owner is not root? – tonix Aug 16 '15 at 21:49
  • I think I get it. You say that what happens is this: `git` runs `git-daemon` as root (effective UID because of sudo), then the `git-daemon` process running with effective UID 0 (root) immediately does a `setuid` and sets it to the UID of an unprivileged user (git-ro) in my case. Is that correct? – tonix Aug 16 '15 at 22:43
  • @tonix: yes. You can cut out one middleman by running `git-daemon` directly, although then you'll need to manually do whatever setup (if any) `git` does that `git-daemon` requires (probably none, but I haven't experimented). – torek Aug 17 '15 at 06:00
  • All right. And further, I can also kill those two git processes running as root waiting for the git process to finish safely, can't I? – tonix Aug 17 '15 at 06:03
  • You *can*; normally, though, you'd just start `git-daemon` directly as root from some rc.d/ or similar file (no idea how one does this with systemd, I've managed to avoid using that). Then you'd have no sudo and no middleman git to worry about in the first place... – torek Aug 17 '15 at 06:13
  • Thanks. I am running it as a launchd on-demand daemon on OS X, launchd starts it on-demand if the git-daemon is not running yet whenever an incoming request arrives on port 9418. – tonix Aug 17 '15 at 06:25