From c9b2d3e5a9e1f7b8328a241d7a900974a54c5cd6 Mon Sep 17 00:00:00 2001 From: Valentin Lab Date: Thu, 18 Apr 2024 19:46:26 +0200 Subject: [PATCH] new: [rsync-backup-target] add ``ssh-key {enable|disable} IDENT`` admin command --- rsync-backup-target/build/Dockerfile | 3 +- .../src/usr/local/sbin/ssh-admin-cmd-validate | 9 ++ .../build/src/usr/local/sbin/ssh-key | 151 +++++++++++++++++- 3 files changed, 156 insertions(+), 7 deletions(-) diff --git a/rsync-backup-target/build/Dockerfile b/rsync-backup-target/build/Dockerfile index 28fe600..e57911c 100644 --- a/rsync-backup-target/build/Dockerfile +++ b/rsync-backup-target/build/Dockerfile @@ -4,7 +4,8 @@ MAINTAINER Valentin Lab ## coreutils is for ``date`` support of ``--rfc-3339=seconds`` argument. ## findutils is for ``find`` support of ``--newermt`` argument. -RUN apk add rsync sudo bash openssh-server coreutils findutils +## gawk is for ``awk`` support of unicode strings. +RUN apk add rsync sudo bash openssh-server coreutils findutils gawk RUN ssh-keygen -A ## New user/group rsync/rsync with home dir in /var/lib/rsync diff --git a/rsync-backup-target/build/src/usr/local/sbin/ssh-admin-cmd-validate b/rsync-backup-target/build/src/usr/local/sbin/ssh-admin-cmd-validate index 4f8e7cc..9876db6 100755 --- a/rsync-backup-target/build/src/usr/local/sbin/ssh-admin-cmd-validate +++ b/rsync-backup-target/build/src/usr/local/sbin/ssh-admin-cmd-validate @@ -80,6 +80,15 @@ elif [[ "$SSH_ORIGINAL_COMMAND" =~ ^"ssh-key get-type "[a-zA-Z0-9._-]+$ ]]; then # echo "Would accept: $SSH_ORIGINAL_COMMAND" >&2 exec sudo /usr/local/sbin/ssh-key get-type "$label" "${ssh_args[@]:2}" +elif [[ "$SSH_ORIGINAL_COMMAND" =~ ^"ssh-key "(enable|disable)" "[a-zA-Z0-9._-]+$ ]]; then + log "ACCEPTED: $SSH_ORIGINAL_COMMAND" + + ## Interpret \ to allow passing spaces (want to avoid possible issue with \n) + #read -a ssh_args <<< "${SSH_ORIGINAL_COMMAND}" + ssh_args=(${SSH_ORIGINAL_COMMAND}) + + # echo "Would accept: $SSH_ORIGINAL_COMMAND" >&2 + exec sudo /usr/local/sbin/ssh-key "${ssh_args[1]}" "$label" "${ssh_args[@]:2}" elif [[ "$SSH_ORIGINAL_COMMAND" =~ ^"request-recovery-key "[a-zA-Z0-9._-]+$ ]]; then log "ACCEPTED: $SSH_ORIGINAL_COMMAND" diff --git a/rsync-backup-target/build/src/usr/local/sbin/ssh-key b/rsync-backup-target/build/src/usr/local/sbin/ssh-key index 1eff620..1c2765a 100755 --- a/rsync-backup-target/build/src/usr/local/sbin/ssh-key +++ b/rsync-backup-target/build/src/usr/local/sbin/ssh-key @@ -25,18 +25,115 @@ 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 - for f in "${RSYNC_KEY_PATH}"/backup/"$label"/*.pub; do + 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") + content=$(cat "$f") || return 1 key=${content#* } key=${key% *} - printf "${DARKGRAY}..${NORMAL}%24s ${DARKCYAN}%s${NORMAL}\n" "${key: -24}" "$ident" - done + 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" } @@ -45,10 +142,47 @@ ssh-key-rm() { delete="${RSYNC_KEY_PATH}/backup/$label/$ident.pub" if ! [ -e "$delete" ]; then - echo "Error: key '$ident' not found." >&2 - return 1 + 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 } @@ -141,6 +275,11 @@ case "$1" in shift ssh-key-ls "$@" ;; + "disable"|"enable") + arg="$1" + shift + ssh-key-"$arg" "$@" + ;; "get-type") shift ssh-key-get-type "$@"