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

160 lines
6.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.2.sh
# Datum: 2025-09-28
# ========================= FEATURE MANIFEST (mTLS v4.2) ======================
# 🔒 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: ~/.*/vault/mtls/{agent.crt, agent.key}
# 5) Schreibt Vault-TLS-CA nach: ~/.*/vault/ca/ca.pem
#
# 🛠️ 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>.<env>.privsec.ch (ttl=720h) aus.
#
# 🔐 Vault auth/cert
# 8) Aktiviert auth/cert (idempotent) und legt Mapping an:
# - certificate = Issuing CA der Client-Rolle
# - allowed_common_names = CN (agent-<app>.<env>.privsec.ch)
# - policies = pki-issue-<app>, token_ttl=24h
#
# 🔧 Ownership / Permissions
# 9) KEY 0600, CRT 0644, CA 0644; Owner = Linux-User aus YAML.
#
# 🧪 Ausgabe / Checks
# 10) Kurzer OpenSSL-Check (subject, notBefore/notAfter) + Pfadübersicht.
# 11) Warnung, falls Policy pki-issue-<app> noch nicht existiert (wird i.d.R. vom Agent-Script erzeugt).
#
# 🧰 CLI-Flags / Defaults
# 12) Flags: --env (default: test), --config (default: ./config/apps.yaml),
# --app (erforderlich), --pki-mount (optional Override).
#
# 🚦 Exit-Codes
# 13) 2=Args/Env unvollständig; 3=Linux-User fehlt; 6=PKI-Issue fehlgeschlagen.
# ============================================================================
# ------------------------- FILES & PATHS (mTLS v4.2) ----------------------------
# Schreibt Login-mTLS & CA (Owner=<user>):
# ~/vault/mtls/agent.crt (0644)
# ~/vault/mtls/agent.key (0600)
# ~/vault/ca/ca.pem (0644) ← Issuing-CA des Client-Zerts
#
# Vault (Konfiguration, keine Dateien):
# auth/cert enabled
# auth/cert/certs/<app>-<env> → certificate=(Issuing CA), allowed_common_names=agent-<app>.<env>.privsec.ch,
# policies=pki-issue-<app>, token_ttl=24h
#
# Hinweis:
# Für TLS-Verify ggü. Vault-Server ggf. die Server-CA/Chain in ~/vault/ca/ca.pem ablegen,
# falls nicht identisch mit der Issuing-CA.
# --------------------------------------------------------------------------------
# ===== Dependencies =====
need(){ command -v "$1" >/dev/null || { echo "missing: $1" >&2; exit 2; }; }
need vault; need jq; need python3; need install; need openssl
# ===== Defaults / Args =====
: "${DEFAULT_ENV:=test}"
: "${DEFAULT_CONFIG:=./config/apps.yaml}"
[[ -n "${VAULT_ADDR:-}" && -n "${VAULT_TOKEN:-}" ]] || { echo "VAULT_ADDR/VAULT_TOKEN required" >&2; exit 2; }
ENV_NAME="$DEFAULT_ENV"; CFG="$DEFAULT_CONFIG"; APPN=""; PKI_MOUNT_OVERRIDE=""
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;;
-h|--help) sed -n '1,200p' "$0"; exit 0;;
*) echo "unknown arg: $1" >&2; exit 2;;
esac
done
[[ -n "$APPN" ]] || { echo "--app required" >&2; exit 2; }
# ===== YAML laden (robust, Argument an Python übergeben) =====
CFG_ABS="$(readlink -f -- "$CFG")" || { echo "cannot resolve $CFG" >&2; 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
)" || { echo "YAML parse failed: $CFG_ABS" >&2; 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" ]] || { echo "pki_mount missing (YAML/env or --pki-mount)" >&2; exit 2; }
[[ -n "$APP_USER" && "$APP_USER" != "null" ]] || { echo "app user missing (YAML)" >&2; exit 2; }
id -u "$APP_USER" >/dev/null 2>&1 || { echo "Linux-User $APP_USER fehlt" >&2; exit 3; }
# ===== Pfade (müssen zu Agent v4.2 passen) =====
HOME_DIR="/home/${APP_USER}"
MTLS_DIR="${HOME_DIR}/vault/mtls"
CA_DIR="${HOME_DIR}/vault/ca"
CRT="${MTLS_DIR}/agent.crt"
KEY="${MTLS_DIR}/agent.key"
CA_FILE="${CA_DIR}/ca.pem"
install -d -m 0755 -o "$APP_USER" -g "$APP_USER" "$CA_DIR" "$MTLS_DIR"
# ===== PKI-Clientrolle & Zertifikat =====
ROLE="agent-mtls-${APPN}"
CN_AGENT="agent-${APPN}.${ENV_NAME}.privsec.ch" # app-spezifisch
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
ISSUE_JSON="$(vault write -format=json "${PKI_MOUNT}/issue/${ROLE}" common_name="${CN_AGENT}" ttl=720h)" \
|| { echo "PKI issue failed (role=${ROLE})" >&2; exit 6; }
# Dateien schreiben (Owner, Rechte)
echo "$ISSUE_JSON" | jq -r '.data.private_key' | install -m 0600 -o "$APP_USER" -g "$APP_USER" /dev/stdin "$KEY"
echo "$ISSUE_JSON" | jq -r '.data.certificate' | install -m 0644 -o "$APP_USER" -g "$APP_USER" /dev/stdin "$CRT"
# Hinweis: Diese CA ist die ausstellende CA des Client-Zerts. Für Vault-Server-TLS ggf. Server-CA/Chain hier ablegen.
echo "$ISSUE_JSON" | jq -r '.data.issuing_ca' | install -m 0644 -o "$APP_USER" -g "$APP_USER" /dev/stdin "$CA_FILE" || true
# ===== auth/cert aktivieren & Mapping setzen =====
vault auth enable cert >/dev/null 2>&1 || true
TMP="$(mktemp)"; jq -r '.data.issuing_ca' <<<"$ISSUE_JSON" > "$TMP"
vault write "auth/cert/certs/${APPN}-${ENV_NAME}" \
certificate=@"$TMP" \
allowed_common_names="${CN_AGENT}" \
policies="pki-issue-${APPN}" \
token_ttl="24h" >/dev/null
rm -f "$TMP"
# ===== Hinweis, falls Policy noch nicht existiert =====
if ! vault policy read "pki-issue-${APPN}" >/dev/null 2>&1; then
echo "WARN: Policy pki-issue-${APPN} existiert noch nicht das Agent-Script v4.2 erzeugt sie automatisch." >&2
fi
# ===== Zusammenfassung / Check =====
openssl x509 -in "$CRT" -noout -subject -dates | sed 's/^/ 🔎 /'
echo "OK: mTLS Material erzeugt (User=${APP_USER})"
echo " KEY: $KEY (0600)"
echo " CRT: $CRT (0644)"
echo " CA : $CA_FILE (0644) ← für Vault-TLS-Verify ggf. durch Server-CA ersetzen"
echo " auth/cert mapping: path=auth/cert/certs/${APPN}-${ENV_NAME}, CN=${CN_AGENT}, policy=pki-issue-${APPN}"
# Ende