* Add flag ignore_errors to allow uninterrupted fragment deployment

* Other minor updates
This commit is contained in:
Patrick Van der Veken 2025-04-27 09:08:14 +02:00
parent 813315bc4f
commit 78d2d6aff1
4 changed files with 56 additions and 26 deletions

View File

@ -2,9 +2,8 @@
## What's new
:loudspeaker: **30/12/2020**:
* added support for SELinux (CentOS/RHEL 8.x)
* various fixes
:loudspeaker: **27/04/2025**:
* added the `ignore_errors` flag to allow uninterrupted deployment of fragements.
## About

View File

@ -2,7 +2,7 @@
#******************************************************************************
# @(#) manage_sudo.sh
#******************************************************************************
# @(#) Copyright (C) 2014 by KUDOS BVBA (info@kudos.be). All rights reserved.
# @(#) Copyright (C) 2014 by KUDOS BV (info@kudos.be). All rights reserved.
#
# This program is a free software; you can redistribute it and/or modify
# it under the same terms of the GNU General Public License as published by
@ -42,7 +42,7 @@
# or LOCAL_CONFIG_FILE instead
# define the version (YYYY-MM-DD)
typeset -r SCRIPT_VERSION="2021-06-17"
typeset -r SCRIPT_VERSION="2025-04-27"
# name of the global configuration file (script)
typeset -r GLOBAL_CONFIG_FILE="manage_sudo.conf"
# name of the local configuration file (script)
@ -585,7 +585,7 @@ function display_usage
cat << EOT
**** ${SCRIPT_NAME} ****
**** (c) KUDOS BVBA - Patrick Van der Veken ****
**** (c) KUDOS BV - Patrick Van der Veken ****
Performs basic functions for SUDO controls: update SUDOers files locally or
remote, validate SUDO syntax or copy/distribute the SUDO controls files

View File

@ -12,6 +12,9 @@
# use short hostnames or FQDN (0=short names; 1=FQDN) [default: 0]
use_fqdn=1
# ignore errors during fragment deployment (0=no; 1=yes [default: 0])
ignore_errors=0
# target directory for sudo fragment files
fragments_dir=/etc/sudo_controls/sudoers.d

View File

@ -2,7 +2,7 @@
#******************************************************************************
# @(#) update_sudo.pl
#******************************************************************************
# @(#) Copyright (C) 2014 by KUDOS BVBA <info@kudos.be>. All rights reserved.
# @(#) Copyright (C) 2014 by KUDOS BV <info@kudos.be>. All rights reserved.
#
# This program is a free software; you can redistribute it and/or modify
# it under the same terms of the GNU General Public License as published by
@ -44,7 +44,7 @@ use File::Temp qw(tempfile);
# ------------------------- CONFIGURATION starts here -------------------------
# define the version (YYYY-MM-DD)
my $script_version = "2020-12-30";
my $script_version = "2025-04-27";
# name of global configuration file (no path, must be located in the script directory)
my $global_config_file = "update_sudo.conf";
# name of localized configuration file (no path, must be located in the script directory)
@ -55,7 +55,7 @@ my $max_recursion = 5;
my $selinux_context = "etc_t";
# ------------------------- CONFIGURATION ends here ---------------------------
# initialize variables
my ($debug, $verbose, $preview, $global, $use_fqdn) = (0,0,0,0,0);
my ($debug, $verbose, $preview, $global, $use_fqdn, $ignore_errors) = (0,0,0,0,0,0);
my (@config_files, $fragments_dir, $visudo_bin, $immutable_self_file, $immutable_self_cmd);
my (%options, @uname, %aliases, %frags, @grants);
my ($os, $host, $hostname, $run_dir);
@ -89,7 +89,7 @@ sub parse_config_file {
my $config_file = shift;
unless (open (CONF_FD, "<", $config_file)) {
do_log ("ERROR: failed to open the configuration file ${config_file} [$! $hostname]")
do_log ("ERROR: failed to open the configuration file ${config_file} [$!/$hostname]")
and exit (1);
}
while (<CONF_FD>) {
@ -102,6 +102,10 @@ sub parse_config_file {
$use_fqdn = $1;
do_log ("DEBUG: picking up setting: use_fqdn=${use_fqdn}");
}
if (/^\s*ignore_errors\s*=\s*(0|1)\s*$/) {
$ignore_errors = $1;
do_log ("DEBUG: picking up setting: ignore_errors=${ignore_errors}");
}
if (/^\s*fragments_dir\s*=\s*([0-9A-Za-z_\-\.\/~]+)\s*$/) {
$fragments_dir = $1;
do_log ("DEBUG: picking up setting: fragments_dir=${fragments_dir}");
@ -156,12 +160,24 @@ sub set_file {
my ($file, $perm, $uid, $gid) = @_;
chmod ($perm, "$file")
or do_log ("ERROR: cannot set permissions on $file [$! $hostname]")
and exit (1);
chown ($uid, $gid, "$file")
or do_log ("ERROR: cannot set ownerships on $file [$! $hostname]")
and exit (1);
my $rc = chmod ($perm, "$file");
if (!$rc) {
if ($ignore_errors) {
do_log ("ERROR: cannot set permissions on $file [$!/$hostname] -- IGNORED");
} else {
do_log ("ERROR: cannot set permissions on $file [$!/$hostname]");
exit (1);
}
}
my $rc = chown ($uid, $gid, "$file");
if (!$rc) {
if ($ignore_errors) {
do_log ("ERROR: cannot set ownerships on $file [$!/$hostname] -- IGNORED");
} else {
do_log ("ERROR: cannot set ownerships on $file [$!/$hostname]");
exit (1);
}
}
return (1);
}
@ -182,6 +198,7 @@ if ( @ARGV > 0 ) {
debug|d
help|h|?
global|g
ignore|i
preview|p
verbose|v
version|V
@ -204,6 +221,10 @@ if ($options{'help'}) {
if ($options{'global'}) {
$global = 1;
}
# check ignore parameter
if ($options{'ignore'}) {
$ignore_errors = 1;
}
# check preview parameter
if ($options{'preview'}) {
$preview = 1;
@ -292,7 +313,7 @@ do_log ("INFO: runtime info: ".getpwuid ($<)."; ${hostname}\@${run_dir}; Perl v$
do_log ("INFO: reading 'alias' file ...");
open (ALIASES, "<", "${run_dir}/alias")
or do_log ("ERROR: cannot read 'alias' file [$! $hostname]") and exit (1);
or do_log ("ERROR: cannot read 'alias' file [$!/$hostname]") and exit (1);
while (<ALIASES>) {
my ($key, $value, @values);
@ -370,7 +391,7 @@ if (-d "${run_dir}/fragments.d" && -f "${run_dir}/fragments") {
if (-d "${run_dir}/fragments.d") {
do_log ("INFO: local 'fragments' are stored in a DIRECTORY on $hostname");
opendir (FRAGS_DIR, "${run_dir}/fragments.d")
or do_log ("ERROR: cannot open 'fragments.d' directory [$! $hostname]")
or do_log ("ERROR: cannot open 'fragments.d' directory [$!/$hostname]")
and exit (1);
while (my $frag_file = readdir (FRAGS_DIR)) {
next if ($frag_file =~ /^\./);
@ -388,7 +409,7 @@ if (-d "${run_dir}/fragments.d") {
# process 'fragments' files
foreach my $frag_file (@frag_files) {
open (FRAGS, "<", $frag_file)
or do_log ("ERROR: cannot read 'fragments' file [$! $hostname]")
or do_log ("ERROR: cannot read 'fragments' file [$!/$hostname]")
and exit (1);
do_log ("INFO: reading SUDO fragments from file: $frag_file");
@ -482,7 +503,7 @@ if ($? == 0) {
do_log ("INFO: reading 'grants' file ...");
open (GRANTS, "<", "${run_dir}/grants")
or do_log ("ERROR: cannot read 'grants' file [$! $hostname]") and exit (1);
or do_log ("ERROR: cannot read 'grants' file [$!/$hostname]") and exit (1);
while (<GRANTS>) {
my ($what, $where, @what, @where);
@ -523,7 +544,7 @@ print Dumper(\@grants) if $debug;
if ($preview && $global) {
open (GRANTS, "<", "${run_dir}/grants")
or do_log ("ERROR: cannot read 'grants' file [$! $hostname]") and exit (1);
or do_log ("ERROR: cannot read 'grants' file [$!/$hostname]") and exit (1);
while (<GRANTS>) {
my ($what, $where, @what, @where);
@ -574,7 +595,7 @@ unless ($preview) {
# remove previous fragment files first
opendir (FRAGS_DIR, "${fragments_dir}")
or do_log ("ERROR: cannot open ${fragments_dir} directory [$! $hostname]")
or do_log ("ERROR: cannot open ${fragments_dir} directory [$!/$hostname]")
and exit (1);
while (my $frag_file = readdir (FRAGS_DIR)) {
next if ($frag_file =~ /^\./ or $frag_file eq $immutable_self_file);
@ -587,7 +608,7 @@ while (my $frag_file = readdir (FRAGS_DIR)) {
if (unlink ($frag_file)) {
do_log ("INFO: de-activating fragment file $frag_file on $hostname");
} else {
do_log ("ERROR: cannot de-activate fragment file(s) [$! $hostname]");
do_log ("ERROR: cannot de-activate fragment file(s) [$!/$hostname]");
exit (1);
}
}
@ -604,7 +625,7 @@ foreach my $grant (@grants) {
unless ($preview) {
open (SUDO_FILE, "+>", $sudo_file)
or do_log ("ERROR: cannot open file for writing in $fragments_dir [$! $hostname]")
or do_log ("ERROR: cannot open file for writing in $fragments_dir [$!/$hostname]")
and exit (1);
}
print SUDO_FILE "$frags{$grant}\n" unless $preview;
@ -639,7 +660,7 @@ unless ($preview) {
my $self_file = "$fragments_dir/$immutable_self_file";
open (SELF_FILE, "+>", $self_file)
or do_log ("ERROR: cannot open file for writing in $fragments_dir [$! $hostname]")
or do_log ("ERROR: cannot open file for writing in $fragments_dir [$!/$hostname]")
and exit (1);
print SELF_FILE "# THIS IS THE IMMUTABLE SELF FRAGMENT OF SUDO CONTROLS\n";
@ -682,6 +703,7 @@ update_sudo.pl - distributes SUDO fragments according to a desired state model.
update_sudo.pl [-d|--debug]
[-h|--help]
[-i|--ignore]
([-p|--preview] [-g|--global])
[-v|--verbose]
[-V|--version]
@ -716,6 +738,8 @@ Following settings must be configured:
=item * B<use_fqdn> : whether to use short or FQDN host names
=item * B<ignore_errors> : whether to ignore errors during fragment deployment
=item * B<fragments_dir> : target directory for SUDO fragments files
=item * B<visudo_bin> : path to the visudo tool (for sudo rules syntax checking)
@ -736,6 +760,10 @@ S< >Be I<very> verbose during execution; show array/hash dumps.
S< >Show the help page.
=item -i | --ignore
S< >Ignore errors during fragment deployment.
=item -p | --preview
S< >Do not actually distribute any SUDO fragments, nor update/remove SUDO files.
@ -766,4 +794,4 @@ S< >Show version of the script.
=head1 AUTHOR
(c) KUDOS BVBA, Patrick Van der Veken
(c) KUDOS BV, Patrick Van der Veken