VaultTest/vault-bootstrap.sh
Murat ÖZDEMİR 508363fc75 refactor(vault): Replace transit auto-unseal with Shamir + Docker secret
Remove vault-transit service entirely. Each vault node now auto-unseals at
startup by reading the Shamir unseal key from a Docker secret managed by
vault-bootstrap.sh. Eliminates the transit token expiry failure mode and
removes the vault_transit node-pinning requirement.

Changes:
- docker-stack-vault.yml: remove vault-transit service, vault_transit_config,
  vault-transit-data-vl, transit_master_token / vault_transit_unseal_key
  secrets; add vault_unseal_key secret; rewrite vault entrypoint to background
  start + poll + auto-unseal loop
- vault-template-v1.json, vault-template-v2.json: remove seal.transit block
- vault-template-transit.json: deleted (vault-transit is gone)
- vault-bootstrap.sh: full rewrite — node-agnostic run_vault() helper (docker
  exec fallback to docker run over overlay network), 7-step Shamir flow with
  SKIP_DEPLOY support and early-exit when vault is already healthy
- deploy-prod.yml: replace BE-Forecast deploy with vault stack deploy +
  bootstrap (SKIP_DEPLOY=true) + cluster health check
2026-06-10 13:37:32 +03:00

167 lines
9.1 KiB
Bash
Executable File
Raw 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.

#!/bin/bash
# vault-bootstrap.sh — Vault HA cluster bootstrap (Shamir seal, Docker secret)
# Node-agnostic: uses docker exec for local replicas, falls back to the overlay
# network via docker run when no local replica is found on this Swarm manager.
set -euo pipefail
# ─── Configuration ───────────────────────────────────────────────────
STACK_NAME="iklimco"
STACK_FILE="$(cd "$(dirname "$0")" && pwd)/docker-stack-vault.yml"
OUT_DIR="/tmp/vault-bootstrap"
SKIP_DEPLOY="${SKIP_DEPLOY:-false}"
# ─────────────────────────────────────────────────────────────────────
mkdir -p "$OUT_DIR"
MAIN_INIT_FILE="$OUT_DIR/main-vault-init.txt"
# ─── Logging ─────────────────────────────────────────────────────────
step() { echo; echo "════════════════════════════════════════════════"; echo " [$(date '+%H:%M:%S')] $*"; echo "════════════════════════════════════════════════"; }
ok() { echo " [OK] $*"; }
info() { echo " --> $*"; }
fail() { echo; echo " [HATA] $*" >&2; exit 1; }
trap 'echo; echo " [HATA] Script satir $LINENO'"'"'de beklenmedik sekilde sonlandi" >&2' ERR
# ─────────────────────────────────────────────────────────────────────
# ─── Helpers ─────────────────────────────────────────────────────────
wait_service_running() {
local svc="$1" expected="$2" timeout="${3:-180}" elapsed=0
info "Bekleniyor: $svc ($expected running task)..."
while [ "$elapsed" -lt "$timeout" ]; do
running=$(docker service ps "$svc" \
--filter "desired-state=running" \
--format '{{.CurrentState}}' 2>/dev/null \
| grep -c "^Running" || true)
if [ "$running" -ge "$expected" ]; then
ok "$svc hazir: $running/$expected"
return 0
fi
sleep 5; elapsed=$((elapsed+5))
echo " ${elapsed}s/${timeout}s — running: $running/$expected"
done
fail "$svc $timeout saniye icinde hazir olmadi"
}
# Run a vault CLI command — uses docker exec if a vault replica is on this node,
# otherwise falls back to the overlay network via docker run.
VAULT_TOKEN=""
run_vault() {
local cmd="$*"
[ -n "$VAULT_TOKEN" ] && cmd="VAULT_TOKEN=$VAULT_TOKEN $cmd"
local cid
cid=$(docker ps -q -f "name=${STACK_NAME}_vault\." | head -1 || true)
if [ -n "$cid" ]; then
docker exec -i "$cid" sh -c "VAULT_ADDR=https://127.0.0.1:8200 VAULT_SKIP_VERIFY=true $cmd"
else
docker run --rm -i --network iklimco-net hashicorp/vault:2.0.1 \
sh -c "VAULT_ADDR=https://vault.iklim.co:8200 VAULT_SKIP_VERIFY=true $cmd"
fi
}
# ─────────────────────────────────────────────────────────────────────
# ━━━ ADIM 0 — On kosullar ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
step "ADIM 0 — On kosullar kontrol ediliyor"
docker node ls &>/dev/null || fail "Swarm manager node gerekli"
[ -f "$STACK_FILE" ] || fail "Stack dosyasi bulunamadi: $STACK_FILE"
ok "On kosullar tamam"
# ━━━ ADIM 1 — Placeholder secret ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
step "ADIM 1 — vault_unseal_key kontrol ediliyor"
if docker secret ls --format '{{.Name}}' | grep -q '^vault_unseal_key'; then
info "vault_unseal_key mevcut, atlaniyor"
else
echo "bootstrap" | docker secret create vault_unseal_key - >/dev/null
ok "vault_unseal_key (placeholder) olusturuldu"
fi
# ━━━ ADIM 2 — Stack deploy ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
step "ADIM 2 — Stack deploy"
if [ "$SKIP_DEPLOY" = "true" ]; then
info "SKIP_DEPLOY=true — atlaniyor"
else
docker stack deploy --with-registry-auth -c "$STACK_FILE" "$STACK_NAME"
ok "Stack deploy edildi"
fi
# ━━━ ADIM 3 — Vault cluster bekleniyor ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
step "ADIM 3 — Vault cluster bekleniyor"
wait_service_running "${STACK_NAME}_vault" 3 300
sleep 10
# ━━━ ADIM 4 — Vault durum kontrolu ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
step "ADIM 4 — Vault durum kontrolu"
VAULT_STATUS_OUT=$(run_vault "vault status 2>/dev/null" || true)
VAULT_INITIALIZED=$(echo "$VAULT_STATUS_OUT" | awk '/^Initialized/{print $2}')
VAULT_SEALED=$(echo "$VAULT_STATUS_OUT" | awk '/^Sealed/{print $2}')
info "Initialized: ${VAULT_INITIALIZED:-unknown}, Sealed: ${VAULT_SEALED:-unknown}"
if [ "$VAULT_INITIALIZED" = "true" ] && [ "$VAULT_SEALED" = "false" ]; then
ok "Vault zaten initialize edilmis ve unsealed"
echo
echo "════════════════════════════════════════════════"
echo " BOOTSTRAP TAMAMLANDI (Vault saglıklı)"
echo "════════════════════════════════════════════════"
exit 0
fi
# ━━━ ADIM 5 — Vault initialize (gerekirse) ━━━━━━━━━━━━━━━━━━━━━━━━━
step "ADIM 5 — Vault initialize / unseal key hazirlaniyor"
if [ "$VAULT_INITIALIZED" = "true" ]; then
# Vault is sealed but initialized. This happens when the vault_unseal_key Docker secret
# contains the wrong value (e.g., placeholder was never replaced). Provide the init file
# so the real key can be extracted and pushed to the secret.
info "Vault sealed ama initialize edilmis — mevcut init dosyasi kullanilacak"
[ -f "$MAIN_INIT_FILE" ] && grep -q "Unseal Key 1" "$MAIN_INIT_FILE" \
|| fail "Init dosyasi eksik: $MAIN_INIT_FILE\nUnseal Key'i manuel olarak su formatta dosyaya ekleyin:\n Unseal Key 1: <gercek-key>"
ok "Init dosyasi mevcut"
else
info "Vault initialize ediliyor..."
run_vault "vault operator init -key-shares=1 -key-threshold=1" | tee "$MAIN_INIT_FILE"
ok "Vault init tamamlandi: $MAIN_INIT_FILE"
fi
# ━━━ ADIM 6 — vault_unseal_key Docker secret guncelle ━━━━━━━━━━━━━━
# Two-step update (delete + recreate with the same name) keeps the secret name
# consistent with the stack file so future 'docker stack deploy' runs do not
# trigger a service restart or revert to the placeholder.
step "ADIM 6 — vault_unseal_key Docker secret guncelleniyor"
UNSEAL_KEY=$(awk '/Unseal Key 1:/{print $NF}' "$MAIN_INIT_FILE")
[ -n "$UNSEAL_KEY" ] || fail "Unseal key '$MAIN_INIT_FILE' dosyasinda bulunamadi"
info "Eski secret servis uzerinden kaldiriliyor (rolling restart 1/2)..."
docker service update --secret-rm vault_unseal_key "${STACK_NAME}_vault" >/dev/null
sleep 5
docker secret rm vault_unseal_key || true
info "Gercek unseal key ile secret yeniden olusturuluyor (rolling restart 2/2)..."
echo "$UNSEAL_KEY" | docker secret create vault_unseal_key - >/dev/null
docker service update --secret-add vault_unseal_key "${STACK_NAME}_vault" >/dev/null
ok "vault_unseal_key gercek degerle guncellendi"
# ━━━ ADIM 7 — Unseal dogrula ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
step "ADIM 7 — Vault unseal dogrulaniyor"
info "Rolling restart tamamlanmasi ve unseal bekleniyor (30s)..."
sleep 30
UNSEALED=0
for i in $(seq 1 12); do
STATUS=$(run_vault "vault status 2>/dev/null" | awk '/^Sealed/{print $2}' || echo "true")
if [ "$STATUS" = "false" ]; then
ok "Vault cluster unsealed"
UNSEALED=1
break
fi
[ "$i" -eq 12 ] && break
echo " ${i}/12 — Sealed: $STATUS, retrying in 5s..."
sleep 5
done
[ "$UNSEALED" -eq 1 ] || fail "Vault cluster unseal olmadi — 'docker service logs ${STACK_NAME}_vault' ile loglari kontrol edin"
echo
echo "════════════════════════════════════════════════"
echo " BOOTSTRAP TAMAMLANDI"
echo " Init cikti: $MAIN_INIT_FILE"
echo " ONEMLI: Bu dosyayi guvenli yere yedekle ve"
echo " produksiyon ortamindan sil!"
echo "════════════════════════════════════════════════"