foundation-vault (hashicorp/vault:1.18, digest-pinned) with integrated raft storage in foundation-vault-data (-> /vault/file, which the entrypoint chowns to the vault user), IPC_LOCK for mlock, internal only (8200 unpublished). Init + unseal reuse the olsitec-core pattern but over docker-exec/SSH (ADR-007): the foundation-vault-init command inits 1-of-1 Shamir, unseals, and emits keys + root token on stdout — marked secret and NOT streamed (logging:Stderr) so they never reach the terminal/logs (D2). run.sh captures them into vaultCredentials:* (the one bootstrap secret that cannot live in Vault, CONTRACT_002 §2.4) with an idempotent guard that avoids churning the config. vault-unseal.sh is the passphrase-gated reboot helper (ADR-004): reads keys from config, unseals over an SSH stdin pipe. run.sh also now pins the Pulumi backend per-process (PULUMI_BACKEND_URL) instead of a global `pulumi login`. Live on cx33 Helsinki: initialized + unsealed (raft 1.18.5), keys captured to encrypted config, idempotent re-up reuses stored keys, container-restart reseal recovered by vault-unseal.sh. Acceptance T05 met. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
34 lines
1.8 KiB
Bash
Executable file
34 lines
1.8 KiB
Bash
Executable file
#!/usr/bin/env bash
|
|
# Passphrase-gated Vault unseal helper (ADR-004). After a VM reboot foundation-vault
|
|
# re-seals; this reads the unseal keys from the passphrase-encrypted Pulumi config
|
|
# and unseals over SSH via docker exec (ADR-007). No external KMS, no SaaS — reboots
|
|
# require the master passphrase to be available (the ratified Layer-0 trade-off).
|
|
#
|
|
# `pulumi up` unseals as part of a deploy (the foundation-vault-init command); this
|
|
# helper is the break-glass for a reboot WITHOUT a deploy.
|
|
set -euo pipefail
|
|
DIR="$(cd "$(dirname "$0")" && pwd)"
|
|
export PULUMI_BACKEND_URL="file://${DIR}/state"
|
|
export PULUMI_CONFIG_PASSPHRASE="$(pass olsitec-foundation/PULUMI_CONFIG_PASSPHRASE)"
|
|
cd "$DIR"
|
|
pulumi stack select foundation >/dev/null
|
|
|
|
KEYS_JSON=$(pulumi config get vaultCredentials:unsealKeys 2>/dev/null || true)
|
|
[ -n "$KEYS_JSON" ] || { echo "no vaultCredentials:unsealKeys in config — is Vault initialized?" >&2; exit 1; }
|
|
HOST=$(pulumi config get foundation:vm.host)
|
|
PORT=$(pulumi config get foundation:vm.sshPort 2>/dev/null || echo 22)
|
|
SSHUSER=$(pulumi config get foundation:vm.user)
|
|
KEY="${SSH_PRIVATE_KEY_PATH:-${HOME}/.ssh/foundation-test_ed25519}"
|
|
|
|
# Keys travel over the SSH stdin pipe, NEVER on the command line. The remote shell
|
|
# unseals foundation-vault with each one via docker exec.
|
|
printf '%s' "$KEYS_JSON" | jq -r '.[]' | \
|
|
ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -i "$KEY" -p "$PORT" "$SSHUSER@$HOST" '
|
|
n=0
|
|
while IFS= read -r k; do
|
|
docker exec -e VAULT_ADDR=http://127.0.0.1:8200 foundation-vault vault operator unseal "$k" >/dev/null && n=$((n+1))
|
|
done
|
|
echo "applied $n unseal key(s)"
|
|
docker exec -e VAULT_ADDR=http://127.0.0.1:8200 foundation-vault vault status -format=json | jq -r "\"sealed: \" + (.sealed|tostring)"
|
|
'
|
|
echo "vault-unseal.sh: done"
|