160 lines
6.7 KiB
Bash
Executable file
160 lines
6.7 KiB
Bash
Executable file
#!/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
|
||
|