vault-ops/infra/archiv/setup-vault-agent-mtls-client-config-v4.4.sh
2025-10-06 07:25:33 +02:00

214 lines
9.7 KiB
Bash
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
set -Eeuo pipefail
# setup-vault-agent-mtls-client-config-v4.4.sh
# Datum: 2025-09-29
# ========================= FEATURE MANIFEST (mTLS v4.4) ======================
# 🔒 Zweck / Sicherheit
# 1) Bereitet STRICT mTLS-Login für den Vault-Agent vor (auth/cert).
# 2) Keine AppRole, kein Fallback dieses Script erzeugt NUR Client-mTLS & Mapping.
#
# 📦 Konfiguration / YAML
# 3) Liest ./config/apps.yaml:
# environments.<env>.pki_mount
# environments.<env>.app.user (Default-App-User)
# apps[].{name, user?} (per-App User-Override)
#
# 🧩 Pfade (kompatibel zu Agent v4.2+)
# 4) Schreibt Login-mTLS nach: ~/<BASE>/mtls/{agent.crt,agent.key} (BASE=default "vault")
# 5) Schreibt Vault-TLS-CA nach: ~/<BASE>/ca/ca.pem
# ✅ Neu: BASE-Ordner via --home-base <ordner> (z. B. "vault-sidecar") wählbar.
#
# 🛠️ PKI / Client-Zert
# 6) Erzwingt/aktualisiert CLIENT-Rolle (client_flag=true, server_flag=false, EC P-256).
# 7) Stellt Client-Zert mit CN=agent-<app>-<suffix>.<env>.privsec.ch (ttl=720h) aus.
# ✅ Neu: <suffix> via --cn-suffix (Default: == <home-base>).
#
# 🔐 Vault auth/cert
# 8) Aktiviert auth/cert (idempotent) und legt Mapping an:
# - certificate = Issuing CA der Client-Rolle
# - allowed_common_names = CN
# - policies = pki-issue-<app>, token_ttl=24h
# ✅ Neu: Mapping-Name via --mapping-name (Default: <app>-<env>-<suffix>)
#
# 🔧 Ownership / Permissions
# 9) KEY 0600, CRT 0644, CA 0644; Owner = Linux-User aus YAML.
#
# 🧪 Ausgabe / Checks
# 10) OpenSSL-Checks (subject/issuer/dates) + farbige Zusammenfassung.
# 11) Warnung, falls Policy pki-issue-<app> noch nicht existiert (Agent-Setup erzeugt sie i.d.R.).
#
# 🧰 CLI-Flags / Defaults
# 12) Flags: --env (default: test), --config (default: ./config/apps.yaml),
# --app (erforderlich), --pki-mount (optional Override),
# --home-base <ordner> (default: vault),
# --cn-suffix <suffix> (default: == home-base),
# --mapping-name <name> (default: <app>-<env>-<suffix>)
#
# 🚦 Exit-Codes
# 13) 2=Args/Env unvollständig; 3=Linux-User fehlt; 6=PKI-Issue fehlgeschlagen.
# ============================================================================
#
# ================================ EXAMPLES ==================================
# --- Option B (umgekehrt): UNIX-User => ~/vault, Container => anderer Ordner ---
# • UNIX-User (Standard-Pfad ~/vault, Suffix=vault, Mapping=<app>-<env>-vault):
# sudo -E ./setup-vault-agent-mtls-client-config-v4.4.sh --app nctest --env test
# • Container/Sidecar (eigener Pfad ~/vault-sidecar, Suffix=sidecar, Mapping=nctest-test-sidecar):
# sudo -E ./setup-vault-agent-mtls-client-config-v4.4.sh --app nctest --env test --home-base vault-sidecar --cn-suffix sidecar --mapping-name nctest-test-sidecar
# • Weitere App (gleiches Muster):
# sudo -E ./setup-vault-agent-mtls-client-config-v4.4.sh --app apptest --env test
# sudo -E ./setup-vault-agent-mtls-client-config-v4.4.sh --app apptest --env test --home-base vault-sidecar --cn-suffix sidecar --mapping-name apptest-test-sidecar
# • PKI-Mount explizit:
# sudo -E ./setup-vault-agent-mtls-client-config-v4.4.sh --app nctest --pki-mount pki-test
# • Nur anderen BASE-Namen (Suffix wird automatisch == BASE):
# sudo -E ./setup-vault-agent-mtls-client-config-v4.4.sh --app nctest --home-base secrets
#
# Quick-Tests (nach dem Lauf):
# • CRT anzeigen: openssl x509 -in ~/<BASE>/mtls/agent.crt -noout -subject -issuer -dates
# • CA-Bundle Info: openssl x509 -in ~/<BASE>/ca/ca.pem -noout -subject -issuer -enddate || true
# • Mapping prüfen: vault read auth/cert/certs/<mapping-name>
# • Live-Check: vault-tls-check --addr 127.0.0.1:22300 --sni vault.test.privsec.ch --cafile ~/<BASE>/ca/ca.pem --mtls-cert ~/<BASE>/mtls/agent.crt --mtls-key ~/<BASE>/mtls/agent.key
# ============================================================================
# ===== 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; }
# ===== Dependencies =====
need(){ command -v "$1" >/dev/null || { err "missing: $1"; exit 2; }; }
need vault; need jq; need python3; need install; need openssl
# ===== Defaults / Args =====
: "${DEFAULT_ENV:=test}"
: "${DEFAULT_CONFIG:=./config/apps.yaml}"
: "${DEFAULT_HOME_BASE:=vault}"
[[ -n "${VAULT_ADDR:-}" && -n "${VAULT_TOKEN:-}" ]] || { err "VAULT_ADDR/VAULT_TOKEN required"; exit 2; }
ENV_NAME="$DEFAULT_ENV"; CFG="$DEFAULT_CONFIG"; APPN=""
PKI_MOUNT_OVERRIDE=""; HOME_BASE="$DEFAULT_HOME_BASE"
CN_SUFFIX=""; MAPPING_NAME=""
while [[ $# -gt 0 ]]; do
case "$1" in
--env) ENV_NAME="$2"; shift 2;;
--config) CFG="$2"; shift 2;;
--app) APPN="$2"; shift 2;;
--pki-mount) PKI_MOUNT_OVERRIDE="$2"; shift 2;;
--home-base) HOME_BASE="$2"; shift 2;;
--cn-suffix) CN_SUFFIX="$2"; shift 2;;
--mapping-name) MAPPING_NAME="$2"; shift 2;;
-h|--help) sed -n '1,260p' "$0"; exit 0;;
*) err "unknown arg: $1"; exit 2;;
esac
done
[[ -n "$APPN" ]] || { err "--app required"; exit 2; }
[[ -n "$HOME_BASE" ]] || { err "--home-base must be non-empty"; exit 2; }
case "$HOME_BASE" in /*|*/*|*..*) err "--home-base must be a simple name under the user's home (no '/', no '..')"; exit 2;; esac
# ===== YAML laden =====
CFG_ABS="$(readlink -f -- "$CFG")" || { err "cannot resolve $CFG"; exit 2; }
CFGJSON="$(python3 - "$CFG_ABS" <<'PY'
import yaml, json, sys, io
p = sys.argv[1]
with io.open(p, "r", encoding="utf-8") as f:
print(json.dumps(yaml.safe_load(f)))
PY
)" || { err "YAML parse failed: $CFG_ABS"; exit 2; }
jqenv(){ echo "$CFGJSON" | jq -r ".environments.\"$ENV_NAME\"$1"; }
jqapp(){ echo "$CFGJSON" | jq -r ".apps[] | select(.name==\"$APPN\")$1"; }
PKI_MOUNT="${PKI_MOUNT_OVERRIDE:-$(jqenv '.pki_mount')}"
APP_USER_DEF="$(jqenv '.app.user')"
APP_USER_APP="$(jqapp '.user')"
APP_USER="$APP_USER_DEF"; [[ "$APP_USER_APP" != "null" ]] && APP_USER="$APP_USER_APP"
[[ "$PKI_MOUNT" != "null" && -n "$PKI_MOUNT" ]] || { err "pki_mount missing (YAML/env or --pki-mount)"; exit 2; }
[[ -n "$APP_USER" && "$APP_USER" != "null" ]] || { err "app user missing (YAML)"; exit 2; }
id -u "$APP_USER" >/dev/null 2>&1 || { err "Linux-User $APP_USER fehlt"; exit 3; }
# ===== Pfade (BASE-Override) =====
HOME_DIR="/home/${APP_USER}"
BASE_DIR="${HOME_DIR}/${HOME_BASE}"
MTLS_DIR="${BASE_DIR}/mtls"
CA_DIR="${BASE_DIR}/ca"
CRT="${MTLS_DIR}/agent.crt"
KEY="${MTLS_DIR}/agent.key"
CA_FILE="${CA_DIR}/ca.pem"
sudo install -d -m 0755 -o "$APP_USER" -g "$APP_USER" "$BASE_DIR" "$CA_DIR" "$MTLS_DIR"
info "BASE=${BASE_DIR} MTLS=${MTLS_DIR} CA=${CA_DIR}"
# ===== Suffix/Mapping Defaults =====
: "${CN_SUFFIX:=${HOME_BASE}}"
: "${MAPPING_NAME:=${APPN}-${ENV_NAME}-${CN_SUFFIX}}"
ROLE="agent-mtls-${APPN}-${CN_SUFFIX}"
CN_AGENT="agent-${APPN}-${CN_SUFFIX}.${ENV_NAME}.privsec.ch"
info "CN=${CN_AGENT} role=${PKI_MOUNT}/roles/${ROLE} mapping=auth/cert/certs/${MAPPING_NAME}"
# ===== PKI-Clientrolle & Zertifikat =====
info "Upsert PKI client role…"
vault write "${PKI_MOUNT}/roles/${ROLE}" \
allowed_domains="${CN_AGENT}" \
allow_bare_domains=true \
allow_subdomains=false \
server_flag=false client_flag=true \
key_type="ec" key_bits=256 max_ttl="720h" >/dev/null || true
info "Issue client certificate…"
ISSUE_JSON="$(vault write -format=json "${PKI_MOUNT}/issue/${ROLE}" common_name="${CN_AGENT}" ttl=720h)" \
|| { err "PKI issue failed (role=${ROLE})"; exit 6; }
# Dateien schreiben (Owner, Rechte)
echo "$ISSUE_JSON" | jq -r '.data.private_key' | sudo install -m 0600 -o "$APP_USER" -g "$APP_USER" /dev/stdin "$KEY"
echo "$ISSUE_JSON" | jq -r '.data.certificate' | sudo install -m 0644 -o "$APP_USER" -g "$APP_USER" /dev/stdin "$CRT"
echo "$ISSUE_JSON" | jq -r '.data.issuing_ca' | sudo install -m 0644 -o "$APP_USER" -g "$APP_USER" /dev/stdin "$CA_FILE" || true
ok "mTLS material written → KEY=${KEY} CRT=${CRT} CA=${CA_FILE}"
# ===== auth/cert aktivieren & Mapping setzen =====
info "Enable auth/cert & upsert mapping…"
vault auth enable cert >/dev/null 2>&1 || true
TMP="$(mktemp)"; jq -r '.data.issuing_ca' <<<"$ISSUE_JSON" > "$TMP"
vault write "auth/cert/certs/${MAPPING_NAME}" \
certificate=@"$TMP" \
allowed_common_names="${CN_AGENT}" \
policies="pki-issue-${APPN}" \
token_ttl="24h" >/dev/null
rm -f "$TMP"
ok "Mapping updated → auth/cert/certs/${MAPPING_NAME} (policies=pki-issue-${APPN})"
# ===== Hinweis, falls Policy noch nicht existiert =====
if ! vault policy read "pki-issue-${APPN}" >/dev/null 2>&1; then
warn "Policy pki-issue-${APPN} existiert noch nicht das Agent-Setup v4.2+ erzeugt sie i.d.R. automatisch."
fi
# ===== Zusammenfassung / Checks =====
echo
ok "Certificate summary:"
openssl x509 -in "$CRT" -noout -subject -issuer -dates | sed 's/^/ 🔎 /'
if [[ -s "$CA_FILE" ]]; then
info "CA bundle peek:"
( set +e; openssl x509 -in "$CA_FILE" -noout -subject -issuer -enddate 2>/dev/null | sed 's/^/ 📜 /'; true )
fi
echo
ok "SUCCESS (v4.4)"
echo " User: ${APP_USER}"
echo " BASE: ${BASE_DIR}"
echo " CN: ${CN_AGENT}"
echo " Role: ${PKI_MOUNT}/roles/${ROLE}"
echo " Mapping: auth/cert/certs/${MAPPING_NAME}"
echo " Files: ${KEY} (0600), ${CRT} (0644), ${CA_FILE} (0644)"
echo
info "Next:"
echo " • Mount BASE for container if needed: -v ${BASE_DIR}:${BASE_DIR}:ro"
echo " • Test login via mTLS (example):"
echo " VAULT_ADDR=\"https://127.0.0.1:22300\" VAULT_CACERT=\"${CA_FILE}\" \\"
echo " vault login -method=cert -client-cert \"${CRT}\" -client-key \"${KEY}\""