Compare commits

..

1 Commits

Author SHA1 Message Date
Patrick Van der Veken
da092d9a1b fix for sudoers.d ownership on HP-UX (VRF 1.3.4) [Patrick Van der Veken] 2015-09-29 07:51:55 +02:00
6 changed files with 403 additions and 1307 deletions

View File

@ -1,34 +1,18 @@
<p align="center"><img src="logo.png" alt="SUDO Controls Logo"></p>
## What's new
:loudspeaker: **27/04/2025**:
* added the `ignore_errors` flag to allow uninterrupted deployment of fragements.
## About
# SUDO Controls
SUDO Controls is a light-weight **SUDO fragments/rules** distribution & management framework which:
* uses a **desired state** model: SUDO Controls *pushes* fragments from a master (or slave) server onto client host(s) and applies them according to the central configuration.
* uses a **desired state** model: SUDO Controls pushes fragments from a master server onto client host and applies them according to the central configuration.
* uses **SSH** as **transport** mechanism: SUDO Controls connects to client hosts through the secure path of SSH.
* supports a **Master→Slave→Client** model so that information can be propagated within more complex LAN set-ups.
* performs operations with **least privileges**: copy/distribute operations are performed with a low-privileged account. Only the actual snippet updates requires super-user privileges.
* uses a **two-stage** approach to activate **SUDO fragments**: copy (or distribute) and apply. Fragments are first copied into a temporary location on each client hosts - the holding directory - and not applied automatically. Applying or activating fragments on a client host is a separate operation which can be triggered either locally or remotely (from the SUDO master)
* allows the use of (nested) **groups** in the master configuration: fragments and hosts can be grouped in the SUDO master configuration files to allow a simplified configuration. Nesting of groups is allowed up to *5 levels* deep.
* allows the use of (nested) **groups** in the specification of the *push* targets. Either via the `--targets` command-line parameter or via the `targets` configuration file.
* can discover SSH host public keys to (re)create `known_hosts` file(s) for a large amount of hosts
* allows the use of (nested) **groups** in the master configuration: users, fragments and hosts can be grouped in the SUDO master configuration files to allow a simplified configuration. Nesting of groups is allowed up to one level deep.
* requires **no client agent** component and is **stateless**: SUDO Controls performs operations by pushing fragments or commands to client hosts. Update processes on the client hosts will only be started on-demand. If the SUDO master is - for whatever reason - unavailable then active fragments on a client host remain in place.
* is **easy** to **configure** and **maintain** (command-line based): the configuration is stored in a limited number of flat files and be easily updated. A very rudimentary syntax checking facility is also available to check the consistency of the most important (master) configuration files.
More documentation can be found at <https://www.kudos.be/sudo_controls/>
*Logo created with [Free Logo Maker](https://logomakr.com)*
More documentation can be found at http://www.kudos.be/Projects/SUDO_Controls.html

BIN
logo.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.8 KiB

View File

@ -13,7 +13,7 @@
# (leave blank for current user)
SUDO_TRANSFER_USER=""
# name of the UNIX group that should own the SUDO controls files (must exist already)
# name of the OS group that should own the SUDO controls files
SUDO_OWNER_GROUP="sudoadmin"
# whether a 'chmod' needs to be executed after each sftp transfer [0=No; 1=Yes]
@ -32,8 +32,7 @@ LOCAL_DIR="/etc/sudo_master"
REMOTE_DIR="/etc/sudo_controls/holding"
# name of the user account performing the SUDO controls update
# (leave blank for current user running script)
# user should have remote sudo root privs (except when using user 'root')
# (leave blank for current user but user should have remote sudo root privs)
SUDO_UPDATE_USER=""
# options to pass to update_sudo.pl when executing a key update
@ -42,20 +41,6 @@ SUDO_UPDATE_OPTS="--verbose"
# path to the visudo tool
VISUDO_BIN="/usr/sbin/visudo"
# path to the ssh-keyscan too
SSH_KEYSCAN_BIN="/usr/bin/ssh-keyscan"
# extra arguments/options for the ssh-keyscan command
# by default -f <file> is used by manage_sudo.sh to supply hostnames, do not add here
SSH_KEYSCAN_ARGS="-t rsa"
# whether to start an SSH agent process for the master->client operations [0=No; 1=Yes]
DO_SSH_AGENT=0
# location of the SSH private key that should be added to the SSH agent process
# must be a passphrase-less key (required when using DO_SSH_AGENT)
SSH_PRIVATE_KEY="$HOME/.ssh/id_rsa"
# maximum number of background process to spawn (~maxuprc, ~nstrpty etc)
MAX_BACKGROUND_PROCS=30

File diff suppressed because it is too large Load Diff

View File

@ -12,9 +12,6 @@
# 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 BV <info@kudos.be>. All rights reserved.
# @(#) Copyright (C) 2014 by KUDOS BVBA <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
@ -43,23 +43,21 @@ use File::Temp qw(tempfile);
#******************************************************************************
# ------------------------- CONFIGURATION starts here -------------------------
# define the version (YYYY-MM-DD)
my $script_version = "2025-04-27";
# define the V.R.F (version/release/fix)
my $MY_VRF = "1.1.4";
# 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)
my $local_config_file = "update_sudo.conf.local";
# maxiumum level of recursion for alias resolution
my $max_recursion = 5;
# selinux context label of sudoers fragment files
my $selinux_context = "etc_t";
# ------------------------- CONFIGURATION ends here ---------------------------
# ------------------------- CONFIGURATION ends here ---------------------------
# initialize variables
my ($debug, $verbose, $preview, $global, $use_fqdn, $ignore_errors) = (0,0,0,0,0,0);
my ($debug, $verbose, $preview, $global, $use_fqdn) = (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);
my ($selinux_status, $has_selinux, $recursion_count) = ("",0,1);
my ($selinux_status, $has_selinux) = ("",0);
$|++;
@ -69,7 +67,7 @@ $|++;
# -----------------------------------------------------------------------------
sub do_log {
my $message = shift;
if ($message =~ /^ERROR:/ || $message =~ /^WARN:/) {
@ -89,7 +87,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>) {
@ -98,14 +96,10 @@ sub parse_config_file {
if (/^\s*$/ || /^#/) {
next;
} else {
if (/^\s*use_fqdn\s*=\s*(0|1)\s*$/) {
if (/^\s*use_fqdn\s*=\s*([0-9]+)\s*$/) {
$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}");
@ -124,7 +118,7 @@ sub parse_config_file {
}
}
}
# parameter checks
if (not defined ($immutable_self_file) or $immutable_self_file eq "") {
do_log ("ERROR: 'immutable_self_file' parameter not defined [$hostname]")
@ -144,7 +138,7 @@ sub resolve_aliases
foreach $entry (@tmp_array) {
if ($entry =~ /^\@/) {
($aliases{$entry})
? push (@new_array, @{$aliases{$entry}})
? push (@new_array, @{$aliases{$entry}})
: do_log ("WARN: unable to resolve alias $entry [$hostname]");
} else {
($entry)
@ -159,26 +153,14 @@ sub resolve_aliases
sub set_file {
my ($file, $perm, $uid, $gid) = @_;
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);
}
}
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);
return (1);
}
@ -198,18 +180,17 @@ if ( @ARGV > 0 ) {
debug|d
help|h|?
global|g
ignore|i
preview|p
verbose|v
version|V
)) || pod2usage(-verbose => 0);
}
pod2usage(-verbose => 0) unless (%options);
pod2usage(-verbose => 0) unless (%options);
# check version parameter
if ($options{'version'}) {
$verbose = 1;
do_log ("INFO: $0: version $script_version");
do_log ("INFO: $0: version $MY_VRF");
exit (0);
}
# check help parameter
@ -221,16 +202,12 @@ 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;
$verbose = 1;
if ($global) {
do_log ("INFO: running in GLOBAL PREVIEW mode");
do_log ("INFO: running in GLOBAL PREVIEW mode");
} else {
do_log ("INFO: running in PREVIEW mode");
}
@ -250,7 +227,7 @@ $verbose = 1 if ($options{'verbose'});
# where am I? (1/2)
$0 =~ /^(.+[\\\/])[^\\\/]+[\\\/]*$/;
$run_dir = $1 || ".";
my $run_dir = $1 || ".";
$run_dir =~ s#/$##; # remove trailing slash
# don't do anything without configuration file(s)
@ -258,7 +235,7 @@ do_log ("INFO: parsing configuration file(s) ...");
push (@config_files, "$run_dir/$global_config_file") if (-f "$run_dir/$global_config_file");
push (@config_files, "$run_dir/$local_config_file") if (-f "$run_dir/$local_config_file");
unless (@config_files) {
do_log ("ERROR: unable to find any configuration file, bailing out [$hostname]")
do_log ("ERROR: unable to find any configuration file, bailing out [$hostname]")
and exit (1);
}
@ -273,7 +250,7 @@ unless ($preview and $global) {
if (-d $fragments_dir) {
do_log ("INFO: host is under SUDO control via $fragments_dir");
} else {
do_log ("ERROR: host is not under SUDO control [$hostname]")
do_log ("ERROR: host is not under SUDO control [$hostname]")
and exit (1);
}
}
@ -281,7 +258,7 @@ unless ($preview and $global) {
# is syntax checking possible? (not for global preview)
unless ($preview and $global) {
unless (-x $visudo_bin) {
do_log ("ERROR: 'visudo' tool could not be found, will not continue [$hostname]")
do_log ("ERROR: 'visudo' tool could not be found, will not continue [$hostname]")
and exit (1);
}
}
@ -292,7 +269,7 @@ $os = $uname[0];
# who am I?
unless ($preview and $global) {
if ($< != 0) {
do_log ("ERROR: script must be invoked as user 'root' [$hostname]")
do_log ("ERROR: script must be invoked as user 'root' [$hostname]")
and exit (1);
}
}
@ -313,11 +290,11 @@ 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);
chomp ();
next if (/^$/ || /\#/);
s/\s+//g;
@ -330,44 +307,13 @@ close (ALIASES);
do_log ("DEBUG: dumping unexpanded aliases:");
print Dumper (\%aliases) if $debug;
# resolve aliases recursively to a maxium of $max_recursion
while ($recursion_count <= $max_recursion) {
# crawl over all items in the hash %aliases
foreach my $key (keys (%aliases)) {
# crawl over all items in the array @{aliases{$key}}
my @new_array; my @filtered_array; # these are the working stashes
do_log ("DEBUG: expanded alias $key before recursion $recursion_count [$hostname]");
print Dumper (\@{$aliases{$key}}) if $debug;
foreach my $item (@{$aliases{$key}}) {
# is it a group?
if ($item =~ /^\@/) {
# expand the group if it exists
if ($aliases{$item}) {
# add current and new items to the working stash
if (@new_array) {
push (@new_array, @{$aliases{$item}});
} else {
@new_array = (@{$aliases{$key}}, @{$aliases{$item}});
}
# remove the original group item from the working stash
@filtered_array = grep { $_ ne $item } @new_array;
@new_array = @filtered_array;
} else {
do_log ("WARN: unable to resolve alias $item [$hostname]");
}
# no group, just add the item as-is to working stash
} else {
push (@new_array, $item);
}
}
my %seen;
@filtered_array = grep { not $seen{$_}++ } @new_array;
# re-assign working stash back to our original hash key
@{$aliases{$key}} = @filtered_array;
do_log ("DEBUG: expanded alias $key after recursion $recursion_count [$hostname]");
print Dumper (\@{$aliases{$key}}) if $debug;
}
$recursion_count++;
# we can nest aliases one level deep, so do a one-level recursive sort of lookup
# of the remaining '@' aliases. Input should be passed as comma-separated
# string to resolve_aliases so don't forget to smash everything back together
# first.
foreach my $key (keys (%aliases)) {
$aliases{$key} = [resolve_aliases (join (",", @{$aliases{$key}}))];
}
do_log ("INFO: ".scalar (keys (%aliases))." aliases found on $hostname");
@ -375,8 +321,8 @@ do_log ("DEBUG: dumping expanded aliases:");
print Dumper (\%aliases) if $debug;
# -----------------------------------------------------------------------------
# read SUDO fragments stored in a single 'fragments' file or in
# individual fragment files from a 'fragments.d' directory
# read SUDO fragments stored in a single 'fragments' file or in
# individual fragment files from a 'fragments.d' directory
# result: %frags
# -----------------------------------------------------------------------------
@ -391,43 +337,43 @@ 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 =~ /^\./);
push (@frag_files, "${run_dir}/fragments.d/$frag_file");
}
closedir (FRAGS_DIR);
closedir (FRAGS_DIR);
} elsif (-f "${run_dir}/fragments") {
do_log ("INFO: local 'fragments' are stored in a FILE on $hostname");
push (@frag_files, "${run_dir}/fragments");
} else {
do_log ("ERROR: cannot find any SUDO fragments in the repository! [$hostname]")
do_log ("ERROR: cannot find any SUDO fragments in the repository! [$hostname]")
and exit (1);
}
# 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");
my @frag_file = <FRAGS>;
# check for fragments header(s): if there is no fragment header, then we
# consider this a single fragment file, otherwise we consider it a
# consider this a single fragment file, otherwise we consider it a
# collection of fragments that needs to be broken down in individual fragments
if (grep { /^%%%/s } @frag_file) {
do_log ("INFO: fragment file $frag_file contains multiple fragments, parsing ...");
my ($frag_file, $frag_def);
my $count = 1;
foreach (@frag_file) {
# first header found
if (/^%%%/ && (not defined ($frag_def) or $frag_def eq "")) {
@ -451,7 +397,7 @@ foreach my $frag_file (@frag_files) {
chomp ($frag_file);
unless (defined ($frag_file) && $frag_file ne "") {
do_log ("WARN: no fragment file name found in header at line $count [$hostname]")
}
}
} else {
# process fragment definition
$frag_def .= $_;
@ -466,8 +412,8 @@ foreach my $frag_file (@frag_files) {
# strip off path from file name for hash key
$frag_file = fileparse ($frag_file, qr/\.[^.]*/);
do_log ("INFO: fragment file $frag_file contains only 1 fragment on $hostname");
$frags{$frag_file} = join ("\n", @frag_file);
}
$frags{$frag_file} = join (/\n/, @frag_file);
}
close (FRAGS);
}
@ -490,24 +436,24 @@ if ($? == 0) {
do_log ("INFO: syntax check of sudo fragments is OK on $hostname");
unlink $sudo_file;
} else {
do_log "ERROR: visudo check failed: ".join ("\n", @syntax_check)." [$hostname]"
do_log "ERROR: visudo check failed: ".join ("\n", @syntax_check)." [$hostname]"
and exit(1);
}
# -----------------------------------------------------------------------------
# read grant definitions
# result: @grants (array): fragments for which grants have been defined
# result: @grants (array): fragments for which grants have been defined
# for this server.
# -----------------------------------------------------------------------------
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);
chomp ();
next if (/^$/ || /\#/);
s/\s+//g;
@ -519,10 +465,10 @@ while (<GRANTS>) {
do_log ("WARN: ignoring line $. in 'grants' due to missing/non-resolving values [$hostname]");
next;
}
foreach my $grant (sort (@what)) {
foreach my $server (sort (@where)) {
do_log ("DEBUG: adding grants for $grant on $server in \@grants")
do_log ("DEBUG: adding grants for $grant on $server in \@grants")
if ($server eq $hostname);
# add sudo fragment to grants list if the entry is for this host
push (@grants, $grant) if ($server eq $hostname);
@ -544,11 +490,11 @@ 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);
chomp ();
next if (/^$/ || /\#/);
s/\s+//g;
@ -560,15 +506,15 @@ if ($preview && $global) {
do_log ("WARN: ignoring line $. in 'grants' due to missing/non-resolving values [$hostname]");
next;
}
foreach my $grant (sort (@what)) {
foreach my $server (sort (@where)) {
do_log ("$grant|$server")
do_log ("$grant|$server")
}
}
};
close (GRANTS);
exit (0);
}
@ -581,71 +527,71 @@ do_log ("INFO: (de)-activating SUDO fragments ....");
# check for SELinux
unless ($preview) {
SWITCH: {
$os eq "Linux" && do {
$os eq "Linux" && do {
$selinux_status = qx#/usr/sbin/getenforce 2>/dev/null#;
chomp ($selinux_status);
if ($selinux_status eq "Permissive" or $selinux_status eq "Enforcing") {
do_log ("INFO: runtime info: detected active SELinux system on $hostname");
$has_selinux = 1;
}
last SWITCH;
last SWITCH;
};
}
}
# 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);
# safe to ignore . (dot) files as sudo also does as well
unless ($preview) {
my $frag_file = "$fragments_dir/$frag_file";
if (unlink ($frag_file)) {
do_log ("INFO: de-activating fragment file $frag_file on $hostname");
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);
}
}
}
closedir (FRAGS_DIR);
closedir (FRAGS_DIR);
# re-active current fragments
foreach my $grant (@grants) {
# do not create empty sudo files
if (exists ($frags{$grant})) {
my $sudo_file = "$fragments_dir/$grant";
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;
do_log ("INFO: activating fragment $grant on $hostname");
close (SUDO_FILE) unless $preview;
# set permissions to world readable & SELinux contexts
unless ($preview) {
SWITCH: {
$os eq "HP-UX" && do {
set_file ($sudo_file, 0440, 2, 2);
last SWITCH;
$os eq "HP-UX" && do {
set_file ($sudo_file, 0440, 2, 2);
last SWITCH;
};
$os eq "Linux" && do {
$os eq "Linux" && do {
if ($has_selinux) {
system ("/usr/bin/chcon -t $selinux_context $sudo_file") == 0 or
do_log ("WARN: failed to set SELinux context $selinux_context on $sudo_file [$hostname]");
}
set_file ($sudo_file, 0440, 0, 0);
last SWITCH;
last SWITCH;
};
}
}
@ -660,27 +606,27 @@ 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";
print SELF_FILE $immutable_self_cmd."\n";
do_log ("INFO: activating immutable self fragment $immutable_self_file on $hostname");
SWITCH: {
$os eq "HP-UX" && do {
set_file ($self_file, 0440, 2, 2);
$os eq "HP-UX" && do {
set_file ($self_file, 0440, 2, 2);
last SWITCH;
};
$os eq "Linux" && do {
$os eq "Linux" && do {
if ($has_selinux) {
system ("/usr/bin/chcon -t $selinux_context $self_file") == 0 or
do_log ("WARN: failed to set SELinux context $selinux_context on $self_file [$hostname]");
}
set_file ($self_file, 0440, 0, 0);
last SWITCH;
last SWITCH;
};
}
close (SELF_FILE);
close (SELF_FILE);
}
exit (0);
@ -701,21 +647,20 @@ update_sudo.pl - distributes SUDO fragments according to a desired state model.
=head1 SYNOPSIS
update_sudo.pl [-d|--debug]
[-h|--help]
[-i|--ignore]
update_sudo.pl [-d|--debug]
[-h|--help]
([-p|--preview] [-g|--global])
[-v|--verbose]
[-V|--version]
=head1 DESCRIPTION
B<update_sudo.pl> distributes SUDO fragments into the C<$fragments_dir> repository based on the F<grants>, F<alias> and F<fragments> files.
This script should be run on each host where SUDO is the required method of privilege escalation.
This script should be run on each host where SUDO is the required method of privilege escalation.
Orginally SUDO fragments must be stored in a generic F<fragments> file within the same directory as B<update_sudo.pl> script.
Alternatively SUDO fragments may be stored as set of individual files within a called sub-directory called F<fragments.d>.
For update SUDO fragments must be stored in a generic F<fragments> file within the same directory as B<update_sudo.pl> script.
Alternatively SUDO fragments may be stored as set of individual files within a called sub-directory called F<fragments.d>.
Both methods are mutually exclusive and the latter always take precedence.
=head1 CONFIGURATION
@ -728,7 +673,7 @@ B<update_sudo.pl> requires the presence of at least one of the following configu
=item * F<update_sudo.conf.local>
=back
=back
Use F<update_sudo.conf.local> for localized settings per host. Settings in the localized configuration file will always override other values.
@ -738,8 +683,6 @@ 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)
@ -760,10 +703,6 @@ 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.
@ -775,12 +714,12 @@ S< >Must be used in conjunction with the --preview option. This will dump
=item -v | --verbose
S< >Be verbose during exection.
=item -V | --version
S< >Show version of the script.
=back
=back
=head1 NOTES
@ -790,8 +729,20 @@ S< >Show version of the script.
=item * Options may be bundled (e.g. -vp)
=back
=back
=head1 AUTHOR
(c) KUDOS BV, Patrick Van der Veken
(c) KUDOS BVBA, Patrick Van der Veken
=head1 HISTORY
@(#) 2014-12-04: VRF 1.0.0: first version [Patrick Van der Veken]
@(#) 2014-12-16: VRF 1.0.1: added SELinux context [Patrick Van der Veken]
@(#) 2014-12-16: VRF 1.0.2: fixed a problem with the immutable self fragment code [Patrick Van der Veken]
@(#) 2015-02-02: VRF 1.0.3: changed 'basename' into 'fileparse' call to support fragment files with extensions [Patrick Van der Veken]
@(#) 2015-08-18: VRF 1.1.0: replace uname/hostname syscalls, now support for FQDN via $use_fqdn, other fixes [Patrick Van der Veken]
@(#) 2015-08-26: VRF 1.1.1: small and not so small fixes [Patrick Van der Veken]
@(#) 2015-08-27: VRF 1.1.2: small fix [Patrick Van der Veken]
@(#) 2015-09-09: VRF 1.1.3: small selinux fix [Patrick Van der Veken]
@(#) 2015-09-09: VRF 1.1.4: wrong handling of RC=0 in system() [Patrick Van der Veken]