diff --git a/src/gitolite.pm b/src/gitolite.pm index 0ce43bf..dbb8152 100644 --- a/src/gitolite.pm +++ b/src/gitolite.pm @@ -472,6 +472,117 @@ sub cli_repo_rights { print "$perm $creator\n"; } +# ---------------------------------------------------------------------------- +# setup the ~/.ssh/authorized_keys file +# ---------------------------------------------------------------------------- + +sub setup_authkeys +{ + # ARGUMENTS + + my($bindir, $GL_KEYDIR, $user_list_p) = @_; + # calling from outside the normal compile script may mean that argument 2 + # may not be passed; so make sure it's a valid hashref, even if empty + $user_list_p = {} unless $user_list_p; + + # CONSTANTS + + # command and options for authorized_keys + my $AUTH_COMMAND="$bindir/gl-auth-command"; + my $AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; + + # START + + my $authkeys_fh = wrap_open( "<", $ENV{HOME} . "/.ssh/authorized_keys", + "\tFor security reasons, gitolite will not *create* this file if it does\n" . + "\tnot already exist. Please see the \"admin\" document for details\n"); + my $newkeys_fh = wrap_open( ">", $ENV{HOME} . "/.ssh/new_authkeys" ); + # save existing authkeys minus the GL-added stuff + while (<$authkeys_fh>) + { + print $newkeys_fh $_ unless (/^# gito(sis-)?lite start/../^# gito(sis-)?lite end/); + } + + # add our "start" line, each key on its own line (prefixed by command and + # options, in the standard ssh authorized_keys format), then the "end" line. + print $newkeys_fh "# gitolite start\n"; + wrap_chdir($GL_KEYDIR); + my @not_in_config; # pubkeys exist but users don't appear in the config file + for my $pubkey (`find . -type f`) + { + chomp($pubkey); $pubkey =~ s(^\./)(); + + # security check (thanks to divVerent for catching this) + unless ($pubkey =~ $REPONAME_PATT) { + print STDERR "$pubkey contains some unsavoury characters; ignored...\n"; + next; + } + + # lint check 1 + unless ($pubkey =~ /\.pub$/) + { + print STDERR "WARNING: pubkey files should end with \".pub\", ignoring $pubkey\n"; + next; + } + + my $user = $pubkey; + $user =~ s(.*/)(); # foo/bar/baz.pub -> baz.pub + $user =~ s/(\@[^.]+)?\.pub$//; # baz.pub, baz@home.pub -> baz + + # lint check 2 -- don't print right now; just collect the messages + push @not_in_config, "$user($pubkey)" if %$user_list_p and not $user_list_p->{$user}; + $user_list_p->{$user} = 'has pubkey' if %$user_list_p; + # apparently some pubkeys don't end in a newline... + my $pubkey_content; + { + local $/ = undef; + local @ARGV = ($pubkey); + $pubkey_content = <>; + } + $pubkey_content =~ s/\s*$/\n/; + # don't trust files with multiple lines (i.e., something after a newline) + if ($pubkey_content =~ /\n./) + { + print STDERR "WARNING: a pubkey file can only have one line (key); ignoring $pubkey\n" . + " If you want to add multiple public keys for a single user, use\n" . + " \"user\@host.pub\" file names. See the \"one user, many keys\"\n" . + " section in doc/3-faq-tips-etc.mkd for details.\n"; + next; + } + print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; + print $newkeys_fh $pubkey_content; + } + + # lint check 2 -- print less noisily + if (@not_in_config > 10) { + print STDERR "$WARN You have " . scalar(@not_in_config) . " pubkeys that do not appear to be used in the config\n"; + } elsif (@not_in_config) { + print STDERR "$WARN the following users (pubkey files in parens) do not appear in the config file:\n", join(",", sort @not_in_config), "\n"; + } + + # lint check 3; a little more severe than the first two I guess... + { + my @no_pubkey = + grep { $_ !~ /^(gitweb|daemon|\@.*|~\$creator|\$readers|\$writers)$/ } + grep { $user_list_p->{$_} ne 'has pubkey' } + keys %{$user_list_p}; + if (@no_pubkey > 10) { + print STDERR "$WARN You have " . scalar(@no_pubkey) . " users WITHOUT pubkeys...!\n"; + } elsif (@no_pubkey) { + print STDERR "$WARN the following users have no pubkeys:\n", join(",", sort @no_pubkey), "\n"; + } + } + + print $newkeys_fh "# gitolite end\n"; + close $newkeys_fh or die "$ABRT close newkeys failed: $!\n"; + + # all done; overwrite the file (use cat to avoid perm changes) + system("cat $ENV{HOME}/.ssh/authorized_keys > $ENV{HOME}/.ssh/old_authkeys"); + system("cat $ENV{HOME}/.ssh/new_authkeys > $ENV{HOME}/.ssh/authorized_keys") + and die "couldn't write authkeys file\n"; + system("rm $ENV{HOME}/.ssh/new_authkeys"); +} + # ---------------------------------------------------------------------------- # S P E C I A L C O M M A N D S # ---------------------------------------------------------------------------- diff --git a/src/gl-compile-conf b/src/gl-compile-conf index f80c035..d4b7289 100755 --- a/src/gl-compile-conf +++ b/src/gl-compile-conf @@ -54,7 +54,7 @@ open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q'); # these are set by the "rc" file our ($GL_ADMINDIR, $GL_CONF, $GL_KEYDIR, $GL_CONF_COMPILED, $REPO_BASE, $REPO_UMASK, $PROJECTS_LIST, $GIT_PATH, $GL_WILDREPOS, $GL_GITCONFIG_KEYS, $GL_PACKAGE_HOOKS, $GL_BIG_CONFIG, $GL_NO_DAEMON_NO_GITWEB, $GL_NO_CREATE_REPOS, $GL_NO_SETUP_AUTHKEYS); # and these are set by gitolite.pm -our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $AUTH_COMMAND, $AUTH_OPTIONS, $ABRT, $WARN); +our ($REPONAME_PATT, $REPOPATT_PATT, $USERNAME_PATT, $ABRT, $WARN); # the common setup module is in the same directory as this running program is my $bindir = $0; @@ -73,10 +73,6 @@ $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH; # definitions specific to this program # ---------------------------------------------------------------------------- -# command and options for authorized_keys -$AUTH_COMMAND="$bindir/gl-auth-command"; -$AUTH_OPTIONS="no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; - # groups can now represent user groups or repo groups. # $groups{group}{member} = "master" (or name of fragment file in which the @@ -559,93 +555,6 @@ unless ($GL_NO_DAEMON_NO_GITWEB) { # "compile" ssh authorized_keys # ---------------------------------------------------------------------------- -# NOTE: for now we assume that setting up authkeys is the LAST thing we do! -exit 0 if $GL_NO_SETUP_AUTHKEYS; - -my $authkeys_fh = wrap_open( "<", $ENV{HOME} . "/.ssh/authorized_keys", - "\tFor security reasons, gitolite will not *create* this file if it does\n" . - "\tnot already exist. Please see the \"admin\" document for details\n"); -my $newkeys_fh = wrap_open( ">", $ENV{HOME} . "/.ssh/new_authkeys" ); -# save existing authkeys minus the GL-added stuff -while (<$authkeys_fh>) -{ - print $newkeys_fh $_ unless (/^# gito(sis-)?lite start/../^# gito(sis-)?lite end/); +unless ($GL_NO_SETUP_AUTHKEYS) { + &setup_authkeys($bindir, $GL_KEYDIR, \%user_list); } - -# add our "start" line, each key on its own line (prefixed by command and -# options, in the standard ssh authorized_keys format), then the "end" line. -print $newkeys_fh "# gitolite start\n"; -wrap_chdir($GL_KEYDIR); -my @not_in_config; # pubkeys exist but users don't appear in the config file -for my $pubkey (`find . -type f`) -{ - chomp($pubkey); $pubkey =~ s(^\./)(); - - # security check (thanks to divVerent for catching this) - unless ($pubkey =~ $REPONAME_PATT) { - print STDERR "$pubkey contains some unsavoury characters; ignored...\n"; - next; - } - - # lint check 1 - unless ($pubkey =~ /\.pub$/) - { - print STDERR "WARNING: pubkey files should end with \".pub\", ignoring $pubkey\n"; - next; - } - - my $user = $pubkey; - $user =~ s(.*/)(); # foo/bar/baz.pub -> baz.pub - $user =~ s/(\@[^.]+)?\.pub$//; # baz.pub, baz@home.pub -> baz - - # lint check 2 -- don't print right now; just collect the messages - push @not_in_config, "$user($pubkey)" unless $user_list{$user}; - $user_list{$user} = 'has pubkey'; - # apparently some pubkeys don't end in a newline... - my $pubkey_content = `cat $pubkey`; - $pubkey_content =~ s/\s*$/\n/; - # don't trust files with multiple lines (i.e., something after a newline) - if ($pubkey_content =~ /\n./) - { - print STDERR "WARNING: a pubkey file can only have one line (key); ignoring $pubkey\n" . - " If you want to add multiple public keys for a single user, use\n" . - " \"user\@host.pub\" file names. See the \"one user, many keys\"\n" . - " section in doc/3-faq-tips-etc.mkd for details.\n"; - next; - } - print $newkeys_fh "command=\"$AUTH_COMMAND $user\",$AUTH_OPTIONS "; - print $newkeys_fh $pubkey_content; -} - -# lint check 2 -- print less noisily -if (@not_in_config > 10) { - print STDERR "$WARN You have " . scalar(@not_in_config) . " pubkeys that do not appear to be used in the config\n"; -} elsif (@not_in_config) { - print STDERR "$WARN the following users (pubkey files in parens) do not appear in the config file:\n", join(",", sort @not_in_config), "\n"; -} - -# lint check 3; a little more severe than the first two I guess... -{ - my @no_pubkey = - grep { $_ !~ /^(gitweb|daemon|\@.*|~\$creator|\$readers|\$writers)$/ } - grep { $user_list{$_} ne 'has pubkey' } - keys %user_list; - if (@no_pubkey > 10) { - print STDERR "$WARN You have " . scalar(@no_pubkey) . " users WITHOUT pubkeys...!\n"; - } elsif (@no_pubkey) { - print STDERR "$WARN the following users have no pubkeys:\n", join(",", sort @no_pubkey), "\n"; - } -} - -print $newkeys_fh "# gitolite end\n"; -close $newkeys_fh or die "$ABRT close newkeys failed: $!\n"; - -# all done; overwrite the file (use cat to avoid perm changes) -system("cat $ENV{HOME}/.ssh/authorized_keys > $ENV{HOME}/.ssh/old_authkeys"); -system("cat $ENV{HOME}/.ssh/new_authkeys > $ENV{HOME}/.ssh/authorized_keys") - and die "couldn't write authkeys file\n"; -system("rm $ENV{HOME}/.ssh/new_authkeys"); - -# NOTE: if you're adding code here that is unrelated to setting up authkeys, -# remember that control may not reach here if a sysadm has set -# GL_NO_SETUP_AUTHKEYS in the rc file. diff --git a/src/gl-setup-authkeys b/src/gl-setup-authkeys new file mode 100755 index 0000000..377f798 --- /dev/null +++ b/src/gl-setup-authkeys @@ -0,0 +1,54 @@ +#!/usr/bin/perl -w + +# shim program + +# arg-1: keydir + +# - an external program populates "keydir" with *all* keys and then +# calls us, giving "keydir" as arg-1 +# - we then call gitolite.pm's "setup_authkeys" function to do its thing + +# IMPLEMENTATION NOTE: make sure this is in the same directory as +# "gitolite.pm" and all the rest of "src/". + +# DISCUSSION: +# +# For now, we will assume *all* the keys are in the keydir passed. The +# setup_authkeys routine factored out from the old gl-compile-conf is +# not setup to take a partial set of keys and create the +# ~/.ssh/authorized_keys file. +# +# Also, there are issues to do with *deleted* keys that need to be taken +# care of. +# +# All in all, unless it is shown to be quite inefficient, I'd much +# prefer processing *all* keys each time there is a change. + +# setup +my $bindir = $0; +$bindir =~ s/\/[^\/]+$//; +$bindir = "$ENV{PWD}/$bindir" unless $bindir =~ /^\//; +require "$bindir/gitolite.pm"; + +# prevent newbie from running it accidentally and clobbering his authkeys +# file! +if (@ARGV and $ARGV[0] eq '-batch') { + shift; +} else { + print STDERR " + This is a cronnable, batchable, program to rewrite ~/.ssh/authorized_keys + using public keys in a given directory. + + If you are ABSOLUTELY sure you know what you're doing, here's how: + + $0 -batch keydir + + where 'keydir' contains a bunch of '*.pub' files\n\n"; + exit 1; +} + +# quick sanity check and run +my $keydir = shift or die "I need a directory name\n"; +-d $keydir or die "$keydir should be a directory\n"; + +&setup_authkeys($bindir, $keydir);