* added support for standard home directory location for public keys ($HOME/.ssh
). Set $key_location=use_sshd
in update_ssh.conf[.local]
with AuthorizedKeysFile
set to the default value in sshd_config
(or use the value .ssh/authorized_keys
).
* added support for SELinux (CentOS/RHEL 8.x) * various fixes (incl. shellcheck + quoting)
This commit is contained in:
parent
b8004afe62
commit
77a332e324
11
README.md
11
README.md
@ -1,5 +1,14 @@
|
|||||||
<p align="center"><img src="logo.png" alt="SSH Controls Logo"></p>
|
<p align="center"><img src="logo.png" alt="SSH Controls Logo"></p>
|
||||||
|
|
||||||
|
## What's new
|
||||||
|
|
||||||
|
:loudspeaker: **30/12/2020**:
|
||||||
|
* added support for standard home directory location for public keys (`$HOME/.ssh`). Set `$key_location=use_sshd` in `update_ssh.conf[.local]` with `AuthorizedKeysFile` set to the default value in `sshd_config` (or use the value `.ssh/authorized_keys`). *Caveat*: SSH Controls will not create parent nor intermediate directories in the public key file path if they are missing.
|
||||||
|
* added support for SELinux (CentOS/RHEL 8.x)
|
||||||
|
* various fixes
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
SSH Controls is a light-weight SSH **public key** distribution & management framework
|
SSH Controls is a light-weight SSH **public key** distribution & management framework
|
||||||
|
|
||||||
* uses a **desired state** model: SSH Controls *pushes* public keys from a key master (or slave) server onto client host(s) and applies them according to the central configuration.
|
* uses a **desired state** model: SSH Controls *pushes* public keys from a key master (or slave) server onto client host(s) and applies them according to the central configuration.
|
||||||
@ -8,7 +17,7 @@ SSH Controls is a light-weight SSH **public key** distribution & management fram
|
|||||||
|
|
||||||
* supports a **Master→Slave→Client** model so that information can be propagated within more complex LAN set-ups.
|
* supports a **Master→Slave→Client** model so that information can be propagated within more complex LAN set-ups.
|
||||||
|
|
||||||
* **shields** public keys from owners/users on client systems: SSH Controls requires the standard `sshd_config` to be reconfigured with an alternate path for the `AuthorizedKeysFile` setting so that public keys are stored in common location which cannot be manipulated by the owners of the public keys. This allows for more administrative control and better security.
|
* can **shield** public keys from owners/users on client systems: SSH Controls may require the standard `sshd_config` to be reconfigured with an alternate path for the `AuthorizedKeysFile` setting so that public keys are stored in common location which cannot be manipulated by the owners of the public keys. This allows for more administrative control and better security.
|
||||||
|
|
||||||
* performs operations with **least privileges**: copy/distribute operations are performed with a low-privileged account. Only the actual key updates requires super-user privileges which need to be configured via SUDO.
|
* performs operations with **least privileges**: copy/distribute operations are performed with a low-privileged account. Only the actual key updates requires super-user privileges which need to be configured via SUDO.
|
||||||
|
|
||||||
|
269
manage_ssh.sh
269
manage_ssh.sh
@ -43,7 +43,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_ssh.conf"
|
typeset -r GLOBAL_CONFIG_FILE="manage_ssh.conf"
|
||||||
# name of the local configuration file (script)
|
# name of the local configuration file (script)
|
||||||
@ -289,11 +289,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
|
||||||
@ -333,7 +333,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
|
||||||
@ -427,6 +427,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}"
|
||||||
@ -457,24 +458,25 @@ typeset KEY_FILE=""
|
|||||||
typeset KEY_FIELDS=""
|
typeset KEY_FIELDS=""
|
||||||
|
|
||||||
# access should have 3 fields
|
# access should have 3 fields
|
||||||
grep -v -E -e '^#|^$' "${LOCAL_DIR}/access" 2>/dev/null | while read ACCESS_LINE
|
grep -v -E -e '^#|^$' "${LOCAL_DIR}/access" 2>/dev/null | while read -r ACCESS_LINE
|
||||||
do
|
do
|
||||||
ACCESS_FIELDS=$(count_fields "${ACCESS_LINE}" ":")
|
ACCESS_FIELDS=$(count_fields "${ACCESS_LINE}" ":")
|
||||||
(( ACCESS_FIELDS != 3 )) && die "line '${ACCESS_LINE}' in access file has missing or too many field(s) (should be 3)"
|
(( ACCESS_FIELDS != 3 )) && die "line '${ACCESS_LINE}' in access file has missing or too many field(s) (should be 3)"
|
||||||
done
|
done
|
||||||
|
|
||||||
# alias should have 2 fields
|
# alias should have 2 fields
|
||||||
grep -v -E -e '^#|^$' "${LOCAL_DIR}/alias" 2>/dev/null | while read ALIASES_LINE
|
grep -v -E -e '^#|^$' "${LOCAL_DIR}/alias" 2>/dev/null | while read -r ALIASES_LINE
|
||||||
do
|
do
|
||||||
ALIAS_FIELDS=$(count_fields "${ALIASES_LINE}" ":")
|
ALIAS_FIELDS=$(count_fields "${ALIASES_LINE}" ":")
|
||||||
(( ALIAS_FIELDS != 2 )) && die "line '${ALIASES_LINE}' in alias file has missing or too many field(s) (should be 2)"
|
(( ALIAS_FIELDS != 2 )) && die "line '${ALIASES_LINE}' in alias file has missing or too many field(s) (should be 2)"
|
||||||
done
|
done
|
||||||
|
|
||||||
# key files should have 3 fields
|
# key files should have 3 fields
|
||||||
ls -1 ${LOCAL_DIR}/keys.d/* ${LOCAL_DIR}/keys 2>/dev/null | while read KEY_FILE
|
# shellcheck disable=SC2012
|
||||||
|
ls -1 "${LOCAL_DIR}"/keys.d/* "${LOCAL_DIR}"/keys 2>/dev/null | while read -r KEY_FILE
|
||||||
do
|
do
|
||||||
grep -v -E -e '^#|^$' ${KEY_FILE} 2>/dev/null |\
|
grep -v -E -e '^#|^$' "${KEY_FILE}" 2>/dev/null |\
|
||||||
while read KEY_LINE
|
while read -r KEY_LINE
|
||||||
do
|
do
|
||||||
KEY_FIELDS=$(count_fields "${KEY_LINE}" ",")
|
KEY_FIELDS=$(count_fields "${KEY_LINE}" ",")
|
||||||
(( KEY_FIELDS != 3 )) && die "line '${KEY_LINE}' in a keys file has missing or too many field(s) (should be 3)"
|
(( KEY_FIELDS != 3 )) && die "line '${KEY_LINE}' in a keys file has missing or too many field(s) (should be 3)"
|
||||||
@ -530,7 +532,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)
|
||||||
|
|
||||||
print ${NUM_FIELDS}
|
print "${NUM_FIELDS}"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
@ -547,7 +549,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
|
||||||
@ -567,10 +569,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
|
||||||
@ -622,6 +624,7 @@ Parameters:
|
|||||||
--fix-local : fix permissions on the local SSH controls repository
|
--fix-local : fix permissions on the local SSH controls repository
|
||||||
(local SSH controls repository given by --fix-dir)
|
(local SSH controls repository given by --fix-dir)
|
||||||
--fix-remote : fix permissions on the remote SSH controls repository
|
--fix-remote : fix permissions on the remote SSH controls repository
|
||||||
|
(only valud when using a centralized public key location)
|
||||||
--fix-user : UNIX account to own SSH controls files [default: current user]
|
--fix-user : UNIX account to own SSH controls files [default: current user]
|
||||||
--help|-h : this help text
|
--help|-h : this help text
|
||||||
--local-dir : location of the SSH control files on the local filesystem.
|
--local-dir : location of the SSH control files on the local filesystem.
|
||||||
@ -668,8 +671,9 @@ typeset TMP_WORK_DIR=""
|
|||||||
typeset BLACKLIST_FILE=""
|
typeset BLACKLIST_FILE=""
|
||||||
|
|
||||||
# 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"
|
||||||
@ -686,10 +690,11 @@ for FILE in "${LOCAL_DIR}/access!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
|
||||||
|
# shellcheck disable=SC2086
|
||||||
log "transferred ${FILE%!*} to ${SERVER}:${REMOTE_DIR}"
|
log "transferred ${FILE%!*} to ${SERVER}:${REMOTE_DIR}"
|
||||||
else
|
else
|
||||||
warn "failed to transfer ${FILE%!*} to ${SERVER}:${REMOTE_DIR} [RC=${COPY_RC}]"
|
warn "failed to transfer ${FILE%!*} to ${SERVER}:${REMOTE_DIR} [RC=${COPY_RC}]"
|
||||||
@ -702,16 +707,17 @@ if [[ -n "${KEYS_DIR}" ]]
|
|||||||
then
|
then
|
||||||
# merge keys file(s) before copy (in a temporary location)
|
# merge keys file(s) before copy (in a temporary location)
|
||||||
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 'keys' file"
|
die "unable to create temporary directory ${TMP_WORK_DIR} for mangling of 'keys' file"
|
||||||
fi
|
fi
|
||||||
TMP_MERGE_FILE="${TMP_WORK_DIR}/keys"
|
TMP_MERGE_FILE="${TMP_WORK_DIR}/keys"
|
||||||
log "keys are stored in a DIRECTORY, first merging all keys into ${TMP_MERGE_FILE}"
|
log "keys are stored in a DIRECTORY, first merging all keys into ${TMP_MERGE_FILE}"
|
||||||
cat ${KEYS_DIR}/* >${TMP_MERGE_FILE}
|
cat "${KEYS_DIR}"/* >"${TMP_MERGE_FILE}"
|
||||||
# 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
|
||||||
@ -720,9 +726,9 @@ then
|
|||||||
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 "${KEYS_FILE}!640" ${SERVER}
|
sftp_file "${KEYS_FILE}!640" "${SERVER}"
|
||||||
COPY_RC=$?
|
COPY_RC=$?
|
||||||
if (( COPY_RC == 0 ))
|
if (( COPY_RC == 0 ))
|
||||||
then
|
then
|
||||||
@ -735,14 +741,14 @@ fi
|
|||||||
# discover a keys blacklist file, also copy it across if we find one
|
# discover a keys blacklist file, also copy it across if we find one
|
||||||
# never use a keys blacklist file from the local config though
|
# never use a keys blacklist file from the local config though
|
||||||
[[ -r ${LOCAL_DIR}/update_ssh.conf ]] && \
|
[[ -r ${LOCAL_DIR}/update_ssh.conf ]] && \
|
||||||
BLACKLIST_FILE="$(grep -E -e '^blacklist_file' ${LOCAL_DIR}/update_ssh.conf 2>/dev/null | cut -f2 -d'=')"
|
BLACKLIST_FILE=$(grep -E -e '^blacklist_file' "${LOCAL_DIR}/update_ssh.conf" 2>/dev/null | cut -f2 -d'=')
|
||||||
if [[ -n "${BLACKLIST_FILE}" ]]
|
if [[ -n "${BLACKLIST_FILE}" ]]
|
||||||
then
|
then
|
||||||
if [[ -r "${BLACKLIST_FILE}" ]]
|
if [[ -r "${BLACKLIST_FILE}" ]]
|
||||||
then
|
then
|
||||||
log "keys blacklist file found at ${BLACKLIST_FILE}"
|
log "keys blacklist file found at ${BLACKLIST_FILE}"
|
||||||
# sftp transfer
|
# sftp transfer
|
||||||
sftp_file "${BLACKLIST_FILE}!660" ${SERVER}
|
sftp_file "${BLACKLIST_FILE}!660" "${SERVER}"
|
||||||
COPY_RC=$?
|
COPY_RC=$?
|
||||||
if (( COPY_RC == 0 ))
|
if (( COPY_RC == 0 ))
|
||||||
then
|
then
|
||||||
@ -768,8 +774,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"
|
||||||
@ -782,13 +789,15 @@ then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
log "copying SSH controls on ${SERVER} in slave mode, this may take a while ..."
|
log "copying SSH 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}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -799,9 +808,9 @@ 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
|
||||||
log "*** finish of ${SCRIPT_NAME} [${CMD_LINE}] /$$@${HOST_NAME}/ ***"
|
log "*** finish of ${SCRIPT_NAME} [${CMD_LINE}] /$$@${HOST_NAME}/ ***"
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
@ -820,8 +829,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"
|
||||||
@ -842,25 +852,29 @@ log "fixing SSH controls on ${SERVER} ..."
|
|||||||
if [[ -z "${SSH_UPDATE_USER}" ]]
|
if [[ -z "${SSH_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 [[ "${SSH_UPDATE_USER}" != "root" ]]
|
elif [[ "${SSH_UPDATE_USER}" != "root" ]]
|
||||||
then
|
then
|
||||||
# other user w/ sudo
|
# other user w/ sudo
|
||||||
( RC=0; ssh ${SSH_ARGS} ${SSH_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} "${SSH_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}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -877,8 +891,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"
|
||||||
@ -896,13 +911,15 @@ then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
log "fixing SSH controls on ${SERVER} in slave mode, this may take a while ..."
|
log "fixing SSH 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}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -941,6 +958,9 @@ then
|
|||||||
*release\ 7*)
|
*release\ 7*)
|
||||||
RHEL_VERSION=7
|
RHEL_VERSION=7
|
||||||
;;
|
;;
|
||||||
|
*release\ 7*)
|
||||||
|
RHEL_VERSION=8
|
||||||
|
;;
|
||||||
*)
|
*)
|
||||||
RHEL_VERSION=""
|
RHEL_VERSION=""
|
||||||
;;
|
;;
|
||||||
@ -967,7 +987,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
|
||||||
@ -987,12 +1007,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
|
||||||
@ -1027,7 +1047,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
|
||||||
@ -1047,12 +1067,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
|
||||||
@ -1072,7 +1092,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
|
||||||
@ -1092,12 +1112,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*)
|
||||||
@ -1138,7 +1158,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
|
||||||
@ -1159,6 +1179,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}" ]]
|
||||||
@ -1217,6 +1238,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}" ]]
|
||||||
@ -1240,6 +1262,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
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1264,26 +1287,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} ${SSH_TRANSFER_USER}@${TRANSFER_HOST} >/dev/null <<EOT
|
sftp ${SFTP_ARGS} "${SSH_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} ${SSH_TRANSFER_USER}@${TRANSFER_HOST} >/dev/null <<EOT
|
sftp ${SFTP_ARGS} "${SSH_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}
|
||||||
}
|
}
|
||||||
@ -1310,6 +1333,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
|
||||||
@ -1338,7 +1362,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
|
||||||
|
|
||||||
@ -1346,7 +1372,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
|
||||||
@ -1375,8 +1403,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"
|
||||||
@ -1392,25 +1421,29 @@ log "setting SSH controls on ${SERVER} ..."
|
|||||||
if [[ -z "${SSH_UPDATE_USER}" ]]
|
if [[ -z "${SSH_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 [[ "${SSH_UPDATE_USER}" != "root" ]]
|
elif [[ "${SSH_UPDATE_USER}" != "root" ]]
|
||||||
then
|
then
|
||||||
# other user w/ sudo
|
# other user w/ sudo
|
||||||
( RC=0; ssh ${SSH_ARGS} ${SSH_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} "${SSH_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}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1424,8 +1457,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"
|
||||||
@ -1438,13 +1472,15 @@ then
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
log "applying SSH controls on ${SERVER} in slave mode, this may take a while ..."
|
log "applying SSH 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}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1467,18 +1503,20 @@ FINGER_FIELDS=$(count_fields "${FINGER_LINE}" ",")
|
|||||||
(( FINGER_FIELDS != 3 )) && die "line '${FINGER_LINE}' has missing or too many field(s) (should be 3))"
|
(( FINGER_FIELDS != 3 )) && die "line '${FINGER_LINE}' has missing or too many field(s) (should be 3))"
|
||||||
|
|
||||||
# create fingerprint
|
# create fingerprint
|
||||||
FINGER_USER="$(print ${FINGER_LINE} | awk -F, '{print $1}')"
|
FINGER_USER=$(print "${FINGER_LINE}" | awk -F, '{print $1}')
|
||||||
print "${FINGER_LINE}" | awk -F, '{print $2 " " $3}' > ${TMP_FILE}
|
print "${FINGER_LINE}" | awk -F, '{print $2 " " $3}' > "${TMP_FILE}"
|
||||||
# check if fingerprint is valid
|
# check if fingerprint is valid
|
||||||
FINGERPRINT="$(ssh-keygen ${SSH_KEYGEN_OPTS} -l -f ${TMP_FILE} 2>&1)"
|
FINGERPRINT=$(ssh-keygen ${SSH_KEYGEN_OPTS} -l -f "${TMP_FILE}" 2>&1)
|
||||||
FINGER_RC=$?
|
FINGER_RC=$?
|
||||||
if (( FINGER_RC == 0 ))
|
if (( FINGER_RC == 0 ))
|
||||||
then
|
then
|
||||||
case "${OS_NAME}" in
|
case "${OS_NAME}" in
|
||||||
HP-UX)
|
HP-UX)
|
||||||
|
# shellcheck disable=SC2086
|
||||||
FINGER_ENTRY="$(print ${FINGERPRINT} | awk '{print $1,$2,$4}')"
|
FINGER_ENTRY="$(print ${FINGERPRINT} | awk '{print $1,$2,$4}')"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
|
# shellcheck disable=SC2086
|
||||||
FINGER_ENTRY="$(print ${FINGERPRINT} | awk '{print $1,$2,$5}')"
|
FINGER_ENTRY="$(print ${FINGERPRINT} | awk '{print $1,$2,$5}')"
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
@ -1523,6 +1561,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"
|
||||||
@ -1530,6 +1569,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 ))
|
||||||
@ -1561,7 +1601,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
|
||||||
@ -1581,12 +1621,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
|
||||||
@ -1613,7 +1653,7 @@ return 0
|
|||||||
CMD_LINE="$*"
|
CMD_LINE="$*"
|
||||||
for PARAMETER in ${CMD_LINE}
|
for PARAMETER in ${CMD_LINE}
|
||||||
do
|
do
|
||||||
case ${PARAMETER} in
|
case "${PARAMETER}" in
|
||||||
-a|-apply|--apply)
|
-a|-apply|--apply)
|
||||||
(( ARG_ACTION > 0 )) && {
|
(( ARG_ACTION > 0 )) && {
|
||||||
print -u2 "ERROR: multiple actions specified"
|
print -u2 "ERROR: multiple actions specified"
|
||||||
@ -1821,6 +1861,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
|
||||||
|
|
||||||
@ -1828,6 +1869,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"
|
||||||
@ -1840,9 +1882,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}] ..."
|
||||||
@ -1852,6 +1894,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=''
|
||||||
@ -1881,6 +1924,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
|
||||||
|
|
||||||
@ -1888,6 +1932,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"
|
||||||
@ -1900,9 +1945,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}] ..."
|
||||||
@ -1912,6 +1957,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=''
|
||||||
@ -1964,16 +2010,16 @@ case ${ARG_ACTION} in
|
|||||||
# are keys stored in a file or a directory?
|
# are keys stored in a file or a directory?
|
||||||
if [[ -n "${KEYS_DIR}" ]]
|
if [[ -n "${KEYS_DIR}" ]]
|
||||||
then
|
then
|
||||||
cat ${KEYS_DIR}/* | sort | while read LINE
|
cat "${KEYS_DIR}"/* | sort | while read -r LINE
|
||||||
do
|
do
|
||||||
update_fingerprints "${LINE}"
|
update_fingerprints "${LINE}"
|
||||||
KEY_COUNT=$(( KEY_COUNT + 1 ))
|
KEY_COUNT=$(( KEY_COUNT + 1 ))
|
||||||
done
|
done
|
||||||
else
|
else
|
||||||
while read LINE
|
while read -r LINE
|
||||||
do
|
do
|
||||||
update_fingerprints "${LINE}"
|
update_fingerprints "${LINE}"
|
||||||
done < ${KEYS_FILE}
|
done < "${KEYS_FILE}"
|
||||||
fi
|
fi
|
||||||
log "${KEY_COUNT} public keys discovered with following bits distribution:"
|
log "${KEY_COUNT} public keys discovered with following bits distribution:"
|
||||||
log " 1024 bits: ${KEY_1024_COUNT}"
|
log " 1024 bits: ${KEY_1024_COUNT}"
|
||||||
@ -1984,11 +2030,11 @@ case ${ARG_ACTION} in
|
|||||||
;;
|
;;
|
||||||
4) # apply SSH controls locally (root user)
|
4) # apply SSH controls locally (root user)
|
||||||
log "ACTION: apply SSH controls locally"
|
log "ACTION: apply SSH controls locally"
|
||||||
( RC=0; ${LOCAL_DIR}/update_ssh.pl ${SSH_UPDATE_OPTS};
|
( RC=0; "${LOCAL_DIR}/update_ssh.pl" ${SSH_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 SSH controls locally [RC=${RC}]"
|
die "failed to apply SSH controls locally [RC=${RC}]"
|
||||||
@ -2000,7 +2046,7 @@ case ${ARG_ACTION} in
|
|||||||
log "ACTION: fix local SSH controls repository"
|
log "ACTION: fix local SSH controls repository"
|
||||||
check_root_user || die "must be run as user 'root'"
|
check_root_user || die "must be run as user 'root'"
|
||||||
log "resetting ownerships to UNIX user ${SUDO_FIX_USER}"
|
log "resetting ownerships to UNIX user ${SUDO_FIX_USER}"
|
||||||
if [[ ${SSH_FIX_USER} = "root" ]]
|
if [[ "${SSH_FIX_USER}" = "root" ]]
|
||||||
then
|
then
|
||||||
warn "!!! resetting ownerships to user root !!!"
|
warn "!!! resetting ownerships to user root !!!"
|
||||||
fi
|
fi
|
||||||
@ -2072,7 +2118,7 @@ case ${ARG_ACTION} in
|
|||||||
5)
|
5)
|
||||||
chcon -R -t sshd_key_t "${FIX_DIR}/keys.d"
|
chcon -R -t sshd_key_t "${FIX_DIR}/keys.d"
|
||||||
;;
|
;;
|
||||||
6|7)
|
6|7|8)
|
||||||
chcon -R -t ssh_home_t "${FIX_DIR}/keys.d"
|
chcon -R -t ssh_home_t "${FIX_DIR}/keys.d"
|
||||||
;;
|
;;
|
||||||
*)
|
*)
|
||||||
@ -2116,6 +2162,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
|
||||||
|
|
||||||
@ -2123,6 +2170,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"
|
||||||
@ -2135,9 +2183,9 @@ case ${ARG_ACTION} in
|
|||||||
do
|
do
|
||||||
if (( DO_SLAVE > 0 ))
|
if (( DO_SLAVE > 0 ))
|
||||||
then
|
then
|
||||||
fix2slave ${CLIENT} "${FIX_DIR}" "${SSH_UPDATE_USER}" &
|
fix2slave "${CLIENT}" "${FIX_DIR}" "${SSH_UPDATE_USER}" &
|
||||||
else
|
else
|
||||||
fix2host ${CLIENT} "${FIX_DIR}" "${SSH_UPDATE_USER}" &
|
fix2host "${CLIENT}" "${FIX_DIR}" "${SSH_UPDATE_USER}" &
|
||||||
fi
|
fi
|
||||||
PID=$!
|
PID=$!
|
||||||
log "fixing SSH controls on ${CLIENT} in background [PID=${PID}] ..."
|
log "fixing SSH controls on ${CLIENT} in background [PID=${PID}] ..."
|
||||||
@ -2147,6 +2195,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=''
|
||||||
@ -2164,7 +2213,7 @@ case ${ARG_ACTION} in
|
|||||||
;;
|
;;
|
||||||
7) # dump the configuration namespace
|
7) # dump the configuration namespace
|
||||||
log "ACTION: dumping the global access namespace with resolved aliases ..."
|
log "ACTION: dumping the global access namespace with resolved aliases ..."
|
||||||
${LOCAL_DIR}/update_ssh.pl --preview --global
|
"${LOCAL_DIR}/update_ssh.pl" --preview --global
|
||||||
log "finished dumping the global namespace"
|
log "finished dumping the global namespace"
|
||||||
;;
|
;;
|
||||||
8) # check syntax of the access/alias/keys files
|
8) # check syntax of the access/alias/keys files
|
||||||
@ -2185,16 +2234,21 @@ case ${ARG_ACTION} in
|
|||||||
# keys files
|
# keys files
|
||||||
if [[ -n "${KEYS_DIR}" ]]
|
if [[ -n "${KEYS_DIR}" ]]
|
||||||
then
|
then
|
||||||
|
# shellcheck disable=SC2086
|
||||||
log "$(tar -cvf ${BACKUP_TAR_FILE} ${KEYS_DIR} 2>/dev/null)"
|
log "$(tar -cvf ${BACKUP_TAR_FILE} ${KEYS_DIR} 2>/dev/null)"
|
||||||
else
|
else
|
||||||
|
# shellcheck disable=SC2086
|
||||||
log "$(tar -cvf ${BACKUP_TAR_FILE} ${KEYS_FILE} 2>/dev/null)"
|
log "$(tar -cvf ${BACKUP_TAR_FILE} ${KEYS_FILE} 2>/dev/null)"
|
||||||
fi
|
fi
|
||||||
# configuration files
|
# configuration files
|
||||||
for FILE in "${LOCAL_DIR}/access" "${LOCAL_DIR}/alias ${LOCAL_DIR}/targets"
|
for FILE in "${LOCAL_DIR}/access" "${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 SSH master?"
|
die "could not find backup directory ${BACKUP_DIR}. Host is not an SSH master?"
|
||||||
@ -2210,6 +2264,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
|
||||||
@ -2219,10 +2274,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"
|
||||||
|
@ -15,6 +15,14 @@ use_fqdn=1
|
|||||||
# target directory for allowed SSH key files
|
# target directory for allowed SSH key files
|
||||||
access_dir=/etc/ssh_controls/keys.d
|
access_dir=/etc/ssh_controls/keys.d
|
||||||
|
|
||||||
|
# toggle to specify the final location of public keys by allowing to override
|
||||||
|
# the value of $access_dir with the 'AuthorizedKeysFile' in sshd (=enables the
|
||||||
|
# use of $HOME/.ssh for public keys for example):
|
||||||
|
# 'use_controls': take the value from the configured 'access_dir' option
|
||||||
|
# 'use_sshd' : use the value from 'AuthorizedKeysFile' setting in sshd
|
||||||
|
# [default: use_controls]
|
||||||
|
key_location=use_controls
|
||||||
|
|
||||||
# location of the keys blacklist file
|
# location of the keys blacklist file
|
||||||
blacklist_file=/etc/ssh_controls/keys.blacklisted
|
blacklist_file=/etc/ssh_controls/keys.blacklisted
|
||||||
|
|
||||||
|
170
update_ssh.pl
170
update_ssh.pl
@ -42,7 +42,7 @@ use Pod::Usage;
|
|||||||
|
|
||||||
# ------------------------- 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_ssh.conf";
|
my $global_config_file = "update_ssh.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)
|
||||||
@ -52,13 +52,18 @@ my $max_recursion = 5;
|
|||||||
# selinux context labels of key files for different RHEL version
|
# selinux context labels of key files for different RHEL version
|
||||||
my %selinux_contexts = ( '5' => 'sshd_key_t',
|
my %selinux_contexts = ( '5' => 'sshd_key_t',
|
||||||
'6' => 'ssh_home_t',
|
'6' => 'ssh_home_t',
|
||||||
'7' => 'ssh_home_t');
|
'7' => 'ssh_home_t',
|
||||||
|
'8' => 'ssh_home_t');
|
||||||
|
# disallowed paths for home directories for accounts
|
||||||
|
my @disallowed_homes = ('/', '/etc', '/bin', '/sbin', '/usr/bin', '/usr/sbin');
|
||||||
|
# disallowed login shells for @accounts
|
||||||
|
my @disallowed_shells = ('/bin/nologin','/bin/false','/sbin/nologin','/sbin/false');
|
||||||
# ------------------------- CONFIGURATION ends here ---------------------------
|
# ------------------------- CONFIGURATION ends here ---------------------------
|
||||||
# initialize variables
|
# initialize variables
|
||||||
my ($debug, $verbose, $preview, $remove, $global, $use_fqdn) = (0,0,0,0,0,0);
|
my ($debug, $verbose, $preview, $remove, $global, $use_fqdn) = (0,0,0,0,0,0);
|
||||||
my (@config_files, @zombie_files, $access_dir, $blacklist_file);
|
my (@config_files, @zombie_files, $access_dir, $key_location, $blacklist_file);
|
||||||
my (%options, @uname, @pwgetent, @accounts, %aliases, %keys, %access, @blacklist);
|
my (%options, @uname, @pwgetent, @accounts, %aliases, %keys, %access, @blacklist);
|
||||||
my ($os, $hostname, $run_dir);
|
my ($os, $hostname, $run_dir, $authorizedkeys_option);
|
||||||
my ($selinux_status, $selinux_context, $linux_version, $has_selinux, $recursion_count) = ("","","",0,1);
|
my ($selinux_status, $selinux_context, $linux_version, $has_selinux, $recursion_count) = ("","","",0,1);
|
||||||
$|++;
|
$|++;
|
||||||
|
|
||||||
@ -89,7 +94,7 @@ sub parse_config_file {
|
|||||||
my $config_file = shift;
|
my $config_file = shift;
|
||||||
|
|
||||||
unless (open (CONF_FD, "<", $config_file)) {
|
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);
|
and exit (1);
|
||||||
}
|
}
|
||||||
while (<CONF_FD>) {
|
while (<CONF_FD>) {
|
||||||
@ -98,7 +103,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}");
|
||||||
}
|
}
|
||||||
@ -106,6 +111,15 @@ sub parse_config_file {
|
|||||||
$access_dir = $1;
|
$access_dir = $1;
|
||||||
do_log ("DEBUG: picking up setting: access_dir=${access_dir}");
|
do_log ("DEBUG: picking up setting: access_dir=${access_dir}");
|
||||||
}
|
}
|
||||||
|
if (/^\s*key_location\s*=\s*(use_controls|use_sshd)\s*/) {
|
||||||
|
$key_location = $1;
|
||||||
|
do_log ("DEBUG: picking up setting: key_location=${key_location}");
|
||||||
|
if ($key_location eq 'use_sshd') {
|
||||||
|
do_log ("DEBUG: applied setting: key_location=${key_location}");
|
||||||
|
} else {
|
||||||
|
do_log ("DEBUG: applied default setting: key_location=${key_location}");
|
||||||
|
}
|
||||||
|
}
|
||||||
if (/^\s*blacklist_file\s*=\s*([0-9A-Za-z_\-\.\/~]+)\s*$/) {
|
if (/^\s*blacklist_file\s*=\s*([0-9A-Za-z_\-\.\/~]+)\s*$/) {
|
||||||
$blacklist_file = $1;
|
$blacklist_file = $1;
|
||||||
# support tilde (~) expansion for ~root
|
# support tilde (~) expansion for ~root
|
||||||
@ -151,10 +165,10 @@ sub set_file {
|
|||||||
my ($file, $perm, $uid, $gid) = @_;
|
my ($file, $perm, $uid, $gid) = @_;
|
||||||
|
|
||||||
chmod ($perm, "$file")
|
chmod ($perm, "$file")
|
||||||
or do_log ("ERROR: cannot set permissions on $file [$! $hostname]")
|
or do_log ("ERROR: cannot set permissions on $file [$!/$hostname]")
|
||||||
and exit (1);
|
and exit (1);
|
||||||
chown ($uid, $gid, "$file")
|
chown ($uid, $gid, "$file")
|
||||||
or do_log ("ERROR: cannot set ownerships on $file [$! $hostname]")
|
or do_log ("ERROR: cannot set ownerships on $file [$!/$hostname]")
|
||||||
and exit (1);
|
and exit (1);
|
||||||
|
|
||||||
return (1);
|
return (1);
|
||||||
@ -228,7 +242,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)
|
||||||
@ -245,15 +259,20 @@ foreach my $config_file (@config_files) {
|
|||||||
parse_config_file ($config_file);
|
parse_config_file ($config_file);
|
||||||
}
|
}
|
||||||
|
|
||||||
# is the target directory for keys present? (not for global preview)
|
# is the target directory for keys present? (not for global preview and
|
||||||
unless ($preview and $global) {
|
# not when $key_location is use_sshd)
|
||||||
do_log ("INFO: checking for SSH control mode ...");
|
unless (($preview and $global) or $key_location eq 'use_sshd') {
|
||||||
|
do_log ("INFO: checking for SSH controls mode ...");
|
||||||
if (-d $access_dir) {
|
if (-d $access_dir) {
|
||||||
do_log ("INFO: host is under SSH control via $access_dir");
|
do_log ("INFO: host is under SSH controls via $access_dir");
|
||||||
|
} else {
|
||||||
|
if ($key_location eq 'use_sshd') {
|
||||||
|
do_log ("INFO: skipped check since public key location is determined by sshd [$hostname]")
|
||||||
} else {
|
} else {
|
||||||
do_log ("ERROR: host is not under SSH keys only control [$hostname]")
|
do_log ("ERROR: host is not under SSH keys only control [$hostname]")
|
||||||
and exit (1);
|
and exit (1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
# do we have a blacklist file? (optional) (not for global preview)
|
# do we have a blacklist file? (optional) (not for global preview)
|
||||||
@ -261,7 +280,7 @@ unless ($preview and $global) {
|
|||||||
do_log ("INFO: checking for keys blacklist file ...");
|
do_log ("INFO: checking for keys blacklist file ...");
|
||||||
if (-f $blacklist_file) {
|
if (-f $blacklist_file) {
|
||||||
open (BLACKLIST, "<", $blacklist_file) or \
|
open (BLACKLIST, "<", $blacklist_file) or \
|
||||||
do_log ("ERROR: cannot read keys blacklist file [$! $hostname]")
|
do_log ("ERROR: cannot read keys blacklist file [$!/$hostname]")
|
||||||
and exit (1);
|
and exit (1);
|
||||||
@blacklist = <BLACKLIST>;
|
@blacklist = <BLACKLIST>;
|
||||||
close (BLACKLIST);
|
close (BLACKLIST);
|
||||||
@ -291,6 +310,28 @@ if ($use_fqdn) {
|
|||||||
|
|
||||||
do_log ("INFO: runtime info: ".getpwuid ($<)."; ${hostname}\@${run_dir}; Perl v$]");
|
do_log ("INFO: runtime info: ".getpwuid ($<)."; ${hostname}\@${run_dir}; Perl v$]");
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# resolve and check key location
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
if ($key_location eq 'use_sshd') {
|
||||||
|
|
||||||
|
# get sshd setting but only take 1st path into account
|
||||||
|
$authorizedkeys_option = qx#sshd -T | grep "authorizedkeysfile" 2>/dev/null | cut -f2 -d' '#;
|
||||||
|
chomp ($authorizedkeys_option);
|
||||||
|
if (defined ($authorizedkeys_option)) {
|
||||||
|
do_log ("INFO: AuthorizedkeysFile resolves to $authorizedkeys_option [$hostname]");
|
||||||
|
} else {
|
||||||
|
do_log ("ERROR: unable to get AuthorizedkeysFile value from sshd [$hostname]")
|
||||||
|
and exit (1);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
# for SSH controls native logic we require an absolute path
|
||||||
|
if ($authorizedkeys_option =~ /^\//) {
|
||||||
|
do_log ("ERROR: option \$access_dir requires and absolute path [$hostname]")
|
||||||
|
and exit (1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# collect user accounts via getpwent()
|
# collect user accounts via getpwent()
|
||||||
# result: @accounts
|
# result: @accounts
|
||||||
@ -319,7 +360,7 @@ print Dumper (\@accounts) if $debug;
|
|||||||
do_log ("INFO: reading 'alias' file ...");
|
do_log ("INFO: reading 'alias' file ...");
|
||||||
|
|
||||||
open (ALIASES, "<", "${run_dir}/alias")
|
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>) {
|
while (<ALIASES>) {
|
||||||
|
|
||||||
my ($key, $value, @values);
|
my ($key, $value, @values);
|
||||||
@ -398,7 +439,7 @@ if (-d "${run_dir}/keys.d" && -f "${run_dir}/keys") {
|
|||||||
if (-d "${run_dir}/keys.d") {
|
if (-d "${run_dir}/keys.d") {
|
||||||
do_log ("INFO: local 'keys' are stored in a DIRECTORY on $hostname");
|
do_log ("INFO: local 'keys' are stored in a DIRECTORY on $hostname");
|
||||||
opendir (KEYS_DIR, "${run_dir}/keys.d")
|
opendir (KEYS_DIR, "${run_dir}/keys.d")
|
||||||
or do_log ("ERROR: cannot open 'keys.d' directory [$! $hostname]")
|
or do_log ("ERROR: cannot open 'keys.d' directory [$!/$hostname]")
|
||||||
and exit (1);
|
and exit (1);
|
||||||
while (my $key_file = readdir (KEYS_DIR)) {
|
while (my $key_file = readdir (KEYS_DIR)) {
|
||||||
next if ($key_file =~ /^\./);
|
next if ($key_file =~ /^\./);
|
||||||
@ -416,7 +457,7 @@ if (-d "${run_dir}/keys.d") {
|
|||||||
# process 'keys' files
|
# process 'keys' files
|
||||||
foreach my $key_file (@key_files) {
|
foreach my $key_file (@key_files) {
|
||||||
open (KEYS, "<", $key_file)
|
open (KEYS, "<", $key_file)
|
||||||
or do_log ("ERROR: cannot read 'keys' file [$! $hostname]") and exit (1);
|
or do_log ("ERROR: cannot read 'keys' file [$!/$hostname]") and exit (1);
|
||||||
do_log ("INFO: reading public keys from file: $key_file");
|
do_log ("INFO: reading public keys from file: $key_file");
|
||||||
while (<KEYS>) {
|
while (<KEYS>) {
|
||||||
|
|
||||||
@ -454,7 +495,7 @@ print Dumper(\%keys) if $debug;
|
|||||||
do_log ("INFO: reading 'access' file ...");
|
do_log ("INFO: reading 'access' file ...");
|
||||||
|
|
||||||
open (ACCESS, "<", "${run_dir}/access")
|
open (ACCESS, "<", "${run_dir}/access")
|
||||||
or do_log ("ERROR: cannot read 'access' file [$! $hostname]") and exit (1);
|
or do_log ("ERROR: cannot read 'access' file [$!/$hostname]") and exit (1);
|
||||||
while (<ACCESS>) {
|
while (<ACCESS>) {
|
||||||
|
|
||||||
my ($who, $where, $what, @who, @where, @what);
|
my ($who, $where, $what, @who, @where, @what);
|
||||||
@ -507,7 +548,7 @@ if ($preview && $global) {
|
|||||||
do_log ("INFO: display GLOBAL configuration ....");
|
do_log ("INFO: display GLOBAL configuration ....");
|
||||||
|
|
||||||
open (ACCESS, "<", "${run_dir}/access")
|
open (ACCESS, "<", "${run_dir}/access")
|
||||||
or do_log ("ERROR: cannot read 'access' file [$! $hostname]") and exit (1);
|
or do_log ("ERROR: cannot read 'access' file [$!/$hostname]") and exit (1);
|
||||||
while (<ACCESS>) {
|
while (<ACCESS>) {
|
||||||
|
|
||||||
my ($who, $where, $what, @who, @where, @what);
|
my ($who, $where, $what, @who, @where, @what);
|
||||||
@ -542,7 +583,8 @@ if ($preview && $global) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# distribute keys into authorized_keys files in $access_dir
|
# distribute keys into authorized_keys files
|
||||||
|
# (defined by $key_location and/or $access_dir)
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
do_log ("INFO: applying SSH access rules ....");
|
do_log ("INFO: applying SSH access rules ....");
|
||||||
@ -578,6 +620,10 @@ unless ($preview) {
|
|||||||
$linux_version = 7;
|
$linux_version = 7;
|
||||||
last SWITCH_RELEASE;
|
last SWITCH_RELEASE;
|
||||||
};
|
};
|
||||||
|
$release_string =~ m/release 8/i && do {
|
||||||
|
$linux_version = 8;
|
||||||
|
last SWITCH_RELEASE;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# use fall back in case we cannot determine the version
|
# use fall back in case we cannot determine the version
|
||||||
@ -599,17 +645,50 @@ unless ($preview) {
|
|||||||
|
|
||||||
# only add authorized_keys for existing accounts,
|
# only add authorized_keys for existing accounts,
|
||||||
# otherwise revoke access if needed
|
# otherwise revoke access if needed
|
||||||
foreach my $account (sort (@accounts)) {
|
SET_KEY: foreach my $account (sort (@accounts)) {
|
||||||
|
|
||||||
my $access_file = "$access_dir/$account";
|
my ($access_file, $authorizedkeys_file, $uid, $gid, $home_dir, $login_shell) = (undef, undef, undef, undef, undef, undef);
|
||||||
|
|
||||||
|
# set $access_file when using SSH controls logic
|
||||||
|
if ($key_location eq 'use_sshd' and defined ($authorizedkeys_option)) {
|
||||||
|
# use sshd logic (replacing %u,%h, %%)
|
||||||
|
$authorizedkeys_file = $authorizedkeys_option;
|
||||||
|
$authorizedkeys_file =~ s/%u/$account/g;
|
||||||
|
$authorizedkeys_file =~ s/%h/$hostname/g;
|
||||||
|
$authorizedkeys_file =~ s/%%/%/g;
|
||||||
|
# check relative path (assume $HOME needs to be added)
|
||||||
|
if ($authorizedkeys_file !~ /^\//) {
|
||||||
|
($uid, $gid, $home_dir, $login_shell) = (getpwnam($account))[2,3,7,8];
|
||||||
|
# do not accept invalid $HOME or shells
|
||||||
|
if (defined ($home_dir)) {
|
||||||
|
if (grep( /^$home_dir$/, @disallowed_homes) or grep( /^$login_shell/, @disallowed_shells)) {
|
||||||
|
do_log ("DEBUG: invalid HOME or SHELL for $account [$hostname]");
|
||||||
|
next SET_KEY;
|
||||||
|
} else {
|
||||||
|
$authorizedkeys_file = $home_dir."/".$authorizedkeys_file;
|
||||||
|
do_log ("DEBUG: adding $home_dir to public key path for $account [$hostname]");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
do_log ("ERROR: unable to get HOME for $account [$hostname]");
|
||||||
|
next SET_KEY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$access_file = $authorizedkeys_file;
|
||||||
|
} else {
|
||||||
|
# use native SSH controls logic
|
||||||
|
$access_file = "$access_dir/$account";
|
||||||
|
}
|
||||||
|
do_log ("DEBUG: public key location for $account resolves to $authorizedkeys_file [$hostname]");
|
||||||
|
|
||||||
# only add authorised_keys if there are access definitions
|
# only add authorised_keys if there are access definitions
|
||||||
if ($access{$account}) {
|
if ($access{$account}) {
|
||||||
|
|
||||||
unless ($preview) {
|
unless ($preview) {
|
||||||
|
# do not create root or intermediate paths in $access_file;
|
||||||
|
# e.g. if $HOME/.ssh/authorized_keys is the public key path, then $HOME/.ssh must already exist
|
||||||
open (KEYFILE, "+>", $access_file)
|
open (KEYFILE, "+>", $access_file)
|
||||||
or do_log ("ERROR: cannot open file for writing in $access_dir [$! $hostname]")
|
or do_log ("ERROR: cannot open file for writing at $access_file [$!/$hostname]")
|
||||||
and exit (1);
|
and next SET_KEY;
|
||||||
}
|
}
|
||||||
foreach my $person (sort (@{$access{$account}})) {
|
foreach my $person (sort (@{$access{$account}})) {
|
||||||
my $real_name = $person;
|
my $real_name = $person;
|
||||||
@ -626,9 +705,13 @@ foreach my $account (sort (@accounts)) {
|
|||||||
}
|
}
|
||||||
close (KEYFILE) unless $preview;
|
close (KEYFILE) unless $preview;
|
||||||
|
|
||||||
# set permissions to world readable and check for SELinux context
|
# set ownerships/permissions on public key file and check for SELinux context
|
||||||
unless ($preview) {
|
unless ($preview) {
|
||||||
|
if ($key_location eq 'use_controls') {
|
||||||
set_file ($access_file, 0644, 0, 0);
|
set_file ($access_file, 0644, 0, 0);
|
||||||
|
} else {
|
||||||
|
set_file ($access_file, 0600, $uid, $gid);
|
||||||
|
}
|
||||||
# selinux labels
|
# selinux labels
|
||||||
SWITCH: {
|
SWITCH: {
|
||||||
$os eq "Linux" && do {
|
$os eq "Linux" && do {
|
||||||
@ -645,7 +728,7 @@ foreach my $account (sort (@accounts)) {
|
|||||||
if (-f $access_file) {
|
if (-f $access_file) {
|
||||||
unless ($preview) {
|
unless ($preview) {
|
||||||
unlink ($access_file)
|
unlink ($access_file)
|
||||||
or do_log ("ERROR: cannot remove obsolete access file(s) [$! $hostname]")
|
or do_log ("ERROR: cannot remove obsolete access file $access_file [$!/$hostname]")
|
||||||
and exit (1);
|
and exit (1);
|
||||||
} else {
|
} else {
|
||||||
do_log ("INFO: removing obsolete access $access_file on $hostname");
|
do_log ("INFO: removing obsolete access $access_file on $hostname");
|
||||||
@ -655,32 +738,35 @@ foreach my $account (sort (@accounts)) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
# alert on/remove extraneous authorized_keys files
|
# alert on/remove extraneous authorized_keys files (SSH controls logic only)
|
||||||
# (access files for which no longer a valid UNIX account exists)
|
# (access files for which no longer a valid UNIX account exists)
|
||||||
# -----------------------------------------------------------------------------
|
# -----------------------------------------------------------------------------
|
||||||
|
|
||||||
do_log ("INFO: checking for extraneous access files ....");
|
if ($key_location eq 'use_controls') {
|
||||||
|
|
||||||
opendir (ACCESS_DIR, $access_dir)
|
do_log ("INFO: checking for extraneous access files ....");
|
||||||
or do_log ("ERROR: cannot open directory $access_dir [$! $hostname]")
|
|
||||||
|
opendir (ACCESS_DIR, $access_dir)
|
||||||
|
or do_log ("ERROR: cannot open directory $access_dir [$!/$hostname]")
|
||||||
and exit (1);
|
and exit (1);
|
||||||
while (my $access_file = readdir (ACCESS_DIR)) {
|
while (my $access_file = readdir (ACCESS_DIR)) {
|
||||||
next if ($access_file =~ /^\./);
|
next if ($access_file =~ /^\./);
|
||||||
unless (grep (/$access_file/, @accounts)) {
|
unless (grep (/$access_file/, @accounts)) {
|
||||||
do_log ("WARN: found extraneous access file in $access_dir/$access_file [$hostname]");
|
do_log ("WARN: found extraneous access file in $access_dir/$access_file [$hostname]");
|
||||||
push (@zombie_files, "$access_dir/$access_file");
|
push (@zombie_files, "$access_dir/$access_file");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closedir (ACCESS_DIR);
|
closedir (ACCESS_DIR);
|
||||||
do_log ("INFO: ".scalar (@zombie_files)." extraneous access file(s) found on $hostname");
|
do_log ("INFO: ".scalar (@zombie_files)." extraneous access file(s) found on $hostname");
|
||||||
print Dumper (\@zombie_files) if $debug;
|
print Dumper (\@zombie_files) if $debug;
|
||||||
|
|
||||||
# remove if requested and needed
|
# remove if requested and needed
|
||||||
if ($remove && @zombie_files) {
|
if ($remove && @zombie_files) {
|
||||||
my $count = unlink (@zombie_files)
|
my $count = unlink (@zombie_files)
|
||||||
or do_log ("ERROR: cannot remove extraneous access file(s) [$! $hostname]")
|
or do_log ("ERROR: cannot remove extraneous access file(s) [$!/$hostname]")
|
||||||
and exit (1);
|
and exit (1);
|
||||||
do_log ("INFO: $count extraneous access files removed $hostname");
|
do_log ("INFO: $count extraneous access files removed $hostname");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
exit (0);
|
exit (0);
|
||||||
@ -711,9 +797,11 @@ update_ssh.pl - distributes SSH public keys in a desired state model.
|
|||||||
=head1 DESCRIPTION
|
=head1 DESCRIPTION
|
||||||
|
|
||||||
B<update_ssh.pl> distributes SSH keys to the appropriate files (.e. 'authorized_keys') into the C<$access_dir> repository based on the F<access>, F<alias> and F<keys> files.
|
B<update_ssh.pl> distributes SSH keys to the appropriate files (.e. 'authorized_keys') into the C<$access_dir> repository based on the F<access>, F<alias> and F<keys> files.
|
||||||
|
Alternatively B<update_ssh.pl> can distribute public keys to the location specified in the AuthorizedkeysFile setting of F<sshd_config> (allowing public keys to be distributed
|
||||||
|
to the traditional location in a user's HOME directory). See C<key_location> setting in F<update_ssh.conf[.local]>for more information.
|
||||||
This script should be run on each host where SSH key authentication is the exclusive method of (remote) authentication.
|
This script should be run on each host where SSH key authentication is the exclusive method of (remote) authentication.
|
||||||
|
|
||||||
For update SSH public keys must be stored in a generic F<keys> file within the same directory as B<update_ssh.pl> script.
|
Orginally SSH public keys must be stored in a generic F<keys> file within the same directory as B<update_ssh.pl> script.
|
||||||
Alternatively key files may be stored as set of individual key files within a called sub-directory called F<keys.d>.
|
Alternatively key files may be stored as set of individual key files within a called sub-directory called F<keys.d>.
|
||||||
Both methods are mutually exclusive and the latter always take precedence.
|
Both methods are mutually exclusive and the latter always take precedence.
|
||||||
|
|
||||||
@ -739,6 +827,8 @@ Following settings must be configured:
|
|||||||
|
|
||||||
=item * B<access_dir> : target directory for allowed SSH public key files
|
=item * B<access_dir> : target directory for allowed SSH public key files
|
||||||
|
|
||||||
|
=item * B<key_location> : whether or not to use AuthorizedkeysFile setting in sshd_config for overriding $access_dir
|
||||||
|
|
||||||
=item * B<blacklist_file> : location of the file with blacklisted SSH public keys
|
=item * B<blacklist_file> : location of the file with blacklisted SSH public keys
|
||||||
|
|
||||||
=back
|
=back
|
||||||
@ -746,7 +836,7 @@ Following settings must be configured:
|
|||||||
=head1 BLACKLISTING
|
=head1 BLACKLISTING
|
||||||
|
|
||||||
Key blacklisting can be performed by adding a public key definition in its entirety to the blacklist keys file. When a blacklisted key is
|
Key blacklisting can be performed by adding a public key definition in its entirety to the blacklist keys file. When a blacklisted key is
|
||||||
found in the available F<keys> file(s) during SSH control updates, an alert will be shown on STDOUT and the key will be ignored for the rest.
|
found in the available F<keys> file(s) during SSH controls updates, an alert will be shown on STDOUT and the key will be ignored for the rest.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user