From ca9b8d2ac9cb21d9a175bf18b95696ba01485a87 Mon Sep 17 00:00:00 2001 From: Jamie McClelland Date: Sun, 19 Dec 2010 00:01:19 -0500 Subject: first attempt at closing #499 (changes to user authorized_keys files not immediately incorporated) --- examples/monkeysphere-monitor-keys | 102 +++++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 examples/monkeysphere-monitor-keys diff --git a/examples/monkeysphere-monitor-keys b/examples/monkeysphere-monitor-keys new file mode 100644 index 0000000..d121828 --- /dev/null +++ b/examples/monkeysphere-monitor-keys @@ -0,0 +1,102 @@ +#!/bin/perl + +# This script runs in a loop, monitoring existing ~/.ssh and +# ~/.monkeysphere directories for all users on the system. It's +# designed to be started at system start time and should run +# as root. +# +# Optionally, pass the path to a file that is changed every +# time a user is added and this script will re-load the +# list of directories to monitor to include new additions. +# +# If liblinux-inotify2-perl is installed, then the Linux +# inotify method is used to determine if a file has changed. +# Otherwise, a generic/cross platform library is used. +# +# Known bugs: with inotify, if you pass /etc/passwd as the +# first argument, the script will not detect changes to the +# file when a new user is added. + +use strict; +use File::ChangeNotify; +use File::Basename; + +sub get_home_dirs() { + my @home_dirs; + my @subdirs = ('.monkeysphere', '.ssh'); + # get list of users on the system + while((my $name,my $passwd,my $uid,my $gid,my $gcos,my $dir,my $shell,my $home) = getpwent( )){ + # only monitor regular users + if ( $uid >= 1000 ) { + # The default watcher complains about non-existing directories + # so you should include .monkeysphere and .ssh in /etc/skel + # if you want them monitored for all users. + for my $subdir (@subdirs) { + if ( -d "$home/$subdir" ) { + push(@home_dirs,"$home/$subdir"); + } + } + } + } + endpwent(); + return @home_dirs +} + +sub get_watcher { + my($rescan_file) = $_[0]; + my(@dirs) = get_home_dirs(); + my @filters = ('authorized_keys', 'authorized_user_ids'); + + # if we have a rescan file that indicates when new users are added + # then monitor that file as well (could be /etc/passwd) + if ( -f "$rescan_file" ) { + push(@dirs,dirname($rescan_file)); + push(@filters,basename($rescan_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 = $_[0]; + my $rescan_file = $_[1]; + + while ( my @events = $watcher->wait_for_events() ) { + my $rescan = 0; + my @users; + for my $event (@events) { + if($event->path eq "$rescan_file") { + $rescan = 1; + } else { + # if user deleted, file might not exist + if( -f $event->path) { + my $username = getpwuid((stat($event->path))[4]); + push(@users,$username); + } + } + } + for my $user (@users) { + my @args = ('u',$user); + system 'monkeysphere-authentication', @args; + } + # rescan users if necessary + if($rescan) { + # return to loop so we can be re-run after re-reading the + # user list + return; + } + } +} + +my $rescan_file = $ARGV[0]; +while(1) { + my $watcher = get_watcher($rescan_file); + watch($watcher, $rescan_file); +} -- cgit v1.2.3 From 8ea1266d0dfd68a7f4b72d441226a96eaef35748 Mon Sep 17 00:00:00 2001 From: Jamie McClelland Date: Mon, 20 Dec 2010 09:26:51 -0500 Subject: more precise description of file::ChangeNotify's behavior with regard to different operating systems. And, monitoring /etc/passwd is not recommended, so removed from description. --- examples/monkeysphere-monitor-keys | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/examples/monkeysphere-monitor-keys b/examples/monkeysphere-monitor-keys index d121828..78df88e 100644 --- a/examples/monkeysphere-monitor-keys +++ b/examples/monkeysphere-monitor-keys @@ -5,17 +5,10 @@ # designed to be started at system start time and should run # as root. # -# Optionally, pass the path to a file that is changed every -# time a user is added and this script will re-load the -# list of directories to monitor to include new additions. -# -# If liblinux-inotify2-perl is installed, then the Linux -# inotify method is used to determine if a file has changed. -# Otherwise, a generic/cross platform library is used. -# -# Known bugs: with inotify, if you pass /etc/passwd as the -# first argument, the script will not detect changes to the -# file when a new user is added. +# File::ChangeNotify is cross platform - it will choose an +# sub class for monitoring file system changes appropriate to +# your operating system (if you are running Linux, +# liblinux-inotify2-perl is recommended). use strict; use File::ChangeNotify; -- cgit v1.2.3 From 617031b0585dacb75ced234e04984ca6b4b0568c Mon Sep 17 00:00:00 2001 From: Jamie McClelland Date: Mon, 20 Dec 2010 12:24:54 -0500 Subject: Comments at top now contains more concrete explanation of how the script works. Location of key files to monitor is more configurable by the sys admin. All changed files treated the same for simplicity. Added debug mode. --- examples/monkeysphere-monitor-keys | 142 ++++++++++++++++++++++++------------- 1 file changed, 93 insertions(+), 49 deletions(-) diff --git a/examples/monkeysphere-monitor-keys b/examples/monkeysphere-monitor-keys index 78df88e..8a95118 100644 --- a/examples/monkeysphere-monitor-keys +++ b/examples/monkeysphere-monitor-keys @@ -1,50 +1,102 @@ -#!/bin/perl +#!/usr/bin/perl -# This script runs in a loop, monitoring existing ~/.ssh and -# ~/.monkeysphere directories for all users on the system. It's -# designed to be started at system start time and should run -# as root. +# This script automatically runs: # -# File::ChangeNotify is cross platform - it will choose an -# sub class for monitoring file system changes appropriate to -# your operating system (if you are running Linux, -# liblinux-inotify2-perl is recommended). +# monkeysphere-authentication update-users +# +# 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). use strict; use File::ChangeNotify; use File::Basename; +use File::Spec; +use Config::General; -sub get_home_dirs() { - my @home_dirs; - my @subdirs = ('.monkeysphere', '.ssh'); +my $user_update_file = '/var/lib/monkeysphere/user-update/lastchange'; +my $debug = 0; + +sub debug { + if ($debug eq 1) { print $_[0]; } +} + +sub get_watch_files() { + my @watch_files; + my %key_file_locations = get_key_file_locations(); # get list of users on the system while((my $name,my $passwd,my $uid,my $gid,my $gcos,my $dir,my $shell,my $home) = getpwent( )){ - # only monitor regular users - if ( $uid >= 1000 ) { - # The default watcher complains about non-existing directories - # so you should include .monkeysphere and .ssh in /etc/skel - # if you want them monitored for all users. - for my $subdir (@subdirs) { - if ( -d "$home/$subdir" ) { - push(@home_dirs,"$home/$subdir"); - } - } + while (my ($key, $file) = each (%key_file_locations)) { + $file =~ s/%h/$home/; + $file =~ s/%u/$name/; + push(@watch_files,$file); } } endpwent(); - return @home_dirs + push(@watch_files,$user_update_file); + return @watch_files; +} + +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($rescan_file) = $_[0]; - my(@dirs) = get_home_dirs(); - my @filters = ('authorized_keys', 'authorized_user_ids'); + my @filters; + my @dirs; - # if we have a rescan file that indicates when new users are added - # then monitor that file as well (could be /etc/passwd) - if ( -f "$rescan_file" ) { - push(@dirs,dirname($rescan_file)); - push(@filters,basename($rescan_file)); + my(@files) = get_watch_files(); + for my $file (@files) { + my $dir = dirname($file); + if ( -d $dir && !grep $_ eq $dir, @dirs ) { + debug "Watching dir: $dir\n"; + push(@dirs,$dir); + my $file = basename($file); + if ( !grep $_ eq $file, @filters ) { + debug "Adding file filter: $file\n"; + push(@filters,$file); + } + } } # create combined file filters to limit our monitor my $filter = '^(' . join("|",@filters) . ')$'; @@ -58,38 +110,30 @@ sub get_watcher { } sub watch { - my $watcher = $_[0]; - my $rescan_file = $_[1]; - + my $watcher = get_watcher(); while ( my @events = $watcher->wait_for_events() ) { - my $rescan = 0; my @users; for my $event (@events) { - if($event->path eq "$rescan_file") { - $rescan = 1; + if($event->path eq "$user_update_file") { + debug "Reloading user list"; + $watcher = get_watcher(); } else { # if user deleted, file might not exist if( -f $event->path) { my $username = getpwuid((stat($event->path))[4]); - push(@users,$username); + + if ( !grep $_ eq $username, @users ) { + push(@users,$username); + } } } } for my $user (@users) { my @args = ('u',$user); + debug "Updating user: $user"; system 'monkeysphere-authentication', @args; } - # rescan users if necessary - if($rescan) { - # return to loop so we can be re-run after re-reading the - # user list - return; - } } } -my $rescan_file = $ARGV[0]; -while(1) { - my $watcher = get_watcher($rescan_file); - watch($watcher, $rescan_file); -} +watch(); -- cgit v1.2.3