separate out the code that sets up ~/.ssh/authorized_keys
NOTE: there are no *functional* changes in this for *normal* gitolite users. It's just a chunk of code moving into a new subroutine etc. KDE needs to populate the authkeys file from an LDAP store. Other large projects may have similar means to store keys, depending on how they do their user provisioning so a generic solution is worth exploring. This means that in these special cases - the gitolite-admin repo's keydir/ directory is not needed [1] - but they still need to create the authkeys file somehow Implementation: - write a shim program to make the authkeys-generation code callable from the command line/shell. - set $GL_NO_SETUP_AUTHKEYS=1 in the rc file to disable authkey generation during a "compile" (admin repo push) Expected usage of new program gl-setup-authkeys: - LDAP change triggers some script - this script collects all keys from LDAP, puts them in some directory, and then calls gl-setup-authkeys, passing it the name of the directory ALSO PLEASE SEE COMMENTS AT THE TOP OF THE NEW PROGRAM IN THIS COMMIT FOR SOME IMPORTANT DISCUSSION. ---- Footnotes: [1] It doesn't make sense to use it if the keys will be maintained by some other entity and can be called up as needed, and it adds an unnecessary extra step.
This commit is contained in:
parent
2dc02e9a75
commit
c8879264e6
111
src/gitolite.pm
111
src/gitolite.pm
|
@ -472,6 +472,117 @@ sub cli_repo_rights {
|
||||||
print "$perm $creator\n";
|
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
|
# S P E C I A L C O M M A N D S
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
|
@ -54,7 +54,7 @@ open STDOUT, ">", "/dev/null" if (@ARGV and shift eq '-q');
|
||||||
# these are set by the "rc" file
|
# 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);
|
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
|
# 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
|
# the common setup module is in the same directory as this running program is
|
||||||
my $bindir = $0;
|
my $bindir = $0;
|
||||||
|
@ -73,10 +73,6 @@ $ENV{PATH} .= ":$GIT_PATH" if $GIT_PATH;
|
||||||
# definitions specific to this program
|
# 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 can now represent user groups or repo groups.
|
||||||
|
|
||||||
# $groups{group}{member} = "master" (or name of fragment file in which the
|
# $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
|
# "compile" ssh authorized_keys
|
||||||
# ----------------------------------------------------------------------------
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
# NOTE: for now we assume that setting up authkeys is the LAST thing we do!
|
unless ($GL_NO_SETUP_AUTHKEYS) {
|
||||||
exit 0 if $GL_NO_SETUP_AUTHKEYS;
|
&setup_authkeys($bindir, $GL_KEYDIR, \%user_list);
|
||||||
|
|
||||||
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)" 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.
|
|
||||||
|
|
54
src/gl-setup-authkeys
Executable file
54
src/gl-setup-authkeys
Executable file
|
@ -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);
|
Loading…
Reference in a new issue