summaryrefslogtreecommitdiffstats
path: root/src/triggers/post-compile/ssh-authkeys
diff options
context:
space:
mode:
Diffstat (limited to '')
-rwxr-xr-xsrc/triggers/post-compile/ssh-authkeys142
-rwxr-xr-xsrc/triggers/post-compile/ssh-authkeys-shell-users51
-rwxr-xr-xsrc/triggers/post-compile/ssh-authkeys-split87
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