214 lines
6.5 KiB
Bash
Executable File
214 lines
6.5 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
# Rotate and deploy admin keys
|
|
|
|
# List of servers with info
|
|
|
|
# ssh-keygen -t ed25519 -N "" -C "<COMMENT>" -f <FILE>
|
|
|
|
# store old key names, current key names
|
|
|
|
# base folder for all data
|
|
BASE_FOLDER=$(dirname "$(readlink -f "$0")")"/";
|
|
# config folder
|
|
CONFIG_BASE="${BASE_FOLDER}../config/";
|
|
# timestamps of last rotate per user/host
|
|
LAST_ROTATE="${BASE_FOLDER}../last-rotate/";
|
|
# ssh-keys temp holder
|
|
SSH_PRIVATE_KEYS="${BASE_FOLDER}../ssh-keys/";
|
|
# ssh public keys from current and last
|
|
SSH_PUBLIC_KEYS_PREVIOUS="${BASE_FOLDER}../ssh-public-keys/previous/";
|
|
SSH_PUBLIC_KEYS_CURRENT="${BASE_FOLDER}../ssh-public-keys/current/";
|
|
# PEM folder for server
|
|
PEM_SERVER="${HOME}/.ssh/PEMS/SERVER-ADMIN/";
|
|
# PEM archive folder
|
|
PEM_ARCHIVE="${HOME}/.ssh/ARCHIVED_PEMs/SERVER-ADMIN/$(date +%F)/";
|
|
|
|
DRYRUN=0;
|
|
FORCE=0;
|
|
HOST_ONLY="";
|
|
USER_ONLY="";
|
|
#
|
|
while getopts ":h:u:n" opt; do
|
|
case "${opt}" in
|
|
h|hostname)
|
|
HOST_ONLY="${OPTARG}";
|
|
;;
|
|
u|username)
|
|
USER_ONLY="${OPTARG}";
|
|
;;
|
|
n|dryrun)
|
|
DRYRUN=1;
|
|
;;
|
|
f|force)
|
|
FORCE=1;
|
|
;;
|
|
\?)
|
|
echo -e "\n Option does not exist: ${OPTARG}\n";
|
|
echo "-h override single host name";
|
|
echo "-n dry run";
|
|
echo ""
|
|
exit 1;
|
|
;;
|
|
esac;
|
|
done;
|
|
|
|
if [ ! -d "${SSH_PRIVATE_KEYS}" ]; then
|
|
echo "Missing ssh private keys folder: ${SSH_PRIVATE_KEYS}";
|
|
exit;
|
|
fi
|
|
if [ ! -d "${SSH_PUBLIC_KEYS_CURRENT}" ]; then
|
|
echo "Missing ssh public keys current folder: ${SSH_PUBLIC_KEYS_CURRENT}";
|
|
exit;
|
|
fi
|
|
if [ ! -d "${SSH_PUBLIC_KEYS_PREVIOUS}" ]; then
|
|
echo "Missing ssh public keys previous folder: ${SSH_PUBLIC_KEYS_PREVIOUS}";
|
|
exit;
|
|
fi
|
|
|
|
# load config
|
|
if [ -f "${CONFIG_BASE}settings.ini" ]; then
|
|
source <(grep = "${CONFIG_BASE}settings.ini" | sed 's/ *= */=/g')
|
|
fi
|
|
if [ -z "${key_age}" ]; then
|
|
echo "A minimnum key age in days must be set";
|
|
exit;
|
|
fi
|
|
|
|
# we must have "server_list" set and file must be in config folder
|
|
if [ ! -f "${CONFIG_BASE}${server_list}" ]; then
|
|
echo "Cannot find ${server_list} file in the config folder";
|
|
exit;
|
|
fi
|
|
|
|
# default ssh command
|
|
SSH="ssh -a -x";
|
|
|
|
add_ssh_key() {
|
|
AUTH_KEY_FILE="${1}";
|
|
PUB_KEY_DATA="${2}";
|
|
RMV_CHATTR_I="chattr -i "
|
|
ADD_CHATTR_I="chattr +i "
|
|
RMV_CHMOD_UW="chamod u-w "
|
|
ADD_CHMOD_UW="chmod u+w "
|
|
# { [ -z \`tail -1c ${AUTH_KEY_FILE} 2>/dev/null\` ] ||
|
|
# echo >> "${AUTH_KEY_FILE}" || exit 1; } &&
|
|
# cat >> "${AUTH_KEY_FILE}" || exit 1;
|
|
|
|
# check if same key already exists, if yes, skip
|
|
# the -z `tail ...` checks for a trailing newline. The echo adds one if was missing (from ssh-copy-id)
|
|
|
|
# if [ -f "${AUTH_KEY_FILE}" ] && ! cat >> grep "${AUTH_KEY_FILE}"; then
|
|
# ${RMV_CHATTR_I} ${AUTH_KEY_FILE}
|
|
# ${ADD_CHMOD_UW} ${AUTH_KEY_FILE}
|
|
# { [ -z \`tail -1c ${AUTH_KEY_FILE} 2>/dev/null\` ] ||
|
|
# echo >> "${AUTH_KEY_FILE}" || exit 1; } &&
|
|
# cat >> "${AUTH_KEY_FILE}"
|
|
# ${RMV_CHMOD_UW} ${AUTH_KEY_FILE}
|
|
# ${ADD_CHATTR_I} ${AUTH_KEY_FILE}
|
|
# fi;
|
|
|
|
INSTALLKEYS_SH=$(tr '\t\n' ' ' <<-EOF
|
|
if [ -f "~/TEMP_SSH_PUB.pem.pub" ] && ! cat >> grep "~/TEMP_SSH_PUB.pem.pub"; then
|
|
${RMV_CHATTR_I} ~/TEMP_SSH_PUB.pem.pub
|
|
${ADD_CHMOD_UW} ~/TEMP_SSH_PUB.pem.pub
|
|
{ [ -z \`tail -1c ~/TEMP_SSH_PUB.pem.pub 2>/dev/null\` ] ||
|
|
echo >> "~/TEMP_SSH_PUB.pem.pub || exit 1; } &&
|
|
cat >> "~/TEMP_SSH_PUB.pem.pub"
|
|
${RMV_CHMOD_UW} ~/TEMP_SSH_PUB.pem.pub
|
|
${ADD_CHATTR_I} ~/TEMP_SSH_PUB.pem.pub
|
|
fi;
|
|
EOF
|
|
)
|
|
|
|
# to defend against quirky remote shells: use 'exec sh -c' to get POSIX;
|
|
printf "exec sh -c '%s'" "${INSTALLKEYS_SH}"
|
|
}
|
|
|
|
install_ssh_key() {
|
|
HOSTNAME="${1}";
|
|
USERNAME="${2}";
|
|
PUB_KEY="${3}";
|
|
AUTH_KEY_FILE="${4}";
|
|
# PUB_KEY_DATA=$(printf '%s\n' "$(cat "${PUB_KEY}")")
|
|
echo "=== FILE ${AUTH_KEY_FILE}";
|
|
printf '%s\n' "$(cat "${PUB_KEY}")" | ${SSH} "${USERNAME}"@"${HOSTNAME}" "$(add_ssh_key "${AUTH_KEY_FILE}" "${PUB_KEY_DATA}")"
|
|
}
|
|
|
|
# Remove all last entries
|
|
# Move all current to last
|
|
if [ ${DRYRUN} -eq 0 ]; then
|
|
echo "Remove all previous ssh public keys";
|
|
rm "${SSH_PUBLIC_KEYS_PREVIOUS}"*;
|
|
echo "Move all current public keys to the previous folder";
|
|
mv "${SSH_PUBLIC_KEYS_CURRENT}"* "${SSH_PUBLIC_KEYS_PREVIOUS}";
|
|
# create new archive folder local
|
|
mkdir -p "${PEM_ARCHIVE}";
|
|
fi
|
|
|
|
for line in `cat "${CONFIG_BASE}${server_list}" | sed 1d`; do
|
|
if [[ "${i}" =~ ^\# ]]; then
|
|
continue;
|
|
fi
|
|
# hostname is on pos 1
|
|
hostname=$(echo "${line}" | cut -d "," -f 1);
|
|
# if hostname opt set and not matching skip
|
|
if [ ! -z "${HOST_ONLY}" ] && [ "${HOST_ONLY}" != "${hostname}" ]; then
|
|
continue;
|
|
fi
|
|
# login user name
|
|
username=$(echo "${line}" | cut -d "," -f 2);
|
|
# if username opt set and not matching skip
|
|
if [ ! -z "${USER_ONLY}" ] && [ "${USER_ONLY}" != "${username}" ]; then
|
|
continue;
|
|
fi
|
|
# check if force or if last rotaet in valid range
|
|
if [ "${FORCE}" -eq 0 ] && [ -f "${LAST_ROTATE}${hostname}_${username}.last-rotate"]; then
|
|
# holds unix timestamp, if now - this timestamp is < key_age => skip
|
|
last_rotate=$(cat "${LAST_ROTATE}${hostname}_${username}.last-rotate");
|
|
current_timestamp=$(date +%s)
|
|
age=$(( ($current_timestamp - $last_rotate) ))
|
|
days_left=$(( (age)/(3600*24) ))
|
|
if [ $days_left -le $key_age ]; then
|
|
echo "Last rotate for ${username}@${hostname} was ${days_left} days ago, minimum is ${key_age}";
|
|
continue;
|
|
fi
|
|
fi
|
|
|
|
# flags: (not used at the moment)
|
|
flags=$(echo "${line}" | cut -d "," -f 3);
|
|
# name for the SSH key files
|
|
SSH_KEY_FILE="${hostname}_${username}.pem";
|
|
SSH_KEY_PUB_FILE="${hostname}_${username}.pem.pub";
|
|
|
|
# create name
|
|
echo "Create new key for: ${username}@${hostname} with flags '${flags}'";
|
|
# if current exist, skip creation
|
|
# if pem or pub missing, but not both, alert and skip
|
|
# else create new
|
|
if [ -f "${SSH_KEY_FILE}" ]
|
|
ssh-keygen -t ed25519 -N "" -C "${hostname}: $(date +%F)" -f "${SSH_PRIVATE_KEYS}${SSH_KEY_FILE}"
|
|
# move the public key to the current folder
|
|
mv "${SSH_PRIVATE_KEYS}${SSH_KEY_PUB_FILE}" "${SSH_PUBLIC_KEYS_CURRENT}";
|
|
# deploy public key to server
|
|
# - master admin file
|
|
install_ssh_key "${hostname}" "${username}" "${SSH_PUBLIC_KEYS_CURRENT}${SSH_KEY_PUB_FILE}" "/etc/ssh/authorized_keys--master"
|
|
# - admin ssh config auth file
|
|
install_ssh_key "${hostname}" "${username}" "${SSH_PUBLIC_KEYS_CURRENT}${SSH_KEY_PUB_FILE}" "/etc/ssh/authorized_keys/${username}"
|
|
# - copy local PEM file to archive folder
|
|
if [ ${DRYRUN} -eq 0 ]; then
|
|
if [ -f "${PEM_SERVER}${SSH_KEY_FILE}" ]; then
|
|
cp "${PEM_SERVER}${SSH_KEY_FILE}" "${PEM_ARCHIVE}";
|
|
fi
|
|
# - copy to local ssh folder
|
|
mv "${SSH_PRIVATE_KEYS}${SSH_KEY_FILE}" "${PEM_SERVER}";
|
|
fi
|
|
# post roate write timestamp into rotate file
|
|
if [ ${DRYRUN} -eq 0 ]; then
|
|
echo $(date +%s) > "${LAST_ROTATE}${hostname}_${username}.last-rotate";
|
|
fi
|
|
done
|
|
|
|
|
|
# __END__
|