1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157
|
#!/usr/bin/perl
# This script automatically runs:
#
# monkeysphere-authentication update-users <user>
#
# every time it detects a change in an authorized_keys or authorized_user_ids
# file. The update-users command operates on the username that owns the file
# that was updated.
#
# The list of files to monitor is generated from the AUTHORIZED_USER_IDS and
# RAW_AUTHORIZED_KEYS variables found in
# /etc/monkeysphere/monkeysphere-authentication.conf and expanded using a list
# of users on the system.
#
# Additionally, the /var/lib/monkeysphere/user-update/lastchange file is
# monitored. If a change is made to that file, the list of files to monitor is
# re-generated based on a fresh listing of users. If you run a hook on user
# creation and deletion that generates a file in this directory, you can ensure
# that the list of files to monitor is always up-to-date.
#
# On debian system you can install required perl modules with: aptitude install
# libfile-changenotify-perl libfile-spec-perl libconfig-general-perl
#
# This script is designed to run at system start and should be run with root
# privileges.
#
# File::ChangeNotify is cross platform - it will choose a sub class for
# monitoring file system changes appropriate to your operating system (if you
# are running Linux, liblinux-inotify2-perl is recommended).
# FIXME: does this handle revocations and re-keying? if a sysadmin
# switches over to this arrangement, how will the system check for
# revocations? Scheduling a simple gpg --refresh should handle
# revocations. I'm not sure how to best handle re-keyings.
use strict;
use warnings;
use File::ChangeNotify;
use File::Basename;
use File::Spec;
use Config::General;
my $user_update_file = '/var/lib/monkeysphere/user-update/lastchange';
my %watch_files;
my $debug = 0;
if (defined($ENV{MONKEYSPHERE_LOG_LEVEL}) &&
$ENV{MONKEYSPHERE_LOG_LEVEL} =~ /^debug/i) {
$debug = 1;
}
sub debug {
printf STDERR @_
if ($debug eq 1);
}
sub set_watch_files() {
my %key_file_locations = get_key_file_locations();
# get list of users on the system
while(my ($name, $passwd, $uid, $gid, $gcos, $dir, $shell, $home) = getpwent()) {
while (my ($key, $file) = each (%key_file_locations)) {
$file =~ s/%h/$home/;
$file =~ s/%u/$name/;
$watch_files{ $file } = $name;
}
}
endpwent();
$watch_files{ $user_update_file } = '';
}
sub get_key_file_locations {
# set defaults
my %key_file_locations;
$key_file_locations{ 'authorized_user_ids' } = '%h/.monkeysphere/authorized_user_ids';
$key_file_locations{ 'authorized_keys' } = '%h/.ssh/authorized_keys';
# check monkeysphere-authentication configuration
my $config_file = '/etc/monkeysphere/monkeysphere-authentication.conf';
if (-f $config_file) {
if (-r $config_file) {
my %config;
%config = Config::General::ParseConfig($config_file);
if (exists $config{'AUTHORIZED_USER_IDS'}) {
$key_file_locations{'authorized_user_ids'} = $config{'AUTHORIZED_USER_IDS'};
}
if (exists $config{'RAW_AUTHORIZED_KEYS'}) {
$key_file_locations{'authorized_keys'} = $config{'RAW_AUTHORIZED_KEYS'};
}
}
}
return %key_file_locations;
}
sub get_watcher {
my @filters;
my @dirs;
set_watch_files();
for my $file (%watch_files) {
my $dir = dirname($file);
if ( -d $dir && !grep $_ eq $dir, @dirs ) {
debug("Watching dir: %s\n", $dir);
push(@dirs,$dir);
my $file = basename($file);
if ( !grep $_ eq $file, @filters ) {
$file = quotemeta($file);
debug("Adding file filter: %s\n", $file);
push(@filters,$file);
}
}
}
# create combined file filters to limit our monitor
my $filter = '^(' . join("|",@filters) . ')$';
# return a watcher object
return my $watcher =
File::ChangeNotify->instantiate_watcher
( directories => [ @dirs ],
filter => qr/$filter/,
);
}
sub watch {
my $watcher = get_watcher();
while ( my @events = $watcher->wait_for_events() ) {
my %users;
for my $event (@events) {
if($event->path eq "$user_update_file") {
debug("Reloading user list\n");
$watcher = get_watcher();
} else {
# if user deleted, file might not exist
# FIXME - m-a u returns an error if the username
# doesn't exist. It should silently ensure that
# the generated authorized_keys file is deleted.
# Once it's fixed, we should execute even if the
# file is gone.
if( -f $event->path) {
my $username = $watch_files { $event->path };
$users{ $username } = 1;
}
}
}
for ((my $username) = each(%users)) {
debug("Updating user: %s\n", $username);
# FIXME: this call blocks until m-a u finishes running, i think.
# what happens if other changes occur in the meantime? Can we
# rate-limit this? Could we instead spawn child processes that
# run this command directly?
system('monkeysphere-authentication', 'update-users', $username);
}
}
}
watch();
|