feat: add osh-cleanup-guest-key-access.pl script

This script removes system-level access to group keys to old guests
of groups that no longer have any active access to servers of that group.
This only happens when the last access to be removed from them had a TTL.
This commit is contained in:
Stéphane Lesimple 2022-01-21 16:49:40 +00:00 committed by Stéphane Lesimple
parent f43fdaaf82
commit e71aa7b975
4 changed files with 183 additions and 1 deletions

View file

@ -0,0 +1,158 @@
#! /usr/bin/env perl
# vim: set filetype=perl ts=4 sw=4 sts=4 et:
use common::sense;
use Getopt::Long;
use File::Basename;
use lib dirname(__FILE__) . '/../../lib/perl';
use OVH::Bastion;
use OVH::Result;
use OVH::SimpleLog;
# this'll be used in syslog
$ENV{'UNIQID'} = OVH::Bastion::generate_uniq_id()->value;
my $fnret;
# abort early if we're not a master instance
if (OVH::Bastion::config('readOnlySlaveMode')->value) {
_log "We're not a master instance, don't do anything";
exit 0;
}
$fnret = OVH::Bastion::load_configuration_file(
file => OVH::Bastion::main_configuration_directory() . "/osh-cleanup-guest-access.conf",
secure => 1,
keywords => [qw{ SyslogFacility }],
);
my $config;
if (!$fnret) {
if (-e OVH::Bastion::main_configuration_directory() . "/osh-cleanup-guest-access.conf") {
_warn "Error while loading configuration, continuing anyway with default values...";
}
else {
_log "No configuration file found, using default config values...";
}
}
else {
$config = $fnret->value;
if (ref $config ne 'HASH') {
_warn "Invalid data returned while loading configuration, continuing anyway with default values...";
}
}
# set default values
$config = {} if ref $config ne 'HASH';
$config->{'SyslogFacility'} //= 'local6';
# logging
if ($config->{'SyslogFacility'}) {
OVH::SimpleLog::setSyslog($config->{'SyslogFacility'});
}
# command-line
sub print_usage {
print <<"EOF";
$0 [options]
--dry-run Don't actually do anything, just report what would be done
--verbose More detailed logging
EOF
return 1;
}
my ($dryRun, $verbose);
{
my $optwarn = 'Unknown error';
local $SIG{'__WARN__'} = sub { $optwarn = shift; };
if (
!GetOptions(
"dry-run" => \$dryRun,
"verbose+" => \$verbose,
)
)
{
_err "Error while parsing command-line options: $optwarn";
print_usage();
exit 1;
}
}
_log "Looking for group guests that no longer have any access to any server of the group...";
$fnret = OVH::Bastion::get_group_list(groupType => "key");
if (!$fnret) {
_err "Couldn't get group list:" . $fnret->msg;
exit 1;
}
my $groups = $fnret->value;
foreach my $shortGroup (sort keys %$groups) {
foreach my $account (@{$groups->{$shortGroup}{'members'}}) {
next if ($account eq 'allowkeeper'); # don't need to check this special user
_log "<$shortGroup/$account> checking if group guest..." if $verbose;
# rule out realm accounts, we would need to check every remote account's info
next if ($account =~ /^realm_/);
# the "members" of the system group key$shortGroup might be either members or guests,
# so we first rule out members
next if OVH::Bastion::is_group_member(account => $account, group => $shortGroup);
# it seems to be a guest, double-check that
next if !OVH::Bastion::is_group_guest(account => $account, group => $shortGroup);
_log "<$shortGroup/$account> found a guest, checking remaining accesses..." if $verbose;
# okay, any access remaining?
$fnret = OVH::Bastion::get_acl_way(way => 'groupguest', group => $shortGroup, account => $account);
if (!$fnret) {
_warn "<$shortGroup/$account> Error getting guest accesses ($fnret), skipping";
next;
}
elsif ($fnret->err eq 'OK') {
_log "<$shortGroup/$account> The account still has " . (@{$fnret->value}) . " accesses to the group, skipping" if $verbose;
next;
}
elsif ($fnret->err eq 'OK_EMPTY' && !@{$fnret->value}) {
# this is a guest, but no ACL remains (probably the last one had a TTL),
# so we'll cleanup this guest
if ($dryRun) {
_log "<$shortGroup/$account> The account is a guest of group but has no remaining access, would have cleaned up in non-dry-run mode";
next;
}
_log "<$shortGroup/$account> The account is a guest of group but has no remaining access, cleaning up...";
# get $group from $shortGroup
$fnret = OVH::Bastion::is_valid_group_and_existing(group => $shortGroup, groupType => 'key');
if (!$fnret) {
_warn "<$shortGroup/$account> Group seems invalid ($fnret), skipping";
next;
}
my $group = $fnret->value->{'group'};
# remove account from group
my @command = qw{ /usr/bin/env perl -T };
push @command, $OVH::Bastion::BASEPATH . '/bin/helper/osh-groupSetRole';
push @command, '--type', 'guest';
push @command, '--group', $group;
push @command, '--account', $account;
push @command, '--action', 'del';
$fnret = OVH::Bastion::helper(cmd => \@command);
if (!$fnret) {
_err "<$shortGroup/$account> Failed to revoke key access: $fnret";
}
else {
_log "<$shortGroup/$account> Key access revoked";
}
}
}
}
_log "Done, got " . (OVH::SimpleLog::nb_errors()) . " error(s) and " . (OVH::SimpleLog::nb_warnings()) . " warning(s).";

View file

@ -0,0 +1,16 @@
###############################################################################
## Config for /opt/bastion/bin/cron/osh-cleanup-guest-key-access.pl, the script
## responsible for cleaning up dangling accesses to group keys to group guests
## that no longer have access to any server of the group (happens when the last
## access expired with a TTL).
## This is a JSON file.
###############################################################################
{
# > Logging
# >> These options configure the way the script logs its actions
#
# SyslogFacility (string)
# DESC: The syslog facility to use for logging the script output. If set to the empty string, we'll not log through syslog at all. If this configuration option is missing from your config file altogether, the default value will be used (local6), which means that we'll log to syslog.
# DEFAULT: local6
"SyslogFacility": "local6"
}

View file

@ -0,0 +1,2 @@
# Check each 5 minutes that we don't have dangling guests still having access to keys
*/5 * * * * root /opt/bastion/bin/cron/osh-cleanup-guest-key-access.pl >/dev/null

View file

@ -73,6 +73,13 @@ testsuite_scripts()
nocontain "ERROR:"
nocontain "Unexpected termination"
# cleanup guest key access
success cleanup_guest_key_access $r0 /opt/bastion/bin/cron/osh-cleanup-guest-key-access.pl
contain "Done"
nocontain "WARN:"
nocontain "ERROR:"
# encrypt rsync (nothing to encrypt)
success encrypt_rsync_none $r0 /opt/bastion/bin/cron/osh-encrypt-rsync.pl
@ -80,7 +87,6 @@ testsuite_scripts()
contain "Done"
nocontain "WARN:"
nocontain "ERROR:"
nocontain "Unexpected termination"
# ttyrec subfolders cleanup
success ttyrec_cleanup $r0 /opt/bastion/bin/cron/osh-remove-empty-folders.sh