193 lines
7 KiB
Bash
Executable file
193 lines
7 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
# distribute_ca_to_agents_v3.sh
|
|
#
|
|
# v3: Kann "intermediate" direkt aus Vault lesen (pki_mount/cert/ca),
|
|
# oder lokal "root" / "chain" vom Vault-Host kopieren.
|
|
#
|
|
# Beispiele:
|
|
# # Agents (Trust = INTERMEDIATE):
|
|
# sudo -E ./distribute_ca_to_agents_v3.sh \
|
|
# --env test --config ./config/apps.yaml \
|
|
# --which intermediate --users "nctest apptest"
|
|
#
|
|
# # Proxy einmalig mit Chain (I+R) befüllen:
|
|
# sudo -E ./distribute_ca_to_agents_v3.sh \
|
|
# --env test --config ./config/apps.yaml \
|
|
# --which chain --users proxytest \
|
|
# --dest "/home/proxytest/nginx/ca/current-ca-chain.pem"
|
|
#
|
|
# Optionen:
|
|
# --env <name> (z.B. test|prod)
|
|
# --config <file> (apps.yaml für vault_addr/pki_mount)
|
|
# --which intermediate|root|chain
|
|
# --src <pem> (überschreibt env/which für root/chain)
|
|
# --users "u1 u2 ..." (Space-separiert)
|
|
# --users-file <file> (ein User pro Zeile)
|
|
# --dest <PATH> (Ziel-Datei; Default: /home/<user>/vault/ca/ca.pem)
|
|
# --pki <mount> (übersteuert pki_mount aus config)
|
|
# --addr <URL> (übersteuert vault_addr aus config)
|
|
# --force (erzwingt Überschreiben)
|
|
#
|
|
# Erwartet für "intermediate":
|
|
# VAULT_TOKEN im Env (oder VAULT_ADMIN_TOKEN) + VAULT_ADDR (aus config/--addr)
|
|
#
|
|
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 → PEM-Check wird übersprungen"
|
|
command -v jq >/dev/null 2>&1 || warn "jq nicht gefunden → nur Datei-Mode verfügbar"
|
|
|
|
ENV_NAME=""; CFG=""; WHICH=""; SRC=""
|
|
USERS=""; USERS_FILE=""; DEST_OVERRIDE=""
|
|
PKI_MOUNT_OVERRIDE=""; ADDR_OVERRIDE=""
|
|
FORCE=0
|
|
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
--env) ENV_NAME="$2"; shift 2;;
|
|
--config) CFG="$2"; shift 2;;
|
|
--which) WHICH="$2"; shift 2;;
|
|
--src) SRC="$2"; shift 2;;
|
|
--users) USERS="$2"; shift 2;;
|
|
--users-file) USERS_FILE="$2"; shift 2;;
|
|
--dest) DEST_OVERRIDE="$2"; shift 2;;
|
|
--pki) PKI_MOUNT_OVERRIDE="$2"; shift 2;;
|
|
--addr) ADDR_OVERRIDE="$2"; shift 2;;
|
|
--force) FORCE=1; shift;;
|
|
-h|--help) sed -n '1,200p' "$0"; exit 0;;
|
|
*) err "unknown arg: $1"; exit 2;;
|
|
esac
|
|
done
|
|
|
|
[[ -n "$WHICH" ]] || { err "--which intermediate|root|chain ist Pflicht"; exit 2; }
|
|
if [[ -z "$SRC" && -z "$ENV_NAME" ]]; then
|
|
err "entweder --src ODER --env angeben"; exit 2
|
|
fi
|
|
|
|
# ---- Users einsammeln ----
|
|
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 "keine Nutzer angegeben (nutze --users oder --users-file)"; exit 2; }
|
|
|
|
# ---- Config laden (falls nötig) ----
|
|
CFGJSON=""
|
|
if [[ -n "$CFG" ]]; then
|
|
need python3; need jq
|
|
CFG_ABS="$(readlink -f "$CFG" 2>/dev/null || true)"
|
|
[[ -n "$CFG_ABS" && -r "$CFG_ABS" ]] || { err "config nicht lesbar: $CFG"; exit 2; }
|
|
CFGJSON="$(python3 - <<PY
|
|
import yaml, json, sys
|
|
print(json.dumps(yaml.safe_load(open("$CFG_ABS","r",encoding="utf-8"))))
|
|
PY
|
|
)"
|
|
fi
|
|
|
|
jqenv(){ echo "$CFGJSON" | jq -r ".environments.\"$ENV_NAME\"$1"; }
|
|
|
|
VAULT_ADDR="${ADDR_OVERRIDE:-}"
|
|
PKI_MOUNT="${PKI_MOUNT_OVERRIDE:-}"
|
|
|
|
if [[ -z "$SRC" ]]; then
|
|
case "$WHICH" in
|
|
root|chain)
|
|
SRC="/home/vault/tls-${ENV_NAME}/$([[ $WHICH == root ]] && echo root_ca.pem || echo ca_chain.pem)"
|
|
;;
|
|
intermediate)
|
|
[[ -n "$CFGJSON" ]] || { err "--config apps.yaml ist für 'intermediate' nötig"; exit 2; }
|
|
[[ -n "$VAULT_ADDR" ]] || VAULT_ADDR="$(jqenv '.vault_addr')"
|
|
[[ -n "$PKI_MOUNT" ]] || PKI_MOUNT="$(jqenv '.pki_mount')"
|
|
[[ -n "$VAULT_ADDR" && "$VAULT_ADDR" != "null" ]] || { err "vault_addr fehlt (config/--addr)"; exit 2; }
|
|
[[ -n "$PKI_MOUNT" && "$PKI_MOUNT" != "null" ]] || { err "pki_mount fehlt (config/--pki)"; exit 2; }
|
|
export VAULT_ADDR
|
|
if [[ -z "${VAULT_TOKEN:-}" && -n "${VAULT_ADMIN_TOKEN:-}" ]]; then
|
|
export VAULT_TOKEN="$VAULT_ADMIN_TOKEN"
|
|
fi
|
|
: "${VAULT_TOKEN:?VAULT_TOKEN oder VAULT_ADMIN_TOKEN im Env erforderlich für --which intermediate}"
|
|
need vault
|
|
TMP_SRC="$(mktemp)"
|
|
if vault read -format=json "${PKI_MOUNT}/cert/ca" | jq -r '.data.certificate' >"$TMP_SRC" && [[ -s "$TMP_SRC" ]]; then
|
|
SRC="$TMP_SRC"
|
|
else
|
|
err "konnte Intermediate aus Vault nicht lesen (${PKI_MOUNT}/cert/ca)"
|
|
exit 3
|
|
fi
|
|
;;
|
|
*) err "--which muss intermediate|root|chain sein"; exit 2;;
|
|
esac
|
|
fi
|
|
|
|
sudo test -r "$SRC" || { err "Quelle nicht lesbar (via sudo): $SRC"; exit 3; }
|
|
if command -v openssl >/dev/null 2>&1; then
|
|
if ! sudo openssl x509 -in "$SRC" -noout >/dev/null 2>&1; then
|
|
warn "openssl konnte keinen einzelnen Cert-Block parsen (bei Chain normal)."
|
|
fi
|
|
fi
|
|
|
|
info "Quelle: $SRC"
|
|
[[ -n "$VAULT_ADDR" ]] && info "Vault: $VAULT_ADDR"
|
|
[[ -n "$PKI_MOUNT" ]] && info "PKI: $PKI_MOUNT"
|
|
info "Modus: $WHICH"
|
|
echo
|
|
|
|
# ---- Kopieren pro User ----
|
|
COPIED=0; SKIPPED=0; MISSING=0; ERRORS=0
|
|
|
|
for u in "${USERS_ARR[@]}"; do
|
|
if ! id -u "$u" >/dev/null 2>&1; then
|
|
warn "user nicht gefunden: $u → skip"; ((MISSING++)); continue
|
|
fi
|
|
HOME_DIR="$(getent passwd "$u" | cut -d: -f6)"; [[ -n "$HOME_DIR" ]] || HOME_DIR="/home/$u"
|
|
|
|
# Default-Ziel
|
|
DEST="${DEST_OVERRIDE}"
|
|
if [[ -z "$DEST" ]]; then
|
|
DEST_DIR="${HOME_DIR}/vault/ca"
|
|
[[ "$WHICH" == "chain" ]] && DEST_FILE="ca-chain.pem" || DEST_FILE="ca.pem"
|
|
DEST="${DEST_DIR}/${DEST_FILE}"
|
|
fi
|
|
|
|
# Wenn mehrere Nutzer und ein absoluter identischer --dest → verhindern Clash
|
|
if (( ${#USERS_ARR[@]} > 1 )) && [[ -n "$DEST_OVERRIDE" && "$DEST_OVERRIDE" = /* ]]; then
|
|
err "--dest ist absolut und mehrere Nutzer angegeben → Pfad-Kollision. Bitte ohne --dest oder je User separat ausführen."
|
|
exit 2
|
|
fi
|
|
|
|
DEST_DIR="$(dirname "$DEST")"
|
|
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] existiert → skip (nutze --force zum Überschreiben): $DEST"; ((SKIPPED++)); continue
|
|
fi
|
|
|
|
if sudo install -m 0644 -o "$u" -g "$u" "$SRC" "$DEST"; then
|
|
ok "[$u] geschrieben: $DEST"
|
|
((COPIED++))
|
|
else
|
|
err "[$u] schreiben fehlgeschlagen: $DEST"
|
|
((ERRORS++))
|
|
fi
|
|
done
|
|
|
|
echo
|
|
ok "Fertig. Copied=${COPIED}, Skipped=${SKIPPED}, MissingUsers=${MISSING}, Errors=${ERRORS}"
|
|
|