diff options
Diffstat (limited to '')
-rwxr-xr-x | src/triggers/post-compile/ssh-authkeys | 142 | ||||
-rwxr-xr-x | src/triggers/post-compile/ssh-authkeys-shell-users | 51 | ||||
-rwxr-xr-x | src/triggers/post-compile/ssh-authkeys-split | 87 |
3 files changed, 280 insertions, 0 deletions
diff --git a/src/triggers/post-compile/ssh-authkeys b/src/triggers/post-compile/ssh-authkeys new file mode 100755 index 0000000..cd59aec --- /dev/null +++ b/src/triggers/post-compile/ssh-authkeys @@ -0,0 +1,142 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use Getopt::Long; + +use lib $ENV{GL_LIBDIR}; +use Gitolite::Rc; +use Gitolite::Common; + +$|++; + +# best called via 'gitolite trigger POST_COMPILE'; other modes at your own +# risk, especially if the rc file specifies arguments for it. (That is also +# why it doesn't respond to "-h" like most gitolite commands do). + +# option procesing +# ---------------------------------------------------------------------- + +# currently has one option: +# -kfn, --key-file-name adds the keyfilename as a second argument + +my $kfn = ''; +GetOptions( 'key-file-name|kfn' => \$kfn, ); + +tsh_try("sestatus"); +my $selinux = ( tsh_text() =~ /enforcing/ ); + +my $ab = $rc{GL_ADMIN_BASE}; +trace( 1, "'keydir' not found in '$ab'; exiting" ), exit if not -d "$ab/keydir"; +my $akdir = "$ENV{HOME}/.ssh"; +my $akfile = "$ENV{HOME}/.ssh/authorized_keys"; +my $glshell = $rc{GL_BINDIR} . "/gitolite-shell"; +my $auth_options = auth_options(); + +sanity(); + +# ---------------------------------------------------------------------- + +_chdir($ab); + +# old data +my $old_ak = slurp($akfile); +my @non_gl = grep { not /^# gito.*start/ .. /^# gito.*end/ } slurp($akfile); +chomp(@non_gl); +my %seen = map { $_ => 'a non-gitolite key' } ( fp(@non_gl) ); + +# pubkey files +chomp( my @pubkeys = `find keydir/ -type f -name "*.pub" | sort` ); +my @gl_keys = (); +for my $f (@pubkeys) { + my $fp = fp($f); + if ( $seen{$fp} ) { + _warn "$f duplicates $seen{$fp}, sshd will ignore it"; + } else { + $seen{$fp} = $f; + } + push @gl_keys, grep { /./ } optionise($f); +} + +# dump it out +my $out = join( "\n", @non_gl, "# gitolite start", @gl_keys, "# gitolite end" ) . "\n"; + +my $ak = slurp($akfile); +_die "'$akfile' changed between start and end of this program!" if $ak ne $old_ak; +_print( $akfile, $out ); + +_warn "you have no keys left; I hope you intended to do that!" unless @gl_keys; + +# ---------------------------------------------------------------------- + +sub sanity { + _die "'$glshell' not found; this should NOT happen..." if not -f $glshell; + _die "'$glshell' found but not readable; this should NOT happen..." if not -r $glshell; + _die "'$glshell' found but not executable; this should NOT happen..." if not -x $glshell; + + my $n = " (this is normal on a brand new install)"; + _warn "$akdir missing; creating a new one\n$n" if not -d $akdir; + _warn "$akfile missing; creating a new one\n$n" if not -f $akfile; + + _mkdir( $akdir, 0700 ) if not -d $akdir; + if ( not -f $akfile ) { + _print( $akfile, "" ); + chmod 0600, $akfile; + } +} + +sub auth_options { + my $auth_options = $rc{AUTH_OPTIONS}; + $auth_options ||= "no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty"; + + return $auth_options; +} + +sub fp { + # input: see below + # output: a (list of) FPs + my $in = shift || ''; + if ( $in =~ /\.pub$/ ) { + # single pubkey file + _die "bad pubkey file '$in'" unless $in =~ $REPONAME_PATT; + return fp_file($in); + } elsif ( -f $in ) { + # an authkeys file + return map { fp_line($_) } grep { !/^#/ and /\S/ } slurp($in); + } else { + # one or more actual keys + return map { fp_line($_) } grep { !/^#/ and /\S/ } ( $in, @_ ); + } +} + +sub fp_file { + return $selinux++ if $selinux; # return a unique "fingerprint" to prevent noise + my $f = shift; + my ($fp, $output) = ssh_fingerprint_file($f); + _die "fingerprinting failed for '$f': $output" unless $fp; + return $fp; +} + +sub fp_line { + my $line = shift; + my ($fp, $output) = ssh_fingerprint_line($line); + _die "fingerprinting failed for '$line': $output" unless $fp; + return $fp; +} + +sub optionise { + my $f = shift; + + my $user = $f; + $user =~ s(.*/)(); # foo/bar/baz.pub -> baz.pub + $user =~ s/(\@[^.]+)?\.pub$//; # baz.pub, baz@home.pub -> baz + + my @line = slurp($f); + if ( @line != 1 ) { + _warn "$f does not contain exactly 1 line; ignoring"; + return ''; + } + chomp(@line); + return "command=\"$glshell $user" . ( $kfn ? " $f" : "" ) . "\",$auth_options $line[0]"; +} + diff --git a/src/triggers/post-compile/ssh-authkeys-shell-users b/src/triggers/post-compile/ssh-authkeys-shell-users new file mode 100755 index 0000000..2dd6643 --- /dev/null +++ b/src/triggers/post-compile/ssh-authkeys-shell-users @@ -0,0 +1,51 @@ +#!/usr/bin/perl +use strict; +use warnings; + +use lib $ENV{GL_LIBDIR}; +use Gitolite::Rc; +use Gitolite::Common; + +$|++; + +my $akfile = "$ENV{HOME}/.ssh/authorized_keys"; + +# ---------------------------------------------------------------------- + +my $aktext = slurp($akfile); + +for my $su ( shell_users() ) { + $aktext =~ s(/gitolite-shell $su([" ].*?),no-pty )(/gitolite-shell -s $su$1 )g; +} + +_print( $akfile, $aktext ); + +# two methods to specify list of shell-capable users. (1) list of usernames +# as arguments to 'Shell' in rc file, (2) list of usernames in a plain text +# file whose name is the first argument to 'Shell' in the rc file. Or both! +sub shell_users { + my ($sufile, @ret); + + # backward compat for 3.6 and below. This code will be removed in 3.7. + # Also, the variable is ignored if you end up using the new variant (i.e., + # put a file name on the 'Shell' line itself). + $sufile = $rc{SHELL_USERS_LIST} if $rc{SHELL_USERS_LIST} and -r $rc{SHELL_USERS_LIST}; + + $sufile = shift @ARGV if @ARGV and -r $ARGV[0]; + + if ($sufile) { + @ret = grep { not /^#/ } slurp($sufile); + chomp(@ret); + } + + for my $u (@ARGV) { + # arguments placed in the rc file appear before the trigger name + last if $u eq 'POST_COMPILE'; + + push @ret, $u; + # no sanity checking, since the rc file can only be created by someone + # who already has shell access + } + _die "'Shell': enabled but no usernames supplied" unless @ret; + return @ret; +} diff --git a/src/triggers/post-compile/ssh-authkeys-split b/src/triggers/post-compile/ssh-authkeys-split new file mode 100755 index 0000000..031bd07 --- /dev/null +++ b/src/triggers/post-compile/ssh-authkeys-split @@ -0,0 +1,87 @@ +#!/bin/bash + +# split multi-key files into separate keys like ssh-authkeys likes + +# WHY +# --- +# +# Yeah I wonder that too, when it's so much more maintainable to keep the damn +# keys as sitaram@home.pub and sitaram@work.pub or such. But there's no +# accounting for tastes, and some old fogies apparently want to put all of a +# user's keys into a single ".pub" file. + +# WARNINGS AND CAVEATS +# -------------------- +# +# - assumes no "@" sign in basenames of any multi-key files (single line file +# may still have them) + +# - assumes you don't have a subdir in keydir called "__split_keys__" + +# SUPPORT +# ------- +# +# NONE. + +# USAGE +# ----- +# +# to enable, uncomment the 'ssh-authkeys-split' line in the ENABLE list in the +# rc file. + +cd $GL_ADMIN_BASE/keydir + +rm -rf __split_keys__ +mkdir __split_keys__ +export SKD=$PWD/__split_keys__ + +# if we're coming from a gitolite-admin push, delete all *.multi, and rename +# all multi-line *.pub to *.multi +if [ "$GL_REPO" = "gitolite-admin" ] || [ "$GL_BYPASS_ACCESS_CHECKS" = "1" ] +then + find . -type f -name "*.multi" | while read k + do + rm -f "$k" + done + find . -type f -name "*.pub" | while read k + do + # is this a multi-key? + lines=`wc -l < $k` + case $lines in + (0|1) continue + esac + + base=`basename $k .pub` + mv $k $base.multi + done +fi + +# now process *.multi +find . -type f -name "*.multi" | while read k +do + # do we need to split? + lines=`wc -l < $k` + case $lines in + (0|1) continue + esac + + base=`basename $k .multi` + # sanity check + echo $base | grep '@' >/dev/null && continue + + # ok do it + seq=0 + while read line + do + (( seq++ )) + [ -z "$line" ] && continue + f=$SKD/$base@$seq.pub + echo "$line" > $f + # similar sanity check as main ssh-authkeys script + if ! ssh-keygen -l -f $f >/dev/null + then + echo 1>&2 "ssh-authkeys-split: bad line $seq in keydir/$k" + rm -f $f + fi + done < $k +done |