You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
291 lines
7.4 KiB
291 lines
7.4 KiB
#!/bin/bash
|
|
|
|
RSYNC_KEY_PATH=/etc/rsync/keys
|
|
|
|
|
|
ANSI_ESC=$'\e['
|
|
|
|
NORMAL="${ANSI_ESC}0m"
|
|
|
|
GRAY="${ANSI_ESC}1;30m"
|
|
RED="${ANSI_ESC}1;31m"
|
|
GREEN="${ANSI_ESC}1;32m"
|
|
YELLOW="${ANSI_ESC}1;33m"
|
|
BLUE="${ANSI_ESC}1;34m"
|
|
PINK="${ANSI_ESC}1;35m"
|
|
CYAN="${ANSI_ESC}1;36m"
|
|
WHITE="${ANSI_ESC}1;37m"
|
|
|
|
DARKGRAY="${ANSI_ESC}0;30m"
|
|
DARKRED="${ANSI_ESC}0;31m"
|
|
DARKGREEN="${ANSI_ESC}0;32m"
|
|
DARKYELLOW="${ANSI_ESC}0;33m"
|
|
DARKBLUE="${ANSI_ESC}0;34m"
|
|
DARKPINK="${ANSI_ESC}0;35m"
|
|
DARKCYAN="${ANSI_ESC}0;36m"
|
|
DARKWHITE="${ANSI_ESC}0;37m"
|
|
|
|
read-0() {
|
|
local eof= IFS=''
|
|
while [ "$1" ]; do
|
|
read -r -d '' -- "$1" || eof=1
|
|
shift
|
|
done
|
|
[ -z "$eof" ]
|
|
}
|
|
|
|
|
|
col:normalize:size() {
|
|
local alignment="$1" colors="$2"
|
|
|
|
|
|
# Encode the associative array into a string for awk
|
|
local col_colors_string=""
|
|
for key in "${!col_colors[@]}"; do
|
|
col_colors_string+="${key}=${col_colors[$key]} "
|
|
done
|
|
|
|
# Pass the string to awk
|
|
awk -v alignment="$alignment" -v colors="$colors" -v colorStr="$col_colors_string" -v normal="$NORMAL" '
|
|
BEGIN {
|
|
# Split the color string into key-value pairs
|
|
n = split(colorStr, pairs);
|
|
for (i = 1; i <= n; i++) {
|
|
split(pairs[i], kv, "=");
|
|
col_colors[kv[1]] = kv[2];
|
|
}
|
|
|
|
}
|
|
{
|
|
# Store the entire line in the lines array
|
|
lines[NR] = $0;
|
|
|
|
# Split the line into fields and update max lengths
|
|
split($0, fields);
|
|
for (i = 1; i <= length(fields); i++) {
|
|
if (length(fields[i]) > max[i]) {
|
|
max[i] = length(fields[i]);
|
|
}
|
|
}
|
|
}
|
|
END {
|
|
# Print lines with fields padded to max, apply color
|
|
for (i = 1; i <= NR; i++) {
|
|
split(lines[i], fields);
|
|
line = "";
|
|
for (j = 1; j <= length(fields); j++) {
|
|
# Determine alignment
|
|
align = substr(alignment, j, 1) == "+" ? "+" : "-";
|
|
color_code = substr(colors, j, 1);
|
|
|
|
color_prefix = (color_code != "-" && color_code in col_colors) ? col_colors[color_code] : "";
|
|
color_suffix = (color_prefix != "") ? normal : "";
|
|
|
|
# Construct line with alignment and color
|
|
line = line color_prefix sprintf("%" align max[j] "s ", fields[j]) color_suffix;
|
|
}
|
|
print line;
|
|
}
|
|
}'
|
|
}
|
|
|
|
declare -A col_colors=(
|
|
[s]=${DARKGRAY} ## s for 'slate' (actually gray)
|
|
[r]=${DARKRED}
|
|
[g]=${DARKGREEN}
|
|
[y]=${DARKYELLOW}
|
|
[b]=${DARKBLUE}
|
|
[p]=${DARKPINK}
|
|
[c]=${DARKCYAN}
|
|
[w]=${DARKWHITE}
|
|
|
|
[S]=${GRAY}
|
|
[R]=${RED}
|
|
[G]=${GREEN}
|
|
[Y]=${YELLOW}
|
|
[B]=${BLUE}
|
|
[P]=${PINK}
|
|
[C]=${CYAN}
|
|
[W]=${WHITE}
|
|
)
|
|
|
|
ssh-key-ls() {
|
|
local label="$1" f content
|
|
shift
|
|
while read-0 f; do
|
|
[ -e "$f" ] || continue
|
|
ident=${f##*/}
|
|
if [ "$f" != "${f%.disabled}" ]; then
|
|
enabled="${RED}x"
|
|
ident=${ident%.disabled}
|
|
else
|
|
enabled="${GREEN}✓"
|
|
fi
|
|
ident=${ident%.pub}
|
|
content=$(cat "$f") || return 1
|
|
key=${content#* }
|
|
key=${key% *}
|
|
commentary=${content##* }
|
|
type=${commentary%%@*}
|
|
# printf "${DARKGRAY}..${NORMAL}%12s ${DARKPINK}%-7s${NORMAL} ${DARKCYAN}%s${NORMAL}\n" \
|
|
# "${key: -12}" "${type}" "$ident"
|
|
printf "%s %s %s %s\n" "…${key: -12}" "$type" "$enabled" "$ident"
|
|
done < <(find "${RSYNC_KEY_PATH}"/backup/"$label" \
|
|
-maxdepth 1 -mindepth 1 \
|
|
\( -name \*.pub -or -name \*.pub.disabled \) -print0 | sort -z
|
|
) | col:normalize:size "----" "ysGc"
|
|
}
|
|
|
|
|
|
ssh-key-rm() {
|
|
local label="$1" ident="$2" delete
|
|
|
|
delete="${RSYNC_KEY_PATH}/backup/$label/$ident.pub"
|
|
if ! [ -e "$delete" ]; then
|
|
if ! [ -e "${delete}.disabled" ]; then
|
|
echo "Error: key '$ident' not found." >&2
|
|
return 1
|
|
fi
|
|
rm "${delete}.disabled"
|
|
return 0
|
|
fi
|
|
rm "$delete"
|
|
/usr/local/sbin/ssh-update-keys
|
|
}
|
|
|
|
ssh-key-disable() {
|
|
local label="$1" ident="$2" disable
|
|
|
|
disable="${RSYNC_KEY_PATH}/backup/$label/$ident.pub"
|
|
if ! [ -e "$disable" ]; then
|
|
if ! [ -e "${disable}.disabled" ]; then
|
|
echo "Error: key '$ident' not found." >&2
|
|
return 1
|
|
fi
|
|
echo "Already disabled." >&2
|
|
return 0
|
|
fi
|
|
mv "${disable}" "${disable}.disabled"
|
|
|
|
/usr/local/sbin/ssh-update-keys
|
|
}
|
|
|
|
ssh-key-enable() {
|
|
local label="$1" ident="$2" enable
|
|
|
|
enable="${RSYNC_KEY_PATH}/backup/$label/$ident.pub.disabled"
|
|
if ! [ -e "$enable" ]; then
|
|
if ! [ -e "${enable%.disabled}" ]; then
|
|
echo "Error: key '$ident' not found." >&2
|
|
return 1
|
|
fi
|
|
echo "Already enabled." >&2
|
|
return 0
|
|
fi
|
|
mv "${enable}" "${enable%.disabled}"
|
|
|
|
/usr/local/sbin/ssh-update-keys
|
|
}
|
|
|
|
|
|
ssh-key-get-type() {
|
|
local label="$1" ident="$2" key content commentary
|
|
|
|
key="${RSYNC_KEY_PATH}/backup/$label/$ident.pub"
|
|
if ! [ -e "$key" ]; then
|
|
echo "Error: key '$ident' not found." >&2
|
|
return 1
|
|
fi
|
|
content=$(cat "$key") || return 1
|
|
commentary=${content##* }
|
|
printf "%s\n" "${commentary%%@*}"
|
|
}
|
|
|
|
|
|
ssh-key-add() {
|
|
local label="$1" type="$2" key="$3" email="$4"
|
|
|
|
[ "$type" == "ssh-rsa" ] || {
|
|
echo "Error: expecting ssh-rsa key type" >&2
|
|
return 1
|
|
}
|
|
|
|
## ident are unique by construction (they are struct keys)
|
|
## but keys need to be also unique
|
|
declare -A keys
|
|
content="$type $key $email"
|
|
ident="${email##*@}"
|
|
target="${RSYNC_KEY_PATH}/backup/$label/$ident.pub"
|
|
|
|
## is key used already ? As key give access to a specified subdir,
|
|
## we need to make sure it is unique.
|
|
|
|
for key_file in "${RSYNC_KEY_PATH}/backup/"*/*.pub; do
|
|
[ -e "$key_file" ] || continue
|
|
key_content=$(cat "$key_file")
|
|
if [ "$type $key" == "${key_content% *}" ]; then
|
|
if [ "$key_file" == "$target" ]; then
|
|
echo "Provided key already present for '$ident'." >&2
|
|
return 0
|
|
elif [[ "$key_file" == "${RSYNC_KEY_PATH}/"*"/$label/"*.pub ]]; then
|
|
type=${key_file#"${RSYNC_KEY_PATH}/"}
|
|
type=${type%"/$label/"*.pub}
|
|
key_ident=${key_file##*/}
|
|
key_ident=${key_ident%.pub}
|
|
echo "Provided key already used as $type key for '$key_ident'." >&2
|
|
return 1
|
|
else
|
|
olabel=${key_file#"${RSYNC_KEY_PATH}/"*/}
|
|
olabel=${olabel%/*.pub}
|
|
echo "Specified key is already used by '$olabel' account, please pick another one." >&2
|
|
return 1
|
|
fi
|
|
fi
|
|
done
|
|
|
|
mkdir -p "${target%/*}"
|
|
if [ -e "$target" ]; then
|
|
echo "Replacing key for '$ident'." >&2
|
|
elif [ -e "${RSYNC_KEY_PATH}/"*"/"*"/$ident.pub" ]; then
|
|
olabel=("${RSYNC_KEY_PATH}/"*"/"*"/$ident.pub")
|
|
olabel="${olabel[0]}"
|
|
olabel=${olabel#"${RSYNC_KEY_PATH}/"*/}
|
|
olabel=${olabel%/*.pub}
|
|
echo "ident '$ident' is already reserved by '$olabel', please pick another one." >&2
|
|
return 1
|
|
fi
|
|
echo "$content" > "$target"
|
|
|
|
/usr/local/sbin/ssh-update-keys
|
|
}
|
|
|
|
|
|
|
|
|
|
case "$1" in
|
|
"add")
|
|
shift
|
|
ssh-key-add "$@"
|
|
;;
|
|
"rm")
|
|
shift
|
|
ssh-key-rm "$@"
|
|
;;
|
|
"ls")
|
|
shift
|
|
ssh-key-ls "$@"
|
|
;;
|
|
"disable"|"enable")
|
|
arg="$1"
|
|
shift
|
|
ssh-key-"$arg" "$@"
|
|
;;
|
|
"get-type")
|
|
shift
|
|
ssh-key-get-type "$@"
|
|
;;
|
|
*)
|
|
echo "Unknown command '$1'."
|
|
;;
|
|
esac
|
|
|