- Added comprehensive step-by-step guide in README.md for Vault Transit auto-unseal setup. - Included Docker Swarm stack definition (docker-stack-vault.yml). - Added Vault configuration templates and bootstrap scripts. - Configured Gitea workflows for the VaultTest environment.
223 lines
11 KiB
Bash
Executable File
223 lines
11 KiB
Bash
Executable File
#!/bin/bash
|
|
# vault-bootstrap.sh — Full Vault HA cluster bootstrap
|
|
# iklim-app-03 (transit node + manager) uzerinde calistirilir. SSH gerektirmez.
|
|
set -euo pipefail
|
|
|
|
# ─── Configuration ───────────────────────────────────────────────────
|
|
STACK_NAME="iklimco"
|
|
STACK_FILE="$(cd "$(dirname "$0")" && pwd)/docker-stack-vault.yml"
|
|
TRANSIT_NODE="iklim-app-03"
|
|
OUT_DIR="/tmp/vault-bootstrap"
|
|
# ─────────────────────────────────────────────────────────────────────
|
|
|
|
mkdir -p "$OUT_DIR"
|
|
TRANSIT_INIT_FILE="$OUT_DIR/transit-init.txt"
|
|
MAIN_INIT_FILE="$OUT_DIR/main-vault-init.txt"
|
|
AUTOUNSEAL_TOKEN_FILE="$OUT_DIR/autounseal-token.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 ─────────────────────────────────────────────────────────
|
|
transit_id() {
|
|
docker ps -q -f "name=${STACK_NAME}_vault-transit" | head -1
|
|
}
|
|
|
|
transit_vault() {
|
|
local cid
|
|
cid=$(transit_id) || fail "Transit container bulunamadi"
|
|
[ -n "$cid" ] || fail "Transit container bulunamadi"
|
|
docker exec "$cid" sh -c "VAULT_ADDR=http://127.0.0.1:8200 $*"
|
|
}
|
|
|
|
secret_exists() {
|
|
docker secret ls --format '{{.Name}}' | grep -q "^$1$"
|
|
}
|
|
|
|
transit_is_sealed() {
|
|
! transit_vault "vault status 2>/dev/null" | grep -q "Sealed.*false"
|
|
}
|
|
|
|
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"
|
|
}
|
|
# ─────────────────────────────────────────────────────────────────────
|
|
|
|
# ━━━ 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 secrets ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
step "ADIM 1 — Placeholder secrets olusturuluyor"
|
|
if secret_exists vault_transit_unseal_key; then
|
|
info "vault_transit_unseal_key zaten var, atlaniyor"
|
|
else
|
|
echo "bootstrap" | docker secret create vault_transit_unseal_key - >/dev/null
|
|
ok "vault_transit_unseal_key olusturuldu"
|
|
fi
|
|
if secret_exists transit_master_token; then
|
|
info "transit_master_token zaten var, atlaniyor"
|
|
else
|
|
echo "bootstrap" | docker secret create transit_master_token - >/dev/null
|
|
ok "transit_master_token olusturuldu"
|
|
fi
|
|
|
|
# ━━━ ADIM 2 — Node label ve stack deploy ━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
step "ADIM 2 — Transit node etiketleniyor ve stack deploy ediliyor"
|
|
docker node update --label-add vault_transit=true "$TRANSIT_NODE" >/dev/null
|
|
ok "Node etiketi eklendi: $TRANSIT_NODE"
|
|
|
|
docker stack deploy --with-registry-auth -c "$STACK_FILE" "$STACK_NAME"
|
|
ok "Stack deploy edildi (ana vault node'lari transit hazir olana kadar crash loop'ta — beklenen)"
|
|
|
|
# ━━━ ADIM 3 — Transit vault initialize ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
step "ADIM 3 — Transit vault bekleniyor ve initialize ediliyor"
|
|
wait_service_running "${STACK_NAME}_vault-transit" 1 120
|
|
sleep 10
|
|
|
|
if transit_vault "vault status 2>/dev/null" | grep -q "Initialized.*true"; then
|
|
info "Transit zaten initialize edilmis"
|
|
[ -f "$TRANSIT_INIT_FILE" ] && grep -q "Initial Root Token" "$TRANSIT_INIT_FILE" \
|
|
|| fail "Transit initialize edilmis ama init dosyasi eksik — Unseal Key ve Root Token'i manuel olarak $TRANSIT_INIT_FILE dosyasina kaydedin"
|
|
else
|
|
info "Transit vault initialize ediliyor..."
|
|
rm -f "$TRANSIT_INIT_FILE"
|
|
transit_vault "vault operator init -key-shares=1 -key-threshold=1" | tee "$TRANSIT_INIT_FILE"
|
|
ok "Transit init tamamlandi — cikti: $TRANSIT_INIT_FILE"
|
|
fi
|
|
|
|
# ━━━ ADIM 4 — Transit unseal ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
step "ADIM 4 — Transit vault unseal ediliyor"
|
|
if transit_is_sealed; then
|
|
UNSEAL_KEY=$(awk '/Unseal Key 1:/{print $NF}' "$TRANSIT_INIT_FILE")
|
|
[ -n "$UNSEAL_KEY" ] || fail "Unseal key '$TRANSIT_INIT_FILE' dosyasinda bulunamadi"
|
|
transit_vault "vault operator unseal $UNSEAL_KEY" >/dev/null
|
|
ok "Transit unsealed"
|
|
else
|
|
ok "Transit zaten unsealed"
|
|
fi
|
|
|
|
# ━━━ ADIM 5 — Transit engine kurulumu ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
step "ADIM 5 — Transit secrets engine kuruluyor"
|
|
ROOT_TOKEN=$(awk '/Initial Root Token:/{print $NF}' "$TRANSIT_INIT_FILE")
|
|
[ -n "$ROOT_TOKEN" ] || fail "Root token '$TRANSIT_INIT_FILE' dosyasinda bulunamadi"
|
|
|
|
# Policy dosyasini direkt container icine yaz
|
|
docker exec "$(transit_id)" sh -c 'cat > /tmp/autounseal-policy.hcl << '"'"'EOF'"'"'
|
|
path "transit/encrypt/autounseal" { capabilities = ["update"] }
|
|
path "transit/decrypt/autounseal" { capabilities = ["update"] }
|
|
EOF'
|
|
ok "Policy dosyasi container icine yazildi"
|
|
|
|
transit_vault "vault login $ROOT_TOKEN" >/dev/null
|
|
ok "Transit root login OK"
|
|
|
|
if transit_vault "vault secrets list 2>/dev/null" | grep -q "^transit/"; then
|
|
info "Transit engine zaten etkin, atlaniyor"
|
|
else
|
|
transit_vault "vault secrets enable transit" >/dev/null
|
|
ok "Transit engine etkinlestirildi"
|
|
fi
|
|
|
|
if transit_vault "vault read transit/keys/autounseal 2>/dev/null" | grep -q "autounseal"; then
|
|
info "Autounseal key zaten var, atlaniyor"
|
|
else
|
|
transit_vault "vault write -f transit/keys/autounseal" >/dev/null
|
|
ok "Autounseal key olusturuldu"
|
|
fi
|
|
|
|
transit_vault "vault policy write autounseal /tmp/autounseal-policy.hcl" >/dev/null
|
|
ok "Autounseal policy yazildi"
|
|
|
|
info "Autounseal policy token olusturuluyor..."
|
|
AUTOUNSEAL_TOKEN=$(transit_vault "vault token create -policy=autounseal -period=768h -orphan -format=json" \
|
|
| grep '"client_token"' | awk -F'"' '{print $4}')
|
|
[ -n "$AUTOUNSEAL_TOKEN" ] || fail "Autounseal token alinamadi"
|
|
echo "$AUTOUNSEAL_TOKEN" > "$AUTOUNSEAL_TOKEN_FILE"
|
|
ok "Autounseal token olusturuldu: ${AUTOUNSEAL_TOKEN:0:20}..."
|
|
|
|
# ━━━ ADIM 6 — Secrets guncelle ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
step "ADIM 6 — Docker secrets gercek degerlerle guncelleniyor"
|
|
UNSEAL_KEY=$(awk '/Unseal Key 1:/{print $NF}' "$TRANSIT_INIT_FILE")
|
|
|
|
# 6a — vault_transit_unseal_key
|
|
info "vault_transit_unseal_key guncelleniyor..."
|
|
docker service update --secret-rm vault_transit_unseal_key "${STACK_NAME}_vault-transit" >/dev/null
|
|
docker secret rm vault_transit_unseal_key
|
|
echo "$UNSEAL_KEY" | docker secret create vault_transit_unseal_key - >/dev/null
|
|
docker service update --secret-add vault_transit_unseal_key "${STACK_NAME}_vault-transit" >/dev/null
|
|
ok "vault_transit_unseal_key guncellendi"
|
|
|
|
# 6b — transit auto-unseal dogrula
|
|
info "Transit restart sonrasi unseal durumu bekleniyor (15s)..."
|
|
sleep 15
|
|
if transit_is_sealed; then
|
|
info "Transit hala sealed, manuel unseal yapiliyor..."
|
|
transit_vault "vault operator unseal $UNSEAL_KEY" >/dev/null
|
|
ok "Transit manuel unsealed"
|
|
else
|
|
ok "Transit auto-unseal basarili"
|
|
fi
|
|
|
|
# 6c — transit_master_token atomic swap (her calistirmada fresh token)
|
|
info "transit_master_token atomic swap yapiliyor..."
|
|
|
|
# Vault servisinde simdi hangi secret transit_master_token olarak mount edilmis?
|
|
CURRENT_TOKEN_SECRET=$(docker service inspect "${STACK_NAME}_vault" \
|
|
--format '{{range .Spec.TaskTemplate.ContainerSpec.Secrets}}{{if eq .File.Name "transit_master_token"}}{{.SecretName}}{{end}}{{end}}' \
|
|
2>/dev/null)
|
|
[ -n "$CURRENT_TOKEN_SECRET" ] || CURRENT_TOKEN_SECRET="transit_master_token"
|
|
info "Mevcut token secret: $CURRENT_TOKEN_SECRET"
|
|
|
|
# Her calistirmada benzersiz isimli fresh secret olustur
|
|
NEW_TOKEN_SECRET="transit_master_token_$(date +%s)"
|
|
echo "$AUTOUNSEAL_TOKEN" | docker secret create "$NEW_TOKEN_SECRET" - >/dev/null
|
|
ok "$NEW_TOKEN_SECRET olusturuldu"
|
|
|
|
# Atomic swap: eski cikart, yenicisi ekle — tek komutta (vault hic token'siz restart olmaz)
|
|
docker service update \
|
|
--secret-rm "$CURRENT_TOKEN_SECRET" \
|
|
--secret-add "source=${NEW_TOKEN_SECRET},target=transit_master_token" \
|
|
"${STACK_NAME}_vault" >/dev/null
|
|
ok "Atomic swap tamamlandi: $CURRENT_TOKEN_SECRET -> $NEW_TOKEN_SECRET"
|
|
|
|
# ━━━ ADIM 7 — Ana vault cluster initialize ━━━━━━━━━━━━━━━━━━━━━━━━━
|
|
step "ADIM 7 — Ana vault cluster bekleniyor ve initialize ediliyor"
|
|
info "Vault node'larinin stabil olmasi bekleniyor..."
|
|
wait_service_running "${STACK_NAME}_vault" 3 300
|
|
|
|
info "Ana vault cluster initialize ediliyor (lokal vault container)..."
|
|
LOCAL_VAULT_ID=$(docker ps -q -f "name=${STACK_NAME}_vault\." | head -1)
|
|
[ -n "$LOCAL_VAULT_ID" ] || fail "Lokal vault container bulunamadi — vault servisi bu node'da calismiyor olmali"
|
|
docker exec "$LOCAL_VAULT_ID" vault operator init | tee "$MAIN_INIT_FILE"
|
|
|
|
echo
|
|
echo "════════════════════════════════════════════════"
|
|
echo " BOOTSTRAP TAMAMLANDI"
|
|
echo " Transit init : $TRANSIT_INIT_FILE"
|
|
echo " Ana vault init: $MAIN_INIT_FILE"
|
|
echo " ONEMLI: Bu dosyalari guvenli yere yedekle!"
|
|
echo "════════════════════════════════════════════════"
|