sshkeys-lint total rewrite, and gl-setup now uses it
...in "admin check" mode
This commit is contained in:
parent
ec93fc7601
commit
c5f342a835
|
@ -136,3 +136,9 @@ gl-compile-conf -q
|
||||||
# now that the admin repo is created, you have to set the hooks properly; best
|
# now that the admin repo is created, you have to set the hooks properly; best
|
||||||
# do it by running install again
|
# do it by running install again
|
||||||
gl-install -q
|
gl-install -q
|
||||||
|
|
||||||
|
# ----
|
||||||
|
|
||||||
|
# the never-ending quest to help with bloody ssh issues...
|
||||||
|
cd $GL_ADMINDIR/keydir
|
||||||
|
[ -n "$pubkey_file" ] && $GL_BINDIR/sshkeys-lint < $HOME/.ssh/authorized_keys -q -a $admin_name
|
||||||
|
|
239
src/sshkeys-lint
239
src/sshkeys-lint
|
@ -1,109 +1,172 @@
|
||||||
#!/usr/bin/perl -w
|
#!/usr/bin/perl
|
||||||
|
|
||||||
use strict;
|
use strict;
|
||||||
our (%users, %linenos, %pubkeyfiles);
|
use warnings;
|
||||||
|
|
||||||
my $thisbin = $0;
|
# complete rewrite of the sshkeys-lint program. Usage has changed, see
|
||||||
$thisbin = "$ENV{PWD}/$thisbin" unless $thisbin =~ /^\//;
|
# usage() function or run without arguments.
|
||||||
|
|
||||||
usage() unless $ARGV[0] and -f $ARGV[0];
|
use Getopt::Long;
|
||||||
my @authlines = filelines($ARGV[0]);
|
my $admin = 0;
|
||||||
my $lineno = 0;
|
my $quiet = 0;
|
||||||
for (@authlines)
|
GetOptions('admin|a=s' => \$admin, 'quiet|q' => \$quiet);
|
||||||
{
|
|
||||||
$lineno++;
|
|
||||||
my $in_gs = (/^# gitolite start/ .. /^# gitolite end/);
|
|
||||||
next if /\# gitolite (start|end)/;
|
|
||||||
|
|
||||||
my $user = "";
|
use Data::Dumper;
|
||||||
$user = "host $1" if /^command=.*gl-mirror-shell (\S+?)"/;
|
$Data::Dumper::Deepcopy = 1;
|
||||||
$user = "user $1" if /^command=.*gl-auth-command (\S+?)"/;
|
$|++;
|
||||||
$user = "shell user $1" if /^command=.*gl-auth-command -s (\S+?)"/;
|
|
||||||
|
|
||||||
die "line $lineno: unrecognised line\n" unless /^(?:command=".*(?:gl-mirror-shell|gl-auth-command(?: -s)?) (?:\S+?)"\S+ )?(?:ssh-rsa|ssh-dss) (\S+)/;
|
my $in_gl_section = 0;
|
||||||
my $key = $1;
|
my $warnings = 0;
|
||||||
if ($linenos{$key}) {
|
|
||||||
warn "authkeys file line $lineno is repeat of line $linenos{$key}, will be ignored by server sshd\n";
|
sub dbg {
|
||||||
next;
|
use Data::Dumper;
|
||||||
|
for my $i (@_) {
|
||||||
|
print STDERR "DBG: " . Dumper($i);
|
||||||
}
|
}
|
||||||
$linenos{$key} = $lineno;
|
|
||||||
$users{$key} = ($user ? "maps to $user" : "gets you a command line");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
print "\n";
|
sub msg {
|
||||||
|
my $warning = shift;
|
||||||
|
return if $quiet and not $warning;
|
||||||
|
$warnings++ if $warning;
|
||||||
|
print "sshkeys-lint: " . ( $warning ? "WARNING: " : "" ) . $_ for @_;
|
||||||
|
}
|
||||||
|
|
||||||
# all *.pub in current dir should be exactly one line, starting with ssh-rsa
|
@ARGV or not -t or usage();
|
||||||
# or ssh-dss
|
our @pubkeyfiles = @ARGV; @ARGV = ();
|
||||||
|
|
||||||
my @pubkeys = sort glob("*.pub");
|
# ------------------------------------------------------------------------
|
||||||
die "no *.pub files here\n" unless @pubkeys;
|
|
||||||
for my $pub (@pubkeys) {
|
my @authkeys;
|
||||||
my @lines = grep { ! /^\s*#/ } filelines($pub);
|
my %seen_fprints;
|
||||||
die "$pub has more than one line\n" if @lines > 1;
|
my %pkf_by_fp;
|
||||||
die "$pub does not start with ssh-rsa or ssh-dss\n" unless $lines[0] =~ /^(?:ssh-rsa|ssh-dss) (\S+)/;
|
msg 0, "==== checking authkeys file:\n";
|
||||||
my $key = $1;
|
fill_authkeys(); # uses up STDIN
|
||||||
print "$pub seems to be A COPY OF $pubkeyfiles{$key}\n" if $pubkeyfiles{$key};
|
|
||||||
$pubkeyfiles{$key} ||= $pub;
|
if ($admin) {
|
||||||
if ($users{$key}) {
|
my $fp = fprint("$admin.pub");
|
||||||
print "$pub $users{$key}\n";
|
my $fpu = ( $seen_fprints{$fp}{user} || 'no access' );
|
||||||
|
# dbg("fpu = $fpu, admin=$admin");
|
||||||
|
die "\t\t*** FATAL ***\n" .
|
||||||
|
"$admin.pub maps to $fpu, not $admin.\n" .
|
||||||
|
"You will not be able to access gitolite with this key.\n" .
|
||||||
|
"Look for the 'ssh troubleshooting' link in http://sitaramc.github.com/gitolite/.\n"
|
||||||
|
if $fpu ne "user $admin";
|
||||||
|
}
|
||||||
|
|
||||||
|
msg 0, "==== checking pubkeys:\n" if @pubkeyfiles;
|
||||||
|
for my $pkf (@pubkeyfiles) {
|
||||||
|
my $fp = fprint($pkf);
|
||||||
|
msg 1, "$pkf appears to be a COPY of $pkf_by_fp{$fp}\n" if $pkf_by_fp{$fp};
|
||||||
|
$pkf_by_fp{$fp} ||= $pkf;
|
||||||
|
my $fpu = ( $seen_fprints{$fp}{user} || 'no access' );
|
||||||
|
msg 0, "$pkf maps to $fpu\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($warnings) {
|
||||||
|
print "\n$warnings warnings found\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
exit $warnings;
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------------
|
||||||
|
sub fill_authkeys {
|
||||||
|
while (<>) {
|
||||||
|
my $seq = $.;
|
||||||
|
next if ak_comment($_); # also sets/clears $in_gl_section global
|
||||||
|
my $fp = fprint($_);
|
||||||
|
my $user = user($_);
|
||||||
|
|
||||||
|
check($seq, $fp, $user);
|
||||||
|
|
||||||
|
$authkeys[$seq]{fprint} = $fp;
|
||||||
|
$authkeys[$seq]{ustatus} = $user;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub check {
|
||||||
|
my ($seq, $fp, $user) = @_;
|
||||||
|
|
||||||
|
msg 1, "line $seq, $user key found *outside* gitolite section!\n"
|
||||||
|
if $user =~ /^user / and not $in_gl_section;
|
||||||
|
|
||||||
|
msg 1, "line $seq, $user key found *inside* gitolite section!\n"
|
||||||
|
if $user !~ /^user / and $in_gl_section;
|
||||||
|
|
||||||
|
if ($seen_fprints{$fp}) {
|
||||||
|
msg 1, "authkeys line $seq ($user) will be ignored by sshd; " .
|
||||||
|
"same key found on line " .
|
||||||
|
$seen_fprints{$fp}{seq} . " (" .
|
||||||
|
$seen_fprints{$fp}{user} . ")\n";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$seen_fprints{$fp}{seq} = $seq;
|
||||||
|
$seen_fprints{$fp}{user} = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub user {
|
||||||
|
my $user = '';
|
||||||
|
$user ||= "user $1" if /^command=.*gl-auth-command (.*?)"/;
|
||||||
|
$user ||= "host $1" if /^command=.*gl-mirror-shell (.*?)"/;
|
||||||
|
$user ||= "unknown command" if /^command/;
|
||||||
|
$user ||= "shell access" if /^ssh-(rsa|dss)/;
|
||||||
|
|
||||||
|
return $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub ak_comment {
|
||||||
|
my $_ = shift;
|
||||||
|
$in_gl_section = 1 if /^# gitolite start/;
|
||||||
|
$in_gl_section = 0 if /^# gitolite end/;
|
||||||
|
die "gitosis? what's that?\n" if /^#.*gitosis/;
|
||||||
|
return /^\s*#/;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub fprint {
|
||||||
|
my $_ = shift;
|
||||||
|
my ($fh, $tempfn, $in);
|
||||||
|
if (/ssh-(dss|rsa) /) {
|
||||||
|
# an actual key was passed. Since ssh-keygen requires an actual file,
|
||||||
|
# make a temp file to take the data and pass on to ssh-keygen
|
||||||
|
s/^.* (ssh-dss|ssh-rsa)/$1/;
|
||||||
|
use File::Temp qw(tempfile);
|
||||||
|
($fh, $tempfn) = tempfile();
|
||||||
|
$in = $tempfn;
|
||||||
|
print $fh $_;
|
||||||
|
close $fh;
|
||||||
} else {
|
} else {
|
||||||
print "$pub has NO ACCESS to the server\n";
|
# a filename was passed
|
||||||
|
$in = $_;
|
||||||
}
|
}
|
||||||
|
# dbg("in = $in");
|
||||||
|
-f $in or die "file not found: $in\n";
|
||||||
|
open($fh, "ssh-keygen -l -f $in |") or die "could not fork: $!\n";
|
||||||
|
my $fp = <$fh>;
|
||||||
|
# dbg("fp = $fp");
|
||||||
|
close $fh;
|
||||||
|
unlink $tempfn if $tempfn;
|
||||||
|
die "fprint failed\n" unless $fp =~ /([0-9a-f][0-9a-f](:[0-9a-f][0-9a-f])+)/;
|
||||||
|
return $1;
|
||||||
}
|
}
|
||||||
|
|
||||||
print <<INFO;
|
# ------------------------------------------------------------------------
|
||||||
|
sub usage {
|
||||||
|
print <<EOF;
|
||||||
|
|
||||||
====
|
sshkeys-lint expects
|
||||||
|
- the contents of an authorized_keys file via STDIN
|
||||||
|
- one or more pubkey filenames as arguments
|
||||||
|
|
||||||
Git operations using a pubkey that gets you a command line will BYPASS
|
sample use to check all keys on gitolite server:
|
||||||
gitolite completely. This means:
|
cd ~/.gitolite/keydir
|
||||||
|
cat ~/.ssh/authorized_keys | sshkeys-lint `find . -name "*.pub"`
|
||||||
|
# or supply only one pubkey file to check only that:
|
||||||
|
cat ~/.ssh/authorized_keys | sshkeys-lint YourName.pub
|
||||||
|
|
||||||
- using "git clone git\@server:reponame" will get you the "does not appear to
|
Note that it runs ssh-keygen -l for each line in the authkeys file and each
|
||||||
be a git repository" message
|
pubkey in the argument list, so be wary of running it on something huge. This
|
||||||
- using "git clone git\@server:repositories/reponame" [assuming default value
|
is meant for troubleshooting.
|
||||||
of \$REPO_BASE) will work but subsequent push will fail
|
|
||||||
|
|
||||||
----
|
|
||||||
|
|
||||||
Now you know what pubkey gets you what access.
|
|
||||||
|
|
||||||
To see what key is *actually* being used when you run your commands, try "ssh
|
|
||||||
-v git\@server" or "ssh -v gitolite", and look for a line saying "Offering
|
|
||||||
public key". If there are more than one such lines, the last one is what
|
|
||||||
counts.
|
|
||||||
|
|
||||||
If at any time you are asked for a password (password, not passphrase; see
|
|
||||||
doc/6 for the difference, if needed), then none of this applies anyway.
|
|
||||||
|
|
||||||
INFO
|
|
||||||
|
|
||||||
sub filelines
|
|
||||||
{
|
|
||||||
my $f;
|
|
||||||
my $fn = shift;
|
|
||||||
open ($f, "<", $fn) or die "open $fn failed: $!\n";
|
|
||||||
return <$f>;
|
|
||||||
}
|
|
||||||
|
|
||||||
sub usage
|
|
||||||
{
|
|
||||||
print STDERR <<EOF;
|
|
||||||
|
|
||||||
On your *client*:
|
|
||||||
|
|
||||||
- copy the server's ~/.ssh/authorized_keys file to your *client*'s
|
|
||||||
/tmp/foo (maybe using "scp" or whatever)
|
|
||||||
|
|
||||||
- cd to the ~/.ssh directory (which contains all the pub keys this client
|
|
||||||
can use)
|
|
||||||
|
|
||||||
- run "$thisbin /tmp/foo"
|
|
||||||
|
|
||||||
Note: people who have so many keypairs they keep them in *sub*-directories of
|
|
||||||
~/.ssh [you know who you are ;-)] can figure it out themselves; you clearly
|
|
||||||
know enough about ssh not to need my help!
|
|
||||||
|
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
exit 1;
|
exit 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue