Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

sshkeys-lint total rewrite, and gl-setup now uses it

...in "admin check" mode
  • Loading branch information...
commit c5f342a835392826c1f7def3491c526521716b42 1 parent ec93fc7
@sitaramc authored
Showing with 153 additions and 84 deletions.
  1. +6 −0 src/gl-setup
  2. +147 −84 src/sshkeys-lint
View
6 src/gl-setup
@@ -136,3 +136,9 @@ gl-compile-conf -q
# now that the admin repo is created, you have to set the hooks properly; best
# do it by running install again
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
View
231 src/sshkeys-lint
@@ -1,109 +1,172 @@
-#!/usr/bin/perl -w
-
+#!/usr/bin/perl
use strict;
-our (%users, %linenos, %pubkeyfiles);
-
-my $thisbin = $0;
-$thisbin = "$ENV{PWD}/$thisbin" unless $thisbin =~ /^\//;
-
-usage() unless $ARGV[0] and -f $ARGV[0];
-my @authlines = filelines($ARGV[0]);
-my $lineno = 0;
-for (@authlines)
-{
- $lineno++;
- my $in_gs = (/^# gitolite start/ .. /^# gitolite end/);
- next if /\# gitolite (start|end)/;
-
- my $user = "";
- $user = "host $1" if /^command=.*gl-mirror-shell (\S+?)"/;
- $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 $key = $1;
- if ($linenos{$key}) {
- warn "authkeys file line $lineno is repeat of line $linenos{$key}, will be ignored by server sshd\n";
- next;
+use warnings;
+
+# complete rewrite of the sshkeys-lint program. Usage has changed, see
+# usage() function or run without arguments.
+
+use Getopt::Long;
+my $admin = 0;
+my $quiet = 0;
+GetOptions('admin|a=s' => \$admin, 'quiet|q' => \$quiet);
+
+use Data::Dumper;
+$Data::Dumper::Deepcopy = 1;
+$|++;
+
+my $in_gl_section = 0;
+my $warnings = 0;
+
+sub dbg {
+ 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";
-
-# all *.pub in current dir should be exactly one line, starting with ssh-rsa
-# or ssh-dss
-
-my @pubkeys = sort glob("*.pub");
-die "no *.pub files here\n" unless @pubkeys;
-for my $pub (@pubkeys) {
- my @lines = grep { ! /^\s*#/ } filelines($pub);
- die "$pub has more than one line\n" if @lines > 1;
- die "$pub does not start with ssh-rsa or ssh-dss\n" unless $lines[0] =~ /^(?:ssh-rsa|ssh-dss) (\S+)/;
- my $key = $1;
- print "$pub seems to be A COPY OF $pubkeyfiles{$key}\n" if $pubkeyfiles{$key};
- $pubkeyfiles{$key} ||= $pub;
- if ($users{$key}) {
- print "$pub $users{$key}\n";
- } else {
- print "$pub has NO ACCESS to the server\n";
- }
+sub msg {
+ my $warning = shift;
+ return if $quiet and not $warning;
+ $warnings++ if $warning;
+ print "sshkeys-lint: " . ( $warning ? "WARNING: " : "" ) . $_ for @_;
+}
+
+@ARGV or not -t or usage();
+our @pubkeyfiles = @ARGV; @ARGV = ();
+
+# ------------------------------------------------------------------------
+
+my @authkeys;
+my %seen_fprints;
+my %pkf_by_fp;
+msg 0, "==== checking authkeys file:\n";
+fill_authkeys(); # uses up STDIN
+
+if ($admin) {
+ my $fp = fprint("$admin.pub");
+ 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";
}
-print <<INFO;
+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);
-Git operations using a pubkey that gets you a command line will BYPASS
-gitolite completely. This means:
+ $authkeys[$seq]{fprint} = $fp;
+ $authkeys[$seq]{ustatus} = $user;
+ }
+}
- - using "git clone git\@server:reponame" will get you the "does not appear to
- be a git repository" message
- - using "git clone git\@server:repositories/reponame" [assuming default value
- of \$REPO_BASE) will work but subsequent push will fail
+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;
-Now you know what pubkey gets you what access.
+ msg 1, "line $seq, $user key found *inside* gitolite section!\n"
+ if $user !~ /^user / and $in_gl_section;
-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 ($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;
+ }
-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.
+ $seen_fprints{$fp}{seq} = $seq;
+ $seen_fprints{$fp}{user} = $user;
+}
-INFO
+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)/;
-sub filelines
-{
- my $f;
- my $fn = shift;
- open ($f, "<", $fn) or die "open $fn failed: $!\n";
- return <$f>;
+ return $user;
}
-sub usage
-{
- print STDERR <<EOF;
+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*#/;
+}
-On your *client*:
+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 {
+ # 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;
+}
- - copy the server's ~/.ssh/authorized_keys file to your *client*'s
- /tmp/foo (maybe using "scp" or whatever)
+# ------------------------------------------------------------------------
+sub usage {
+ print <<EOF;
- - cd to the ~/.ssh directory (which contains all the pub keys this client
- can use)
+sshkeys-lint expects
+ - the contents of an authorized_keys file via STDIN
+ - one or more pubkey filenames as arguments
- - run "$thisbin /tmp/foo"
+sample use to check all keys on gitolite server:
+ 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
-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!
+Note that it runs ssh-keygen -l for each line in the authkeys file and each
+pubkey in the argument list, so be wary of running it on something huge. This
+is meant for troubleshooting.
EOF
-
-exit 1;
+ exit 1;
}
Please sign in to comment.
Something went wrong with that request. Please try again.