121 lines
4.5 KiB
Bash
121 lines
4.5 KiB
Bash
#!/usr/bin/env bash
|
|
set -Eeuo pipefail
|
|
# vault-agent-post-leaf.sh
|
|
# VERSION: v1.1 (2025-09-30)
|
|
#
|
|
# ========================= FEATURE MANIFEST (v1.1) =========================
|
|
# 🔐 Write leaf material
|
|
# • Liest .issue.json (vom Vault Agent Template) und schreibt:
|
|
# - <OUTDIR>/<APP_NAME>.key (0600)
|
|
# - <OUTDIR>/<APP_NAME>.fullchain.pem (0644)
|
|
# • Sichere Staging-Dir-Nutzung (umask 077), atomar via install/cp.
|
|
#
|
|
# 🧾 Optional X.509 Kurzinfo
|
|
# • Zeigt subject des Leaf-Zertifikats (falls openssl verfügbar).
|
|
#
|
|
# ♻️ Container Reload (optional)
|
|
# • Wenn RELOAD_TLS_LABEL gesetzt und podman vorhanden:
|
|
# - reload aller Container mit Label (nginx -s reload, falls vorhanden).
|
|
#
|
|
# 🪝 NEU (v1.1): Default CLI Token setzen
|
|
# • Spiegelt das vom Agent erzeugte Token nach ~/.vault-token:
|
|
# - bevorzugt Symlink ~/.vault-token -> ~/.vault-agent-<app>/token
|
|
# - Fallback: Kopie (0400)
|
|
# • Dadurch nutzt die Vault-CLI automatisch das richtige Token,
|
|
# auch ohne VAULT_TOKEN zu exportieren.
|
|
# ==========================================================================
|
|
|
|
|
|
# ===== Logs (bestehendes Format NICHT ändern) =====
|
|
if [[ -t 1 ]]; then B=$'\e[1m'; R=$'\e[0m'; G=$'\e[32m'; Y=$'\e[33m'; E=$'\e[31m'; else B= R= G= Y= E=; fi
|
|
info(){ echo -e "🟦 ${G}${B}[INFO]${R} $*"; }
|
|
ok(){ echo -e "🟩 ${G}${B}[ OK ]${R} $*"; }
|
|
warn(){ echo -e "🟨 ${Y}${B}[WARN]${R} $*"; }
|
|
die(){ echo -e "🟥 ${E}${B}[FAIL]${R} $*" >&2; exit 1; }
|
|
|
|
|
|
# ===== Context =====
|
|
SCRIPT_DIR="$(cd -- "$(dirname -- "$0")" && pwd)"
|
|
AGENT_DIR="$(dirname -- "$SCRIPT_DIR")" # …/.vault-agent-<app>
|
|
APP_NAME="${APP_NAME:-${AGENT_DIR##*.vault-agent-}}"
|
|
JSON_ISSUE="${JSON_ISSUE:-${AGENT_DIR}/.issue.json}" # vom template erzeugt
|
|
OUTDIR="${OUTDIR:-$HOME/tls}"
|
|
RELOAD_TLS_LABEL="${RELOAD_TLS_LABEL:-}" # optional: Container-Reload per Label
|
|
|
|
command -v jq >/dev/null || die "jq not found"
|
|
command -v openssl >/dev/null || warn "openssl not found → no x509 summary"
|
|
|
|
[[ -r "$JSON_ISSUE" ]] || die "render JSON missing: $JSON_ISSUE"
|
|
|
|
umask 077
|
|
mkdir -p "$OUTDIR"
|
|
|
|
|
|
# ===== Extract & Write =====
|
|
TMP="$(mktemp -d "$OUTDIR/.staging.XXXX")"; trap 'rm -rf "$TMP"' EXIT
|
|
|
|
# private key
|
|
jq -r '.private_key // empty' "$JSON_ISSUE" > "$TMP/${APP_NAME}.key"
|
|
[[ -s "$TMP/${APP_NAME}.key" ]] || die "no private_key in $JSON_ISSUE"
|
|
|
|
# fullchain = certificate + (ca_chain | issuing_ca)
|
|
jq -r '
|
|
.certificate,
|
|
(if (.ca_chain|type=="array") then (.ca_chain|join("\n"))
|
|
else if (.ca_chain|type=="string") then .ca_chain
|
|
else .issuing_ca end end)
|
|
' "$JSON_ISSUE" > "$TMP/${APP_NAME}.fullchain.pem"
|
|
[[ -s "$TMP/${APP_NAME}.fullchain.pem" ]] || die "empty fullchain build"
|
|
|
|
install -m 600 "$TMP/${APP_NAME}.key" "$OUTDIR/${APP_NAME}.key"
|
|
install -m 644 "$TMP/${APP_NAME}.fullchain.pem" "$OUTDIR/${APP_NAME}.fullchain.pem"
|
|
ok "leaf written → $OUTDIR/${APP_NAME}.{key,fullchain.pem}"
|
|
|
|
# optional: brief x509
|
|
if command -v openssl >/dev/null; then
|
|
subj=$(awk 'BEGIN{p=0} /-----BEGIN CERTIFICATE-----/{p=1} p{print} /-----END CERTIFICATE-----/{exit}' \
|
|
"$OUTDIR/${APP_NAME}.fullchain.pem" | openssl x509 -noout -subject 2>/dev/null || true)
|
|
[[ -n "$subj" ]] && info "subject: ${subj#subject=}"
|
|
fi
|
|
|
|
|
|
# ===== NEW v1.1: make Agent-Token the default CLI token (~/.vault-token) =====
|
|
TOKEN_SRC="${AGENT_DIR}/token"
|
|
TOKEN_DST="${HOME}/.vault-token"
|
|
if [[ -r "$TOKEN_SRC" ]]; then
|
|
chmod 0400 "$TOKEN_SRC" 2>/dev/null || true
|
|
# Bevorzugt Symlink (immer „frisch“)
|
|
if ln -sfn "$TOKEN_SRC" "$TOKEN_DST" 2>/dev/null; then
|
|
info "default CLI token symlinked → ${TOKEN_DST} -> ${TOKEN_SRC}"
|
|
else
|
|
# Fallback: harte Kopie
|
|
umask 077
|
|
cp -f "$TOKEN_SRC" "$TOKEN_DST"
|
|
info "default CLI token copied → ${TOKEN_DST} (from ${TOKEN_SRC})"
|
|
fi
|
|
else
|
|
warn "agent token not found at ${TOKEN_SRC} → skip ${TOKEN_DST}"
|
|
fi
|
|
|
|
|
|
# ===== Optional container reload (label) =====
|
|
if [[ -n "$RELOAD_TLS_LABEL" ]]; then
|
|
if command -v podman >/dev/null 2>&1; then
|
|
info "reload label: $RELOAD_TLS_LABEL"
|
|
mapfile -t CIDS < <(podman ps --filter "label=${RELOAD_TLS_LABEL}" --format '{{.ID}}' | sed '/^$/d') || true
|
|
if (( ${#CIDS[@]} == 0 )); then
|
|
warn "no containers with label $RELOAD_TLS_LABEL → skip reload"
|
|
else
|
|
for cid in "${CIDS[@]}"; do
|
|
if podman exec "$cid" sh -lc 'nginx -t >/dev/null 2>&1 && nginx -s reload' >/dev/null 2>&1; then
|
|
ok "reload OK in ${cid}"
|
|
else
|
|
warn "reload FAILED in ${cid}"
|
|
fi
|
|
done
|
|
fi
|
|
else
|
|
warn "podman not found → skip reload"
|
|
fi
|
|
fi
|
|
|