{"thread":{"id":"23768","subject":"avoiding anonymous commits from root/shared accounts","startedAt":"2010-05-10T17:05:17Z","lastAt":"2010-05-10T21:11:23Z","messageCount":2,"participants":["Nick","Alex Vandiver"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"141411","messageId":"4BE83CCD.2090505@letterboxes.org","threadId":"23768","inReplyTo":null,"subject":"avoiding anonymous commits from root/shared accounts","fromName":"Nick","fromEmail":"oinksocket@letterboxes.org","sentAt":"2010-05-10T17:05:17Z","receivedAt":"2010-05-10T17:05:17Z","isPatch":false,"sender":{"key":"oinksocket@letterboxes.org","avatar":null},"body":"Hi,\n\nI have a question about what is probably an unusual use-case, where git is being\nused from the root account, or an account shared by various committers.\n\n\nI'm setting up a shared git repository, currently accessed via plain ssh to the\nserver in question. The code has been inherited, and is migrated from CVS.\n\nThe code is a sysadmin tool designed to set up a new server and keep its\nconfiguration synchronised to one of several templates thereafter. Typically, it\nis checked out in /root on a freshly installed server, and \"make all\" is run as\nroot to configure the services, set up user accounts, etc.\n\n>From then on you pull updates from the repository, run \"make all\", and the\nmachine is reconfigured, services restarted, etc. as necessary.\n\nThe problem is maintenance of this code.  In the past, fixes might be made on\nany server, then pushed back into the repository to be replicated everywhere.\nThere are several users, each of whom might commit changes to this tool.  When\nit was in CVS, a common account was used to allow commits from anyone and\nanywhere - and as a result nothing can be attributed to anyone.\n\n\n\nI want to avoid these anonymous commits from now on.  The trouble is, I'm not\nsure the best way to, as there is no guarantee any accounts but root will exist,\nand if they do, some of these are shared accounts various people can log in as.\n(This may or may not be advisable, but for now that's the way it works)\n\nI also don't want to make it easy for the maintainers to do the right thing,\nthey will already be re-adjusting to git.  For that reason, just mandating that\neveryone sets $GIT_AUTHOR_NAME etc. manually on log-in isn't very satisfactory.\n\nThe best idea I've come across seems to be some sort of wrapper for git, which\nif no $GIT_USER_* is defined, can use $SUDO_USER and/or `who am i` to identify\nthe original log-in account, and sets $GIT_AUTHOR_NAME etc. - else if it can't\ndo this, it refuses to commit.  Or perhaps it would be a script which spawns a\nshell with the right environment to invoke git commands from, after successfully\ndetermining the identity.\n\n\nBut before I investigate this avenue any further, I wonder is there any prior\nart addressing this sort of situation, using git?\n\nThanks\n\nNick\n"},{"id":"141440","messageId":"1273525498-sup-6628@utwig","threadId":"23768","inReplyTo":"4BE83CCD.2090505@letterboxes.org","subject":"Re: avoiding anonymous commits from root/shared accounts","fromName":"Alex Vandiver","fromEmail":"alex@chmrr.net","sentAt":"2010-05-10T21:11:23Z","receivedAt":"2010-05-10T21:11:23Z","isPatch":false,"sender":{"key":"alex@chmrr.net","avatar":"https://avatars.githubusercontent.com/u/28347?v=4"},"body":"At Mon May 10 13:05:17 -0400 2010, Nick wrote:\n> [snip]\n> The best idea I've come across seems to be some sort of wrapper for git, which\n> if no $GIT_USER_* is defined, can use $SUDO_USER and/or `who am i` to identify\n> the original log-in account, and sets $GIT_AUTHOR_NAME etc. - else if it can't\n> do this, it refuses to commit.  Or perhaps it would be a script which spawns a\n> shell with the right environment to invoke git commands from, after successfully\n> determining the identity.\n\nAt work, we have a number of repositories which we store server\nconfigurations in, most of which are only writable as root.  We use\nthe script below to ensure that git mostly doesn't lie about the\nauthors of commits.  This won't solve your problem of people logging\nin under shared credentials -- and it also _does_ allow commits as\n'root' if you logged in directly as root -- but it's perhaps a partial\nsolution for you.\n\n - Alex\n\n-------------------->8--------------------\n#!/usr/bin/perl\n\nuse strict;\nuse warnings;\nuse constant EMAIL_DOMAIN => \"example.com\";\n\nsetenv( get_user($$) );\nexec(\"/usr/bin/git\", @ARGV);\n\nsub setenv {\n    my $user = shift;\n\n    # If they're _really_ _really_ root, just bail now\n    return if $user eq \"root\";\n\n    # Ditto if we can't find the user (?!)\n    my @getpw = getpwnam($user);\n    return unless @getpw;\n\n    my $name;\n    my $email;\n\n    # See if we can pull from the user's config\n    my $gitconfig = \"$getpw[7]/.gitconfig\";\n    if (-r $gitconfig) {\n        $name  = `/usr/bin/git config --file $gitconfig user.name`;\n        chomp $name;\n        $email = `/usr/bin/git config --file $gitconfig user.email`;\n        chomp $email;\n    }\n\n    # Fall back to getent\n    $name  ||= $getpw[6] || $user;\n    $email ||= $user . '@' . EMAIL_DOMAIN;\n\n    $ENV{GIT_AUTHOR_NAME} = $name;\n    $ENV{GIT_AUTHOR_EMAIL} = $email;\n}\n\nsub get_user {\n    my $pid = shift;\n\n    # See if the PID is bogus\n    return \"root\" unless $pid and kill 0, $pid;\n\n    # Pull out the env from it\n    my %env = getenv($pid);\n\n    # Simplest case -- check USER first\n    if ($env{USER} and $env{USER} ne \"root\") {\n        return $env{USER};\n    }\n\n    # Or we're running under sudo\n    if ($env{SUDO_USER} and $env{SUDO_USER} ne \"root\") {\n        return $env{SUDO_USER};\n    }\n\n    # They did something like `sudo su -`\n    return get_user(parent_pid($pid));\n}\n\nsub getenv {\n    my $pid = shift;\n    my $env = do {local @ARGV = (\"/proc/$pid/environ\"); local $/; <>};\n    my @lines = split /\\0/, $env;\n    return () unless grep {/=/} @lines;\n    my %env = map {split /=/, $_, 2} @lines;\n    return %env;\n}\n\nsub parent_pid {\n    my $pid = shift;\n    my $stat = do {local @ARGV = (\"/proc/$pid/stat\"); local $/; <>};\n    my (undef, undef, undef, $ppid) = split ' ', $stat;\n    return $ppid;\n}\n-- \nNetworking -- only one letter away from not working\n"}]}