#!perl
use 5.10.1;
use strict;
use warnings;
use App::authkeymgr;
use File::Copy;
use File::Slurp;
use File::Spec;
use Getopt::Long;
my $opts = {
'version' => sub {
print(
"authkeys-rebuild - App::authkeymgr-",
$App::authkeymgr::VERSION,
"\n",
);
exit 0
},
'help' => sub {
print(
"authkeys-rebuild usage:\n\n",
" --help Display help\n",
" --verbose Display added key paths\n\n",
" --yestoall Run non-interactively\n\n",
" --keydir=PATH\n",
" Path to directory containing .pub keys\n",
" --dest=PATH\n",
" Path to output file\n",
);
exit 0
},
};
GetOptions( $opts,
'help',
'version',
'verbose',
'yestoall',
'keydir=s',
'dest|authkeys=s',
);
$|++;
unless ($opts->{keydir}) {
$ENV{HOME} or die "--keydir not specified and no home directory found\n";
my $default_keydir = File::Spec->catdir($ENV{HOME}, ".ssh", "pubkeys");
die "--keydir not specified and no $default_keydir found\n"
unless -d $default_keydir;
$opts->{keydir} = $default_keydir;
}
until ( -d $opts->{keydir} ) {
unless ($opts->{yestoall}) {
print(
"The specified --keydir is not a directory.\n",
"(Path: $opts->{keydir})\n",
"Specify a valid key directory path, ^C quits.\n",
"> ",
);
my $maybe_keydir = <STDIN>;
chomp($maybe_keydir);
$opts->{keydir} = $maybe_keydir;
} else {
die "--keydir not a directory: $opts->{keydir}\n";
}
}
unless ($opts->{dest}) {
$ENV{HOME} or die "--dest not specified and no home directory found\n";
my $sshdir = File::Spec->catdir($ENV{HOME}, ".ssh");
my $default_dest = File::Spec->catfile($sshdir, "authorized_keys");
if ($opts->{yestoall}) {
print "No --dest specified; defaulting to $default_dest\n";
$opts->{dest} = $default_dest;
} else {
print(
"No destination (--dest) specified.\n",
"Enter a path:\n",
"[$default_dest] ",
);
my $selected_dest = <STDIN>;
chomp($selected_dest);
$opts->{dest} = $selected_dest || $default_dest;
}
}
if (-e $opts->{dest}) {
my $bakpath = $opts->{dest} . ".bak";
print(
"Destination exists: $opts->{dest}\n",
"Copying to: $bakpath\n",
);
copy( $opts->{dest}, $bakpath )
or die "File copy failure: $!";
chmod( 0600, $bakpath ); ## just in case ...
}
my @valid_ext = qw/
pub
pubkey
/;
say "Searching for pubkeys: $opts->{keydir}";
my @found = App::authkeymgr::findkeys($opts->{keydir});
die "No pubkeys found in $opts->{keydir}\n" unless @found;
say "Found ".scalar @found." pubkeys.";
my $outbuf = "## Generated by authkeys-rebuild ##\n";
PUBKEY: for my $thiskey_path (@found) {
my $thiskey_content = read_file($thiskey_path, err_mode => 'carp' )
// next PUBKEY;
$outbuf .= "## $thiskey_path ##\n"
. $thiskey_content
. "\n" ;
say "Added: $thiskey_path" if $opts->{verbose};
}
say "Writing: $opts->{dest}";
write_file($opts->{dest}, $outbuf);
chmod( 0600, $opts->{dest} );
__END__
=pod
=head1 NAME
authkeys-rebuild - build new authorized_keys list
=head1 SYNOPSIS
authkeys-rebuild --help
authkeys-rebuild --keydir=/path/to/pubkeys/
authkeys-rebuild --dest=/path/to/authorized_keys
=head1 DESCRIPTION
Build an authorized_keys file by recursively locating
C<.pub> public key files under a specified directory.
For example:
## Add some users:
mkdir -p ~/.ssh/pubkeys/users
mkdir ~/.ssh/pubkeys/users/joe
cp joe.pub joe-home.pub ~/.ssh/pubkeys/users/joe/
mkdir ~/.ssh/pubkeys/users/bob
cp bob.pub ~/.ssh/pubkeys/users/bob/
## Generate a fresh authorized_keys using defaults:
authkeys-rebuild --yestoall --verbose
## Oops, we need to revoke joe:
rm -r ~/.ssh/pubkeys/users/joe
authkeys-rebuild --yestoall --verbose
B<authkeys-rebuild> is a cheap hack, the result of some discussion on
B<#linode> regarding the lack of an AuthorizedKeysDir sshd_config
directive.
See B<authkeymgr> from this distribution for a complete interactive
approach to managing authorized key sets.
=head1 AUTHOR
Jon Portnoy <avenj@cobaltirc.org>
=cut