* added support for SELinux (CentOS/RHEL 8.x)

* various fixes
This commit is contained in:
Patrick Van der Veken 2020-12-30 19:23:11 +01:00
parent af291c07e0
commit 330e413b98
3 changed files with 183 additions and 114 deletions

View File

@ -1,5 +1,13 @@
<p align="center"><img src="logo.png" alt="SUDO Controls Logo"></p> <p align="center"><img src="logo.png" alt="SUDO Controls Logo"></p>
## What's new
:loudspeaker: **30/12/2020**:
* added support for SELinux (CentOS/RHEL 8.x)
* various fixes
## About
SUDO Controls is a light-weight **SUDO fragments/rules** distribution & management framework which: 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 (or slave) server onto client host(s) and applies them according to the central configuration.

View File

@ -42,7 +42,7 @@
# or LOCAL_CONFIG_FILE instead # or LOCAL_CONFIG_FILE instead
# define the version (YYYY-MM-DD) # define the version (YYYY-MM-DD)
typeset -r SCRIPT_VERSION="2020-05-28" typeset -r SCRIPT_VERSION="2020-12-30"
# name of the global configuration file (script) # name of the global configuration file (script)
typeset -r GLOBAL_CONFIG_FILE="manage_sudo.conf" typeset -r GLOBAL_CONFIG_FILE="manage_sudo.conf"
# name of the local configuration file (script) # name of the local configuration file (script)
@ -279,11 +279,11 @@ fi
# --targets # --targets
if [[ -n "${ARG_TARGETS}" ]] if [[ -n "${ARG_TARGETS}" ]]
then then
: > ${TMP_FILE} : > "${TMP_FILE}"
# write comma-separated target list to the temporary file # write comma-separated target list to the temporary file
print "${ARG_TARGETS}" | tr -s ',' '\n' | while read TARGET_HOST print "${ARG_TARGETS}" | tr -s ',' '\n' | while read -r TARGET_HOST
do do
print ${TARGET_HOST} >>${TMP_FILE} print "${TARGET_HOST}" >>"${TMP_FILE}"
done done
fi fi
# --update + --fix-local + --resolve-alias # --update + --fix-local + --resolve-alias
@ -323,7 +323,7 @@ function check_root_user
typeset UID="" typeset UID=""
# shellcheck disable=SC2046 # shellcheck disable=SC2046
(IFS='()'; set -- $(id); print $2) | read UID (IFS='()'; set -- $(id); print "$2") | read -r UID
if [[ "${UID}" = "root" ]] if [[ "${UID}" = "root" ]]
then then
return 0 return 0
@ -423,6 +423,7 @@ if (( DO_SSH_AGENT ))
then then
# ssh-agent # ssh-agent
which ssh-agent >/dev/null 2>/dev/null which ssh-agent >/dev/null 2>/dev/null
# shellcheck disable=SC2181
if (( $? > 0 )) if (( $? > 0 ))
then then
print -u2 "WARN: ssh-agent not available on ${HOST_NAME}" print -u2 "WARN: ssh-agent not available on ${HOST_NAME}"
@ -450,16 +451,15 @@ typeset ALIAS_FIELDS=""
typeset ALIAS="" typeset ALIAS=""
typeset CHECK_ALIAS="" typeset CHECK_ALIAS=""
# grants should have 2 fields # grants should have 2 fields
cat "${LOCAL_DIR}/grants" | grep -v -E -e '^#|^$' | while read GRANTS_LINE grep -v -E -e '^#|^$' "${LOCAL_DIR}/grants" 2>/dev/null | while read -r GRANTS_LINE
do do
GRANTS_FIELDS=$(count_fields "${GRANTS_LINE}" ":") GRANTS_FIELDS=$(count_fields "${GRANTS_LINE}" ":")
(( GRANTS_FIELDS != 2 )) && die "line '${GRANTS_LINE}' in grants file has missing or too many field(s) (should be 2)" (( GRANTS_FIELDS != 2 )) && die "line '${GRANTS_LINE}' in grants file has missing or too many field(s) (should be 2)"
done done
# alias should have 2 fields # alias should have 2 fields
cat "${LOCAL_DIR}/alias" | grep -v -E -e '^#|^$' | while read ALIAS_LINE grep -v -E -e '^#|^$' "${LOCAL_DIR}/alias" 2>/dev/null | while read -r ALIAS_LINE
do do
ALIAS_FIELDS=$(count_fields "${ALIAS_LINE}" ":") ALIAS_FIELDS=$(count_fields "${ALIAS_LINE}" ":")
(( ALIAS_FIELDS != 2 )) && die "line '${ALIAS_LINE}' in alias file has missing or too many field(s) (should be 2)" (( ALIAS_FIELDS != 2 )) && die "line '${ALIAS_LINE}' in alias file has missing or too many field(s) (should be 2)"
@ -514,6 +514,7 @@ typeset NUM_FIELDS=0
NUM_FIELDS=$(print "${CHECK_LINE}" | awk -F "${CHECK_DELIM}" '{ print NF }' 2>/dev/null) NUM_FIELDS=$(print "${CHECK_LINE}" | awk -F "${CHECK_DELIM}" '{ print NF }' 2>/dev/null)
# shellcheck disable=SC2086
print ${NUM_FIELDS} print ${NUM_FIELDS}
return 0 return 0
@ -531,7 +532,7 @@ if [[ -n "$1" ]]
then then
if (( ARG_LOG > 0 )) if (( ARG_LOG > 0 ))
then then
print - "$*" | while read LOG_LINE print - "$*" | while read -r LOG_LINE
do do
# check for leading log sigils and retain them # check for leading log sigils and retain them
case "${LOG_LINE}" in case "${LOG_LINE}" in
@ -551,10 +552,10 @@ then
LOG_SIGIL="ERROR" LOG_SIGIL="ERROR"
;; ;;
esac esac
print "${NOW}: ${LOG_SIGIL}: [$$]:" "${LOG_LINE}" >>${LOG_FILE} print "${NOW}: ${LOG_SIGIL}: [$$]:" "${LOG_LINE}" >>"${LOG_FILE}"
done done
fi fi
print - "$*" | while read LOG_LINE print - "$*" | while read -r LOG_LINE
do do
# check for leading log sigils and retain them # check for leading log sigils and retain them
case "${LOG_LINE}" in case "${LOG_LINE}" in
@ -650,8 +651,9 @@ typeset COPY_RC=0
typeset TMP_WORK_DIR="" typeset TMP_WORK_DIR=""
# convert line to hostname # convert line to hostname
SERVER=${SERVER%%;*} SERVER="${SERVER%%;*}"
resolve_host ${SERVER} resolve_host "${SERVER}"
# shellcheck disable=SC2181
if (( $? > 0 )) if (( $? > 0 ))
then then
warn "could not lookup host ${SERVER}, skipping" warn "could not lookup host ${SERVER}, skipping"
@ -668,7 +670,7 @@ for FILE in "${LOCAL_DIR}/grants!660" \
"${SCRIPT_DIR}/${GLOBAL_CONFIG_FILE}!660" "${SCRIPT_DIR}/${GLOBAL_CONFIG_FILE}!660"
do do
# sftp transfer # sftp transfer
sftp_file ${FILE} ${SERVER} sftp_file "${FILE}" "${SERVER}"
COPY_RC=$? COPY_RC=$?
if (( COPY_RC == 0 )) if (( COPY_RC == 0 ))
then then
@ -683,30 +685,33 @@ done
if [[ -n "${FRAGS_DIR}" ]] if [[ -n "${FRAGS_DIR}" ]]
then then
TMP_WORK_DIR="${TMP_DIR}/$0.${RANDOM}" TMP_WORK_DIR="${TMP_DIR}/$0.${RANDOM}"
mkdir -p ${TMP_WORK_DIR} mkdir -p "${TMP_WORK_DIR}"
# shellcheck disable=SC2181
if (( $? > 0 )) if (( $? > 0 ))
then then
die "unable to create temporary directory ${TMP_WORK_DIR} for mangling of 'fragments' file" die "unable to create temporary directory ${TMP_WORK_DIR} for mangling of 'fragments' file"
fi fi
# merge fragments file(s) before copy (in a temporary location) # merge fragments file(s) before copy (in a temporary location)
merge_fragments ${TMP_WORK_DIR} merge_fragments "${TMP_WORK_DIR}"
if (( $? )) # shellcheck disable=SC2181
if (( $? > 0 ))
then then
die "failed to merge fragments into the temporary file ${TMP_MERGE_FILE}" die "failed to merge fragments into the temporary file ${TMP_MERGE_FILE}"
fi fi
# sftp transfer # sftp transfer
sftp_file "${TMP_MERGE_FILE}!640" ${SERVER} sftp_file "${TMP_MERGE_FILE}!640" "${SERVER}"
COPY_RC=$? COPY_RC=$?
if (( COPY_RC == 0 )) if (( COPY_RC == 0 ))
then then
# shellcheck disable=SC2086
log "transferred ${TMP_MERGE_FILE} to ${SERVER}:${REMOTE_DIR}" log "transferred ${TMP_MERGE_FILE} to ${SERVER}:${REMOTE_DIR}"
else else
warn "failed to transfer ${TMP_MERGE_FILE%!*} to ${SERVER}:${REMOTE_DIR} [RC=${COPY_RC}]" warn "failed to transfer ${TMP_MERGE_FILE%!*} to ${SERVER}:${REMOTE_DIR} [RC=${COPY_RC}]"
ERROR_COUNT=$(( ERROR_COUNT + 1 )) ERROR_COUNT=$(( ERROR_COUNT + 1 ))
fi fi
[[ -d ${TMP_WORK_DIR} ]] && rm -rf ${TMP_WORK_DIR} 2>/dev/null [[ -d "${TMP_WORK_DIR}" ]] && rm -rf "${TMP_WORK_DIR}" 2>/dev/null
else else
sftp_file "${FRAGS_FILE}!640" ${SERVER} sftp_file "${FRAGS_FILE}!640" "${SERVER}"
COPY_RC=$? COPY_RC=$?
if (( COPY_RC == 0 )) if (( COPY_RC == 0 ))
then then
@ -720,7 +725,6 @@ fi
return ${ERROR_COUNT} return ${ERROR_COUNT}
} }
# ----------------------------------------------------------------------------- # -----------------------------------------------------------------------------
# distribute SUDO controls to a single host in slave mode # distribute SUDO controls to a single host in slave mode
function distribute2slave function distribute2slave
@ -731,8 +735,9 @@ typeset DISTRIBUTE_OPTS=""
typeset RC=0 typeset RC=0
# convert line to hostname # convert line to hostname
SERVER=${SERVER%%;*} SERVER="${SERVER%%;*}"
resolve_host ${SERVER} resolve_host "${SERVER}"
# shellcheck disable=SC2181
if (( $? > 0 )) if (( $? > 0 ))
then then
warn "could not lookup host ${SERVER}, skipping" warn "could not lookup host ${SERVER}, skipping"
@ -745,13 +750,15 @@ then
fi fi
log "copying SUDO controls on ${SERVER} in slave mode, this may take a while ..." log "copying SUDO controls on ${SERVER} in slave mode, this may take a while ..."
( RC=0; ssh -A ${SSH_ARGS} ${SERVER} ${REMOTE_DIR}/${SCRIPT_NAME} --copy ${DISTRIBUTE_OPTS}; # shellcheck disable=SC2029
print "$?" > ${TMP_RC_FILE}; exit ( RC=0; ssh -A ${SSH_ARGS} "${SERVER}" "${REMOTE_DIR}/${SCRIPT_NAME} --copy ${DISTRIBUTE_OPTS}";
) 2>&1 | logc print "$?" > "${TMP_RC_FILE}"; exit
) 2>&1 | logc ""
# fetch return code from subshell # fetch return code from subshell
RC="$(< ${TMP_RC_FILE})" RC=$(< "${TMP_RC_FILE}")
# shellcheck disable=SC2086
return ${RC} return ${RC}
} }
@ -762,13 +769,13 @@ function do_cleanup
log "performing cleanup ..." log "performing cleanup ..."
# remove temporary file(s) # remove temporary file(s)
[[ -f ${TMP_FILE} ]] && rm -f ${TMP_FILE} >/dev/null 2>&1 [[ -f "${TMP_FILE}" ]] && rm -f "${TMP_FILE}" >/dev/null 2>&1
[[ -f ${TMP_MERGE_FILE} ]] && rm -f ${TMP_MERGE_FILE} >/dev/null 2>&1 [[ -f "${TMP_MERGE_FILE}" ]] && rm -f "${TMP_MERGE_FILE}" >/dev/null 2>&1
[[ -f ${TMP_RC_FILE} ]] && rm -f ${TMP_RC_FILE} >/dev/null 2>&1 [[ -f "${TMP_RC_FILE}" ]] && rm -f "${TMP_RC_FILE}" >/dev/null 2>&1
# temporary scan file (syntax check) # temporary scan file (syntax check)
if (( CAN_REMOVE_TEMP > 0 )) if (( CAN_REMOVE_TEMP > 0 ))
then then
[[ -f ${TMP_SCAN_FILE} ]] && rm -f ${TMP_SCAN_FILE} >/dev/null 2>&1 [[ -f "${TMP_SCAN_FILE}" ]] && rm -f "${TMP_SCAN_FILE}" >/dev/null 2>&1
fi fi
log "*** finish of ${SCRIPT_NAME} [${CMD_LINE}] /$$@${HOST_NAME}/ ***" log "*** finish of ${SCRIPT_NAME} [${CMD_LINE}] /$$@${HOST_NAME}/ ***"
@ -789,8 +796,9 @@ typeset FIX_OPTS=""
typeset RC=0 typeset RC=0
# convert line to hostname # convert line to hostname
SERVER=${SERVER%%;*} SERVER="${SERVER%%;*}"
resolve_host ${SERVER} resolve_host "${SERVER}"
# shellcheck disable=SC2181
if (( $? > 0 )) if (( $? > 0 ))
then then
warn "could not lookup host ${SERVER}, skipping" warn "could not lookup host ${SERVER}, skipping"
@ -811,25 +819,29 @@ log "fixing SUDO controls on ${SERVER} ..."
if [[ -z "${SUDO_UPDATE_USER}" ]] if [[ -z "${SUDO_UPDATE_USER}" ]]
then then
# own user w/ sudo # own user w/ sudo
( RC=0; ssh ${SSH_ARGS} ${SERVER} sudo -n ${REMOTE_DIR}/${SCRIPT_NAME} --fix-local --fix-dir=${SERVER_DIR} --fix-user=${SERVER_USER} ${FIX_OPTS}; # shellcheck disable=SC2029
print "$?" > ${TMP_RC_FILE}; exit ( RC=0; ssh ${SSH_ARGS} "${SERVER}" "sudo -n ${REMOTE_DIR}/${SCRIPT_NAME} --fix-local --fix-dir=${SERVER_DIR} --fix-user=${SERVER_USER} ${FIX_OPTS}";
) 2>&1 | logc print "$?" > "${TMP_RC_FILE}"; exit
) 2>&1 | logc ""
elif [[ "${SUDO_UPDATE_USER}" != "root" ]] elif [[ "${SUDO_UPDATE_USER}" != "root" ]]
then then
# other user w/ sudo # other user w/ sudo
( RC=0; ssh ${SSH_ARGS} ${SUDO_UPDATE_USER}@${SERVER} sudo -n ${REMOTE_DIR}/${SCRIPT_NAME} --fix-local --fix-dir=${SERVER_DIR} --fix-user=${SERVER_USER} ${FIX_OPTS}; # shellcheck disable=SC2029
print "$?" > ${TMP_RC_FILE}; exit ( RC=0; ssh ${SSH_ARGS} "${SUDO_UPDATE_USER}@${SERVER}" "sudo -n ${REMOTE_DIR}/${SCRIPT_NAME} --fix-local --fix-dir=${SERVER_DIR} --fix-user=${SERVER_USER} ${FIX_OPTS}";
) 2>&1 | logc print "$?" > "${TMP_RC_FILE}"; exit
) 2>&1 | logc ""
else else
# root user w/o sudo # root user w/o sudo
( RC=0; ssh ${SSH_ARGS} root@${SERVER} ${REMOTE_DIR}/${SCRIPT_NAME} --fix-local --fix-dir=${SERVER_DIR} --fix-user="root" ${FIX_OPTS}; # shellcheck disable=SC2029
print "$?" > ${TMP_RC_FILE}; exit ( RC=0; ssh ${SSH_ARGS} "root@${SERVER}" "${REMOTE_DIR}/${SCRIPT_NAME} --fix-local --fix-dir=${SERVER_DIR} --fix-user=root ${FIX_OPTS}";
) 2>&1 | logc print "$?" > "${TMP_RC_FILE}"; exit
) 2>&1 | logc ""
fi fi
# fetch return code from subshell # fetch return code from subshell
RC="$(< ${TMP_RC_FILE})" RC=$(< "${TMP_RC_FILE}")
# shellcheck disable=SC2086
return ${RC} return ${RC}
} }
@ -846,8 +858,9 @@ typeset FIX_OPTS=""
typeset RC=0 typeset RC=0
# convert line to hostname # convert line to hostname
SERVER=${SERVER%%;*} SERVER="${SERVER%%;*}"
resolve_host ${SERVER} resolve_host "${SERVER}"
# shellcheck disable=SC2181
if (( $? > 0 )) if (( $? > 0 ))
then then
warn "could not lookup host ${SERVER}, skipping" warn "could not lookup host ${SERVER}, skipping"
@ -865,13 +878,15 @@ then
fi fi
log "fixing SUDO controls on ${SERVER} in slave mode, this may take a while ..." log "fixing SUDO controls on ${SERVER} in slave mode, this may take a while ..."
( RC=0; ssh -A ${SSH_ARGS} ${SERVER} ${REMOTE_DIR}/${SCRIPT_NAME} --fix-remote --fix-dir=${SERVER_DIR} --fix-user=${SERVER_USER} ${FIX_OPTS}; # shellcheck disable=SC2029
print "$?" > ${TMP_RC_FILE}; exit ( RC=0; ssh -A ${SSH_ARGS} "${SERVER}" "${REMOTE_DIR}/${SCRIPT_NAME} --fix-remote --fix-dir=${SERVER_DIR} --fix-user=${SERVER_USER} ${FIX_OPTS}";
) 2>&1 | logc print "$?" > "${TMP_RC_FILE}"; exit
) 2>&1 | logc ""
# fetch return code from subshell # fetch return code from subshell
RC="$(< ${TMP_RC_FILE})" RC=$(< "${TMP_RC_FILE}")
# shellcheck disable=SC2086
return ${RC} return ${RC}
} }
@ -910,6 +925,9 @@ then
*release\ 7*) *release\ 7*)
RHEL_VERSION=7 RHEL_VERSION=7
;; ;;
*release\ 8*)
RHEL_VERSION=7
;;
*) *)
RHEL_VERSION="" RHEL_VERSION=""
;; ;;
@ -936,7 +954,7 @@ if [[ -n "$1" ]]
then then
if (( ARG_LOG > 0 )) if (( ARG_LOG > 0 ))
then then
print - "$*" | while read LOG_LINE print - "$*" | while read -r LOG_LINE
do do
# check for leading log sigils and retain them # check for leading log sigils and retain them
case "${LOG_LINE}" in case "${LOG_LINE}" in
@ -956,12 +974,12 @@ then
LOG_SIGIL="INFO" LOG_SIGIL="INFO"
;; ;;
esac esac
print "${NOW}: ${LOG_SIGIL}: [$$]:" "${LOG_LINE}" >>${LOG_FILE} print "${NOW}: ${LOG_SIGIL}: [$$]:" "${LOG_LINE}" >>"${LOG_FILE}"
done done
fi fi
if (( ARG_VERBOSE > 0 )) if (( ARG_VERBOSE > 0 ))
then then
print - "$*" | while read LOG_LINE print - "$*" | while read -r LOG_LINE
do do
# check for leading log sigils and retain them # check for leading log sigils and retain them
case "${LOG_LINE}" in case "${LOG_LINE}" in
@ -996,7 +1014,7 @@ if [[ -n "${LOG_STDIN}" ]]
then then
if (( ARG_LOG > 0 )) if (( ARG_LOG > 0 ))
then then
print - "${LOG_STDIN}" | while read LOG_LINE print - "${LOG_STDIN}" | while read -r LOG_LINE
do do
# check for leading log sigils and retain them # check for leading log sigils and retain them
case "${LOG_LINE}" in case "${LOG_LINE}" in
@ -1016,12 +1034,12 @@ then
LOG_SIGIL="INFO" LOG_SIGIL="INFO"
;; ;;
esac esac
print "${NOW}: ${LOG_SIGIL}: [$$]:" "${LOG_LINE}" >>${LOG_FILE} print "${NOW}: ${LOG_SIGIL}: [$$]:" "${LOG_LINE}" >>"${LOG_FILE}"
done done
fi fi
if (( ARG_VERBOSE > 0 )) if (( ARG_VERBOSE > 0 ))
then then
print - "${LOG_STDIN}" | while read LOG_LINE print - "${LOG_STDIN}" | while read -r LOG_LINE
do do
# check for leading log sigils and retain them # check for leading log sigils and retain them
case "${LOG_LINE}" in case "${LOG_LINE}" in
@ -1041,7 +1059,7 @@ if [[ -n "$1" ]]
then then
if (( ARG_LOG > 0 )) if (( ARG_LOG > 0 ))
then then
print - "$*" | while read LOG_LINE print - "$*" | while read -r LOG_LINE
do do
# check for leading log sigils and retain them # check for leading log sigils and retain them
case "${LOG_LINE}" in case "${LOG_LINE}" in
@ -1061,12 +1079,12 @@ then
LOG_SIGIL="INFO" LOG_SIGIL="INFO"
;; ;;
esac esac
print "${NOW}: ${LOG_SIGIL}: [$$]:" "${LOG_LINE}" >>${LOG_FILE} print "${NOW}: ${LOG_SIGIL}: [$$]:" "${LOG_LINE}" >>"${LOG_FILE}"
done done
fi fi
if (( ARG_VERBOSE > 0 )) if (( ARG_VERBOSE > 0 ))
then then
print - "$*" | while read LOG_LINE print - "$*" | while read -r LOG_LINE
do do
case "${LOG_LINE}" in case "${LOG_LINE}" in
INFO:*|WARN:*|ERROR*) INFO:*|WARN:*|ERROR*)
@ -1107,7 +1125,7 @@ then
fi fi
# get aliases from alias line # get aliases from alias line
ALIASES_LINE=$(grep -E -e "^${NEEDLE}.*:" ${LOCAL_DIR}/alias 2>/dev/null | cut -f2 -d':' 2>/dev/null) ALIASES_LINE=$(grep -E -e "^${NEEDLE}.*:" "${LOCAL_DIR}/alias" 2>/dev/null | cut -f2 -d':' 2>/dev/null)
if [[ -z "${ALIASES_LINE}" ]] if [[ -z "${ALIASES_LINE}" ]]
then then
@ -1128,6 +1146,7 @@ do
RECURSION_COUNT=$(( RECURSION_COUNT + 1 )) RECURSION_COUNT=$(( RECURSION_COUNT + 1 ))
EXPANDED_ALIASES=$(resolve_alias "${ALIAS}" ${RECURSION_COUNT}) EXPANDED_ALIASES=$(resolve_alias "${ALIAS}" ${RECURSION_COUNT})
RECURSION_COUNT=$(( RECURSION_COUNT - 1 )) RECURSION_COUNT=$(( RECURSION_COUNT - 1 ))
# shellcheck disable=SC2181
if (( $? == 0 )) if (( $? == 0 ))
then then
if [[ -z "${ALIAS_LIST}" ]] if [[ -z "${ALIAS_LIST}" ]]
@ -1166,18 +1185,20 @@ function merge_fragments
# initialize temporary working copy (need be different for each background job) # initialize temporary working copy (need be different for each background job)
# do not use 'mktemp' here as we need a fixed file name # do not use 'mktemp' here as we need a fixed file name
TMP_MERGE_FILE="$1/fragments" TMP_MERGE_FILE="$1/fragments"
: > ${TMP_MERGE_FILE} : > "${TMP_MERGE_FILE}"
(( $? )) && die "unable to create temporary file for mangling of 'fragments' file" # shellcheck disable=SC2181
(( $? > 0 )) && die "unable to create temporary file for mangling of 'fragments' file"
log "fragments are stored in a DIRECTORY, first merging all fragments into ${TMP_MERGE_FILE}" log "fragments are stored in a DIRECTORY, first merging all fragments into ${TMP_MERGE_FILE}"
# merge fragments with '%%%<file_name>' headers # merge fragments with '%%%<file_name>' headers
ls -1 ${FRAGS_DIR}/* | while read FILE # shellcheck disable=SC2012
ls -1 "${FRAGS_DIR}"/* | while read -r FILE
do do
# header first, file base name without extension # header first, file base name without extension
BASE_FILE=${FILE##*/} BASE_FILE="${FILE##*/}"
print "%%%${BASE_FILE%%.*}" >>${TMP_MERGE_FILE} print "%%%${BASE_FILE%%.*}" >>"${TMP_MERGE_FILE}"
# content next # content next
cat ${FILE} >>${TMP_MERGE_FILE} cat "${FILE}" >>"${TMP_MERGE_FILE}"
done done
# merge file should not be empty # merge file should not be empty
@ -1213,6 +1234,7 @@ do
if (( IS_TARGET > 0 )) if (( IS_TARGET > 0 ))
then then
EXPANDED_TARGETS=$(resolve_alias "${TARGET}" 0) EXPANDED_TARGETS=$(resolve_alias "${TARGET}" 0)
# shellcheck disable=SC2181
if (( $? == 0 )) if (( $? == 0 ))
then then
if [[ -z "${TARGETS_LIST}" ]] if [[ -z "${TARGETS_LIST}" ]]
@ -1236,6 +1258,7 @@ done
# sort final output and hand it back to the caller # sort final output and hand it back to the caller
print "${TARGETS_LIST}" | grep -v '^$' 2>/dev/null | sort -u 2>/dev/null print "${TARGETS_LIST}" | grep -v '^$' 2>/dev/null | sort -u 2>/dev/null
# shellcheck disable=SC2086
return $0 return $0
} }
@ -1260,26 +1283,26 @@ TRANSFER_FILE="${TRANSFER_FILE%!*}"
SOURCE_FILE="${TRANSFER_FILE##*/}" SOURCE_FILE="${TRANSFER_FILE##*/}"
# shellcheck disable=SC2164 # shellcheck disable=SC2164
OLD_PWD=$(pwd) OLD_PWD=$(pwd)
cd ${TRANSFER_DIR} || return 1 cd "${TRANSFER_DIR}" || return 1
# transfer, (possibly) chmod the file to/on the target server (keep STDERR) # transfer, (possibly) chmod the file to/on the target server (keep STDERR)
if (( DO_SFTP_CHMOD > 1 )) if (( DO_SFTP_CHMOD > 1 ))
then then
sftp ${SFTP_ARGS} ${SUDO_TRANSFER_USER}@${TRANSFER_HOST} >/dev/null <<EOT sftp ${SFTP_ARGS} "${SUDO_TRANSFER_USER}@${TRANSFER_HOST}" >/dev/null <<EOT
cd ${REMOTE_DIR} cd ${REMOTE_DIR}
put ${SOURCE_FILE} put ${SOURCE_FILE}
chmod ${TRANSFER_PERMS} ${SOURCE_FILE} chmod ${TRANSFER_PERMS} ${SOURCE_FILE}
EOT EOT
SFTP_RC=$? SFTP_RC=$?
else else
sftp ${SFTP_ARGS} ${SUDO_TRANSFER_USER}@${TRANSFER_HOST} >/dev/null <<EOT sftp ${SFTP_ARGS} "${SUDO_TRANSFER_USER}@${TRANSFER_HOST}" >/dev/null <<EOT
cd ${REMOTE_DIR} cd ${REMOTE_DIR}
put ${SOURCE_FILE} put ${SOURCE_FILE}
EOT EOT
SFTP_RC=$? SFTP_RC=$?
fi fi
cd ${OLD_PWD} || return 1 cd "${OLD_PWD}" || return 1
return ${SFTP_RC} return ${SFTP_RC}
} }
@ -1306,6 +1329,7 @@ else
return 1 return 1
else else
log "SSH agent started on ${HOST_NAME}:" log "SSH agent started on ${HOST_NAME}:"
# shellcheck disable=SC2086
log "$(ps -fp ${SSH_AGENT_PID})" log "$(ps -fp ${SSH_AGENT_PID})"
fi fi
fi fi
@ -1334,7 +1358,9 @@ if [[ -n "${SSH_AGENT_PID}" ]]
then then
# SIGTERM # SIGTERM
log "stopping (TERM) process on ${HOST_NAME} with PID: ${SSH_AGENT_PID}" log "stopping (TERM) process on ${HOST_NAME} with PID: ${SSH_AGENT_PID}"
# shellcheck disable=SC2086
log "$(ps -fp ${SSH_AGENT_PID})" log "$(ps -fp ${SSH_AGENT_PID})"
# shellcheck disable=SC2086
kill -s TERM ${SSH_AGENT_PID} kill -s TERM ${SSH_AGENT_PID}
sleep 3 sleep 3
@ -1342,7 +1368,9 @@ then
if (( $(pgrep -u "${USER}" ssh-agent | grep -c "${SSH_AGENT_PID}") )) if (( $(pgrep -u "${USER}" ssh-agent | grep -c "${SSH_AGENT_PID}") ))
then then
log "stopping (KILL) process on ${HOST_NAME} with PID: ${SSH_AGENT_PID}" log "stopping (KILL) process on ${HOST_NAME} with PID: ${SSH_AGENT_PID}"
# shellcheck disable=SC2086
log "$(ps -fp ${SSH_AGENT_PID})" log "$(ps -fp ${SSH_AGENT_PID})"
# shellcheck disable=SC2086
kill -s kill ${SSH_AGENT_PID} kill -s kill ${SSH_AGENT_PID}
fi fi
sleep 3 sleep 3
@ -1371,8 +1399,9 @@ typeset UPDATE_OPTS=""
typeset RC=0 typeset RC=0
# convert line to hostname # convert line to hostname
SERVER=${SERVER%%;*} SERVER="${SERVER%%;*}"
resolve_host ${SERVER} resolve_host "${SERVER}"
# shellcheck disable=SC2181
if (( $? > 0 )) if (( $? > 0 ))
then then
warn "could not lookup host ${SERVER}, skipping" warn "could not lookup host ${SERVER}, skipping"
@ -1388,25 +1417,29 @@ log "setting SUDO controls on ${SERVER} ..."
if [[ -z "${SUDO_UPDATE_USER}" ]] if [[ -z "${SUDO_UPDATE_USER}" ]]
then then
# own user w/ sudo # own user w/ sudo
( RC=0; ssh ${SSH_ARGS} ${SERVER} sudo -n ${REMOTE_DIR}/${SCRIPT_NAME} --update ${UPDATE_OPTS}; # shellcheck disable=SC2029
print "$?" > ${TMP_RC_FILE}; exit ( RC=0; ssh ${SSH_ARGS} "${SERVER}" "sudo -n ${REMOTE_DIR}/${SCRIPT_NAME} --update ${UPDATE_OPTS}";
) 2>&1 | logc print "$?" > "${TMP_RC_FILE}"; exit
) 2>&1 | logc ""
elif [[ "${SUDO_UPDATE_USER}" != "root" ]] elif [[ "${SUDO_UPDATE_USER}" != "root" ]]
then then
# other user w/ sudo # other user w/ sudo
( RC=0; ssh ${SSH_ARGS} ${SUDO_UPDATE_USER}@${SERVER} sudo -n ${REMOTE_DIR}/${SCRIPT_NAME} --update ${UPDATE_OPTS}; # shellcheck disable=SC2029
print "$?" > ${TMP_RC_FILE}; exit ( RC=0; ssh ${SSH_ARGS} "${SUDO_UPDATE_USER}@${SERVER}" "sudo -n ${REMOTE_DIR}/${SCRIPT_NAME} --update ${UPDATE_OPTS}";
) 2>&1 | logc print "$?" > "${TMP_RC_FILE}"; exit
) 2>&1 | logc ""
else else
# root user w/o sudo # root user w/o sudo
( RC=0; ssh ${SSH_ARGS} root@${SERVER} ${REMOTE_DIR}/${SCRIPT_NAME} --update ${UPDATE_OPTS}; # shellcheck disable=SC2029
print "$?" > ${TMP_RC_FILE}; exit ( RC=0; ssh ${SSH_ARGS} "root@${SERVER} ${REMOTE_DIR}/${SCRIPT_NAME} --update ${UPDATE_OPTS}";
) 2>&1 | logc print "$?" > "${TMP_RC_FILE}"; exit
) 2>&1 | logc ""
fi fi
# fetch return code from subshell # fetch return code from subshell
RC="$(< ${TMP_RC_FILE})" RC=$(< "${TMP_RC_FILE}")
# shellcheck disable=SC2086
return ${RC} return ${RC}
} }
@ -1420,8 +1453,9 @@ typeset UPDATE_OPTS=""
typeset RC=0 typeset RC=0
# convert line to hostname # convert line to hostname
SERVER=${SERVER%%;*} SERVER="${SERVER%%;*}"
resolve_host ${SERVER} resolve_host "${SERVER}"
# shellcheck disable=SC2181
if (( $? > 0 )) if (( $? > 0 ))
then then
warn "could not lookup host ${SERVER}, skipping" warn "could not lookup host ${SERVER}, skipping"
@ -1434,13 +1468,15 @@ then
fi fi
log "applying SUDO controls on ${SERVER} in slave mode, this may take a while ..." log "applying SUDO controls on ${SERVER} in slave mode, this may take a while ..."
( RC=0; ssh -A ${SSH_ARGS} ${SERVER} ${REMOTE_DIR}/${SCRIPT_NAME} --apply ${UPDATE_OPTS}; # shellcheck disable=SC2029
print "$?" > ${TMP_RC_FILE}; exit ( RC=0; ssh -A ${SSH_ARGS} "${SERVER}" "${REMOTE_DIR}/${SCRIPT_NAME} --apply ${UPDATE_OPTS}";
) 2>&1 | logc print "$?" > "${TMP_RC_FILE}"; exit
) 2>&1 | logc ""
# fetch return code from subshell # fetch return code from subshell
RC="$(< ${TMP_RC_FILE})" RC=$(< "${TMP_RC_FILE}")
# shellcheck disable=SC2086
return ${RC} return ${RC}
} }
@ -1461,6 +1497,7 @@ do
do do
shift shift
# child is still alive? # child is still alive?
# shellcheck disable=SC2086
if kill -0 ${PID} 2>/dev/null if kill -0 ${PID} 2>/dev/null
then then
(( ARG_DEBUG > 0 )) && print -u2 "DEBUG: ${PID} is still alive" (( ARG_DEBUG > 0 )) && print -u2 "DEBUG: ${PID} is still alive"
@ -1468,6 +1505,7 @@ do
# wait for sigchild, catching child exit codes is unreliable because # wait for sigchild, catching child exit codes is unreliable because
# the child might have already ended before we get here (caveat emptor) # the child might have already ended before we get here (caveat emptor)
else else
# shellcheck disable=SC2086
wait ${PID} wait ${PID}
RC=$? RC=$?
if (( RC > 0 )) if (( RC > 0 ))
@ -1499,7 +1537,7 @@ if [[ -n "$1" ]]
then then
if (( ARG_LOG > 0 )) if (( ARG_LOG > 0 ))
then then
print - "$*" | while read LOG_LINE print - "$*" | while read -r LOG_LINE
do do
# check for leading log sigils and retain them # check for leading log sigils and retain them
case "${LOG_LINE}" in case "${LOG_LINE}" in
@ -1519,12 +1557,12 @@ then
LOG_SIGIL="WARN" LOG_SIGIL="WARN"
;; ;;
esac esac
print "${NOW}: ${LOG_SIGIL}: [$$]:" "${LOG_LINE}" >>${LOG_FILE} print "${NOW}: ${LOG_SIGIL}: [$$]:" "${LOG_LINE}" >>"${LOG_FILE}"
done done
fi fi
if (( ARG_VERBOSE > 0 )) if (( ARG_VERBOSE > 0 ))
then then
print - "$*" | while read LOG_LINE print - "$*" | while read -r LOG_LINE
do do
# check for leading log sigils and retain them # check for leading log sigils and retain them
case "${LOG_LINE}" in case "${LOG_LINE}" in
@ -1762,6 +1800,7 @@ case ${ARG_ACTION} in
then then
die "no targets to process" die "no targets to process"
else else
# shellcheck disable=SC2086
log "processing targets: $(print ${CLIENTS} | tr -s '\n' ' ' 2>/dev/null)" log "processing targets: $(print ${CLIENTS} | tr -s '\n' ' ' 2>/dev/null)"
fi fi
@ -1769,6 +1808,7 @@ case ${ARG_ACTION} in
if (( DO_SSH_AGENT > 0 && CAN_START_AGENT > 0 )) if (( DO_SSH_AGENT > 0 && CAN_START_AGENT > 0 ))
then then
start_ssh_agent start_ssh_agent
# shellcheck disable=SC2181
if (( $? > 0 )) if (( $? > 0 ))
then then
die "problem with launching an SSH agent, bailing out" die "problem with launching an SSH agent, bailing out"
@ -1781,9 +1821,9 @@ case ${ARG_ACTION} in
do do
if (( DO_SLAVE > 0 )) if (( DO_SLAVE > 0 ))
then then
update2slave ${CLIENT} & update2slave "${CLIENT}" &
else else
update2host ${CLIENT} & update2host "${CLIENT}" &
fi fi
PID=$! PID=$!
log "updating ${CLIENT} in background [PID=${PID}] ..." log "updating ${CLIENT} in background [PID=${PID}] ..."
@ -1793,6 +1833,7 @@ case ${ARG_ACTION} in
if (( COUNT <= 0 )) if (( COUNT <= 0 ))
then then
# wait until all background processes are completed # wait until all background processes are completed
# shellcheck disable=SC2086
wait_for_children ${PIDS} || \ wait_for_children ${PIDS} || \
warn "$? background jobs (possibly) failed to complete correctly" warn "$? background jobs (possibly) failed to complete correctly"
PIDS='' PIDS=''
@ -1801,6 +1842,7 @@ case ${ARG_ACTION} in
fi fi
done done
# final wait for background processes to be finished completely # final wait for background processes to be finished completely
# shellcheck disable=SC2086
wait_for_children ${PIDS} || \ wait_for_children ${PIDS} || \
warn "$? background jobs (possibly) failed to complete correctly" warn "$? background jobs (possibly) failed to complete correctly"
# stop SSH agent if needed # stop SSH agent if needed
@ -1822,6 +1864,7 @@ case ${ARG_ACTION} in
then then
die "no targets to process" die "no targets to process"
else else
# shellcheck disable=SC2086
log "processing targets: $(print ${CLIENTS} | tr -s '\n' ' ' 2>/dev/null)" log "processing targets: $(print ${CLIENTS} | tr -s '\n' ' ' 2>/dev/null)"
fi fi
@ -1829,6 +1872,7 @@ case ${ARG_ACTION} in
if (( DO_SSH_AGENT > 0 && CAN_START_AGENT > 0 )) if (( DO_SSH_AGENT > 0 && CAN_START_AGENT > 0 ))
then then
start_ssh_agent start_ssh_agent
# shellcheck disable=SC2181
if (( $? > 0 )) if (( $? > 0 ))
then then
die "problem with launching an SSH agent, bailing out" die "problem with launching an SSH agent, bailing out"
@ -1841,9 +1885,9 @@ case ${ARG_ACTION} in
do do
if (( DO_SLAVE )) if (( DO_SLAVE ))
then then
distribute2slave ${CLIENT} & distribute2slave "${CLIENT}" &
else else
distribute2host ${CLIENT} & distribute2host "${CLIENT}" &
fi fi
PID=$! PID=$!
log "copying/distributing to ${CLIENT} in background [PID=${PID}] ..." log "copying/distributing to ${CLIENT} in background [PID=${PID}] ..."
@ -1853,6 +1897,7 @@ case ${ARG_ACTION} in
if (( COUNT <= 0 )) if (( COUNT <= 0 ))
then then
# wait until all background processes are completed # wait until all background processes are completed
# shellcheck disable=SC2086
wait_for_children ${PIDS} || \ wait_for_children ${PIDS} || \
warn "$? background jobs (possibly) failed to complete correctly" warn "$? background jobs (possibly) failed to complete correctly"
PIDS='' PIDS=''
@ -1861,6 +1906,7 @@ case ${ARG_ACTION} in
fi fi
done done
# final wait for background processes to be finished completely # final wait for background processes to be finished completely
# shellcheck disable=SC2086
wait_for_children ${PIDS} || \ wait_for_children ${PIDS} || \
warn "$? background jobs (possibly) failed to complete correctly" warn "$? background jobs (possibly) failed to complete correctly"
# stop SSH agent if needed # stop SSH agent if needed
@ -1874,28 +1920,32 @@ case ${ARG_ACTION} in
if [[ -n "${FRAGS_DIR}" ]] if [[ -n "${FRAGS_DIR}" ]]
then then
TMP_WORK_DIR="${TMP_DIR}/$0.${RANDOM}" TMP_WORK_DIR="${TMP_DIR}/$0.${RANDOM}"
mkdir -p ${TMP_WORK_DIR} mkdir -p "${TMP_WORK_DIR}"
if (( $? )) # shellcheck disable=SC2181
if (( $? > 0 ))
then then
die "unable to create temporary directory ${TMP_WORK_DIR} for mangling of 'fragments' file" die "unable to create temporary directory ${TMP_WORK_DIR} for mangling of 'fragments' file"
fi fi
merge_fragments ${TMP_WORK_DIR} merge_fragments "${TMP_WORK_DIR}"
fi fi
# remove '%%%' headers # remove '%%%' headers
TMP_SCAN_FILE=$(mktemp) TMP_SCAN_FILE=$(mktemp)
(( $? )) && die "unable to create temporary file for validation of 'fragments' file(s)" # shellcheck disable=SC2181
(( $? > 0 )) && die "unable to create temporary file for validation of 'fragments' file(s)"
if [[ -n "${FRAGS_DIR}" ]] if [[ -n "${FRAGS_DIR}" ]]
then then
cat ${TMP_MERGE_FILE} | grep -v '^%%%' >${TMP_SCAN_FILE} grep -v '^%%%' "${TMP_MERGE_FILE}" 2>/dev/null >"${TMP_SCAN_FILE}"
[[ -d ${TMP_WORK_DIR} ]] && rm -rf ${TMP_WORK_DIR} 2>/dev/null [[ -d "${TMP_WORK_DIR}" ]] && rm -rf "${TMP_WORK_DIR}" 2>/dev/null
else else
cat ${FRAGS_FILE} | grep -v '^%%%' >${TMP_SCAN_FILE} grep -v '^%%%' "${FRAGS_FILE}" 2>/dev/null >"${TMP_SCAN_FILE}"
fi fi
# run syntax check # run syntax check
if (( CAN_CHECK_SYNTAX )) if (( CAN_CHECK_SYNTAX ))
then then
# shellcheck disable=SC2086
CHECK_RESULT="$(${VISUDO_BIN} -c -f ${TMP_SCAN_FILE} 2>/dev/null)" CHECK_RESULT="$(${VISUDO_BIN} -c -f ${TMP_SCAN_FILE} 2>/dev/null)"
if (( $? )) # shellcheck disable=SC2181
if (( $? > 0 ))
then then
warn "SUDO syntax check: FAILED: ${CHECK_RESULT})" warn "SUDO syntax check: FAILED: ${CHECK_RESULT})"
CAN_REMOVE_TEMP=0 CAN_REMOVE_TEMP=0
@ -1907,11 +1957,11 @@ case ${ARG_ACTION} in
;; ;;
4) # apply SUDO controls locally (root user) 4) # apply SUDO controls locally (root user)
log "ACTION: apply SUDO controls locally" log "ACTION: apply SUDO controls locally"
( RC=0; ${LOCAL_DIR}/update_sudo.pl ${SUDO_UPDATE_OPTS}; ( RC=0; "${LOCAL_DIR}/update_sudo.pl" ${SUDO_UPDATE_OPTS};
print "$?" > ${TMP_RC_FILE}; exit print "$?" > "${TMP_RC_FILE}"; exit
) 2>&1 | logc ) 2>&1 | logc ""
# fetch return code from subshell # fetch return code from subshell
RC="$(< ${TMP_RC_FILE})" RC=$(< "${TMP_RC_FILE}")
if (( RC > 0 )) if (( RC > 0 ))
then then
die "failed to apply SUDO controls locally [RC=${RC}]" die "failed to apply SUDO controls locally [RC=${RC}]"
@ -2043,6 +2093,7 @@ case ${ARG_ACTION} in
then then
die "no targets to process" die "no targets to process"
else else
# shellcheck disable=SC2086
log "processing targets: $(print ${CLIENTS} | tr -s '\n' ' ' 2>/dev/null)" log "processing targets: $(print ${CLIENTS} | tr -s '\n' ' ' 2>/dev/null)"
fi fi
@ -2050,6 +2101,7 @@ case ${ARG_ACTION} in
if (( DO_SSH_AGENT > 0 && CAN_START_AGENT > 0 )) if (( DO_SSH_AGENT > 0 && CAN_START_AGENT > 0 ))
then then
start_ssh_agent start_ssh_agent
# shellcheck disable=SC2181
if (( $? > 0 )) if (( $? > 0 ))
then then
die "problem with launching an SSH agent, bailing out" die "problem with launching an SSH agent, bailing out"
@ -2062,9 +2114,9 @@ case ${ARG_ACTION} in
do do
if (( DO_SLAVE > 0 )) if (( DO_SLAVE > 0 ))
then then
fix2slave ${CLIENT} "${FIX_DIR}" "${SUDO_UPDATE_USER}" & fix2slave "${CLIENT}" "${FIX_DIR}" "${SUDO_UPDATE_USER}" &
else else
fix2host ${CLIENT} "${FIX_DIR}" "${SUDO_UPDATE_USER}" & fix2host "${CLIENT}" "${FIX_DIR}" "${SUDO_UPDATE_USER}" &
fi fi
PID=$! PID=$!
log "fixing SUDO controls on ${CLIENT} in background [PID=${PID}] ..." log "fixing SUDO controls on ${CLIENT} in background [PID=${PID}] ..."
@ -2074,6 +2126,7 @@ case ${ARG_ACTION} in
if (( COUNT <= 0 )) if (( COUNT <= 0 ))
then then
# wait until all background processes are completed # wait until all background processes are completed
# shellcheck disable=SC2086
wait_for_children ${PIDS} || \ wait_for_children ${PIDS} || \
warn "$? background jobs (possibly) failed to complete correctly" warn "$? background jobs (possibly) failed to complete correctly"
PIDS='' PIDS=''
@ -2091,7 +2144,7 @@ case ${ARG_ACTION} in
;; ;;
7) # dump the configuration namespace 7) # dump the configuration namespace
log "ACTION: dumping the global grants namespace with resolved aliases ..." log "ACTION: dumping the global grants namespace with resolved aliases ..."
${LOCAL_DIR}/update_sudo.pl --preview --global "${LOCAL_DIR}/update_sudo.pl" --preview --global
log "finished dumping the global namespace" log "finished dumping the global namespace"
;; ;;
8) # check syntax of the grants/alias files 8) # check syntax of the grants/alias files
@ -2112,16 +2165,21 @@ case ${ARG_ACTION} in
# fragments files # fragments files
if [[ -n "${FRAGS_DIR}" ]] if [[ -n "${FRAGS_DIR}" ]]
then then
# shellcheck disable=SC2086
log "$(tar -cvf ${BACKUP_TAR_FILE} ${FRAGS_DIR} 2>/dev/null)" log "$(tar -cvf ${BACKUP_TAR_FILE} ${FRAGS_DIR} 2>/dev/null)"
else else
# shellcheck disable=SC2086
log "$(tar -cvf ${BACKUP_TAR_FILE} ${FRAGS_FILE} 2>/dev/null)" log "$(tar -cvf ${BACKUP_TAR_FILE} ${FRAGS_FILE} 2>/dev/null)"
fi fi
# configuration files # configuration files
for FILE in "${LOCAL_DIR}/grants" "${LOCAL_DIR}/alias ${LOCAL_DIR}/targets" for FILE in "${LOCAL_DIR}/grants" "${LOCAL_DIR}/alias ${LOCAL_DIR}/targets"
do do
# shellcheck disable=SC2086
log "$(tar -rvf ${BACKUP_TAR_FILE} ${FILE} 2>/dev/null)" log "$(tar -rvf ${BACKUP_TAR_FILE} ${FILE} 2>/dev/null)"
done done
# shellcheck disable=SC2086
log "$(gzip ${BACKUP_TAR_FILE} 2>/dev/null)" log "$(gzip ${BACKUP_TAR_FILE} 2>/dev/null)"
# shellcheck disable=SC2086
log "resulting backup file is: $(ls -1 ${BACKUP_TAR_FILE}* 2>/dev/null)" log "resulting backup file is: $(ls -1 ${BACKUP_TAR_FILE}* 2>/dev/null)"
else else
die "could not find backup directory ${BACKUP_DIR}. Host is not an SUDO master?" die "could not find backup directory ${BACKUP_DIR}. Host is not an SUDO master?"
@ -2137,6 +2195,7 @@ case ${ARG_ACTION} in
then then
die "no targets to process" die "no targets to process"
else else
# shellcheck disable=SC2086
log "processing targets: $(print ${CLIENTS} | tr -s '\n' ' ' 2>/dev/null)" log "processing targets: $(print ${CLIENTS} | tr -s '\n' ' ' 2>/dev/null)"
fi fi
print "${CLIENTS}" | ${SSH_KEYSCAN_BIN} ${SSH_KEYSCAN_ARGS} -f - 2>/dev/null print "${CLIENTS}" | ${SSH_KEYSCAN_BIN} ${SSH_KEYSCAN_ARGS} -f - 2>/dev/null
@ -2146,10 +2205,12 @@ case ${ARG_ACTION} in
11) # resolve an alias 11) # resolve an alias
log "ACTION: resolving alias ${ARG_ALIAS} ..." log "ACTION: resolving alias ${ARG_ALIAS} ..."
RESOLVE_ALIAS=$(resolve_alias "${ARG_ALIAS}" 0) RESOLVE_ALIAS=$(resolve_alias "${ARG_ALIAS}" 0)
# shellcheck disable=SC2181
if (( $? > 0 )) && [[ -z "${RESOLVE_ALIAS}" ]] if (( $? > 0 )) && [[ -z "${RESOLVE_ALIAS}" ]]
then then
die "alias ${ARG_ALIAS} did not resolve correctly" die "alias ${ARG_ALIAS} did not resolve correctly"
else else
# shellcheck disable=SC2086
log "alias ${ARG_ALIAS} resolves to: $(print ${RESOLVE_ALIAS} | tr -s '\n' ' ' 2>/dev/null)" log "alias ${ARG_ALIAS} resolves to: $(print ${RESOLVE_ALIAS} | tr -s '\n' ' ' 2>/dev/null)"
fi fi
log "finished resolving alias" log "finished resolving alias"

View File

@ -44,7 +44,7 @@ use File::Temp qw(tempfile);
# ------------------------- CONFIGURATION starts here ------------------------- # ------------------------- CONFIGURATION starts here -------------------------
# define the version (YYYY-MM-DD) # define the version (YYYY-MM-DD)
my $script_version = "2018-11-03"; my $script_version = "2020-12-30";
# name of global configuration file (no path, must be located in the script directory) # name of global configuration file (no path, must be located in the script directory)
my $global_config_file = "update_sudo.conf"; my $global_config_file = "update_sudo.conf";
# name of localized configuration file (no path, must be located in the script directory) # name of localized configuration file (no path, must be located in the script directory)
@ -98,7 +98,7 @@ sub parse_config_file {
if (/^\s*$/ || /^#/) { if (/^\s*$/ || /^#/) {
next; next;
} else { } else {
if (/^\s*use_fqdn\s*=\s*([0-9]+)\s*$/) { if (/^\s*use_fqdn\s*=\s*(0|1)\s*$/) {
$use_fqdn = $1; $use_fqdn = $1;
do_log ("DEBUG: picking up setting: use_fqdn=${use_fqdn}"); do_log ("DEBUG: picking up setting: use_fqdn=${use_fqdn}");
} }
@ -229,7 +229,7 @@ $verbose = 1 if ($options{'verbose'});
# where am I? (1/2) # where am I? (1/2)
$0 =~ /^(.+[\\\/])[^\\\/]+[\\\/]*$/; $0 =~ /^(.+[\\\/])[^\\\/]+[\\\/]*$/;
my $run_dir = $1 || "."; $run_dir = $1 || ".";
$run_dir =~ s#/$##; # remove trailing slash $run_dir =~ s#/$##; # remove trailing slash
# don't do anything without configuration file(s) # don't do anything without configuration file(s)
@ -445,7 +445,7 @@ foreach my $frag_file (@frag_files) {
# strip off path from file name for hash key # strip off path from file name for hash key
$frag_file = fileparse ($frag_file, qr/\.[^.]*/); $frag_file = fileparse ($frag_file, qr/\.[^.]*/);
do_log ("INFO: fragment file $frag_file contains only 1 fragment on $hostname"); 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); close (FRAGS);
} }
@ -692,7 +692,7 @@ update_sudo.pl - distributes SUDO fragments according to a desired state model.
B<update_sudo.pl> distributes SUDO fragments into the C<$fragments_dir> repository based on the F<grants>, F<alias> and F<fragments> files. 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.
For update SUDO fragments must be stored in a generic F<fragments> file within the same directory as B<update_sudo.pl> script. 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>. 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. Both methods are mutually exclusive and the latter always take precedence.