vault-ops/infra/archiv/distribute_ca_to_agents.sh
2025-10-06 07:25:33 +02:00

106 lines
3.9 KiB
Bash
Executable file

#!/usr/bin/env bash
set -Eeuo pipefail
# Distribute a CA file from /home/vault/tls-<env>/... to /home/<user>/vault/ca/ca.pem
# Safe/idempotent: skips if identical; use --force to overwrite.
#
# Examples:
# ./distribute_ca_to_agents.sh --src "/home/vault/tls-test/root_ca.pem" --users "nctest apptest proxytest"
# ./distribute_ca_to_agents.sh --env test --which chain --users-file ./agents.txt
# (uses /home/vault/tls-test/ca_chain.pem)
#
# Options:
# --src <path> explicit source PEM (overrides --env/--which)
# --env <name> e.g. test|prod (builds /home/vault/tls-<env>/<file>)
# --which root|chain pick root_ca.pem (default) or ca_chain.pem
# --users "u1 u2" space-separated users
# --users-file <file> one user per line; # comments OK
# --force overwrite even if target exists
# -h|--help show help
# pretty logs
if [[ -t 1 ]]; then
B=$'\e[1m'; R=$'\e[0m'; BL=$'\e[34m'; G=$'\e[32m'; Y=$'\e[33m'; E=$'\e[31m'
else B= R= BL= G= Y= E=; fi
ts(){ date +"%Y-%m-%d %H:%M:%S"; }
info(){ echo -e "🟦 ${BL}${B}[$(ts)]${R} $*"; }
ok(){ echo -e "🟩 ${G}${B}[$(ts)]${R} $*"; }
warn(){ echo -e "🟨 ${Y}${B}[$(ts)]${R} $*"; }
err(){ echo -e "🟥 ${E}${B}[$(ts)]${R} $*" >&2; }
need(){ command -v "$1" >/dev/null 2>&1 || { err "missing: $1"; exit 2; }; }
need sudo; need install; need getent
command -v openssl >/dev/null 2>&1 || warn "openssl not found → skip PEM parse check"
SRC=""; ENV_NAME=""; WHICH="root"; USERS=""; USERS_FILE=""; FORCE=0
while [[ $# -gt 0 ]]; do
case "$1" in
--src) SRC="$2"; shift 2;;
--env) ENV_NAME="$2"; shift 2;;
--which) WHICH="$2"; shift 2;;
--users) USERS="$2"; shift 2;;
--users-file) USERS_FILE="$2"; shift 2;;
--force) FORCE=1; shift;;
-h|--help) sed -n '1,200p' "$0"; exit 0;;
*) err "unknown arg: $1"; exit 2;;
esac
done
# resolve source
if [[ -z "$SRC" ]]; then
[[ -n "$ENV_NAME" ]] || { err "Either --src or --env is required."; exit 2; }
case "$WHICH" in
root) SRC="/home/vault/tls-${ENV_NAME}/root_ca.pem" ;;
chain) SRC="/home/vault/tls-${ENV_NAME}/ca_chain.pem" ;;
*) err "--which must be root|chain"; exit 2;;
esac
fi
sudo test -r "$SRC" || { err "source not readable via sudo: $SRC"; exit 3; }
if command -v openssl >/dev/null 2>&1; then
sudo openssl x509 -in "$SRC" -noout >/dev/null 2>&1 || warn "openssl can't parse a single cert from $SRC (bundle is still OK)"
fi
# users
USERS_ARR=()
[[ -n "$USERS" ]] && read -r -a USERS_ARR <<<"$USERS"
if [[ -n "$USERS_FILE" ]]; then
[[ -r "$USERS_FILE" ]] || { err "users file not readable: $USERS_FILE"; exit 2; }
while IFS= read -r line; do
line="${line%%#*}"; line="$(echo "$line" | xargs || true)"
[[ -z "$line" ]] && continue
USERS_ARR+=("$line")
done < "$USERS_FILE"
fi
(( ${#USERS_ARR[@]} > 0 )) || { err "No users specified. Use --users or --users-file."; exit 2; }
info "Source: $SRC"
info "Users: ${USERS_ARR[*]}"
info "Target name: ca.pem"
info "Mode: $([[ $FORCE -eq 1 ]] && echo 'force overwrite' || echo 'skip if exists / identical')"
echo
COPIED=0; SKIPPED=0; MISSING=0
for u in "${USERS_ARR[@]}"; do
if ! id -u "$u" >/dev/null 2>&1; then
warn "user not found: $u → skip"; ((MISSING++)); continue
fi
HOME_DIR="$(getent passwd "$u" | cut -d: -f6)"; [[ -n "$HOME_DIR" ]] || HOME_DIR="/home/$u"
DEST_DIR="${HOME_DIR}/vault/ca"; DEST="${DEST_DIR}/ca.pem"
sudo install -d -m 0755 -o "$u" -g "$u" "$DEST_DIR" >/dev/null
if [[ -f "$DEST" && $FORCE -eq 0 ]] && sudo cmp -s "$SRC" "$DEST"; then
ok "[$u] up-to-date → ${DEST}"; ((SKIPPED++)); continue
fi
if [[ -f "$DEST" && $FORCE -eq 0 ]]; then
ok "[$u] exists → skip (use --force to overwrite): ${DEST}"; ((SKIPPED++)); continue
fi
if sudo install -m 0644 -o "$u" -g "$u" "$SRC" "$DEST"; then
ok "[$u] wrote ${DEST}"; ((COPIED++))
else
err "[$u] failed to write ${DEST}"
fi
done
echo
ok "Done. Copied: ${COPIED}, Skipped: ${SKIPPED}, Missing users: ${MISSING}"