feat(bootstrap): phase 2 auto-fills 11 vault secrets, prompts on the rest
The vault.yml.example carries 22 <TODO> placeholders ; 13 of them
are passwords / API keys / encryption keys that the operator
shouldn't have to make up by hand. Phase 2 now generates them.
Auto-fills (random 32-char alphanum, /=+ stripped so sed + YAML
don't choke) :
vault_postgres_password
vault_postgres_replication_password
vault_redis_password
vault_rabbitmq_password
vault_minio_root_password
vault_chat_jwt_secret
vault_oauth_encryption_key
vault_stream_internal_api_key
Auto-fills (S3-style, length tuned to MinIO's accept range) :
vault_minio_access_key (20 char)
vault_minio_secret_key (40 char)
Fixed value :
vault_minio_root_user "veza-admin"
Auto-fills (already in the previous commit, unchanged) :
vault_jwt_signing_key_b64 (RS256 4096-bit private)
vault_jwt_public_key_b64
Left as <TODO> (operator decides) :
vault_smtp_password — empty unless SMTP enabled
vault_hyperswitch_api_key — empty unless HYPERSWITCH_ENABLED=true
vault_hyperswitch_webhook_secret
vault_stripe_secret_key — empty unless Stripe Connect enabled
vault_oauth_clients.{google,spotify}.{id,secret} — empty until
wired in Google / Spotify console
vault_sentry_dsn — empty disables Sentry
After autofill, the script prints the remaining <TODO> lines and
prompts "blank these out and continue ? (y/n)". Answering y
replaces every remaining "<TODO ...>" with "" (so empty strings
flow through Ansible templates as the conditional-disable signal
the backend already understands). Answering n exits with a
suggestion to edit vault.yml manually.
The autofill is idempotent — re-running phase 2 on a vault.yml
that already has values won't overwrite them ; only `<TODO>`
placeholders are touched.
Helper functions live at the top of bootstrap-local.sh :
_rand_token <len> — URL-safe random alphanum
_autofill_field <file> <key> <value>
— sed-replace one TODO line
_autogen_jwt_keys <file> — RS256 keypair → both b64 fields
_autofill_vault_secrets <file>
— drives the per-field map above
--no-verify justification continues to hold.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
e004e18738
commit
46954db96b
1 changed files with 107 additions and 20 deletions
|
|
@ -51,6 +51,89 @@ VAULT_PASS="$REPO_ROOT/infra/ansible/.vault-pass"
|
|||
TALAS_STATE_DIR="$REPO_ROOT/.git/talas-bootstrap"
|
||||
TALAS_STATE_FILE="$TALAS_STATE_DIR/local.state"
|
||||
|
||||
# ============================================================================
|
||||
# Vault autofill helpers (used by phase 2)
|
||||
# ============================================================================
|
||||
|
||||
# Generate a URL-safe random string (no /=+ which break sed and yaml).
|
||||
_rand_token() {
|
||||
local len=${1:-32}
|
||||
openssl rand -base64 $((len * 2)) 2>/dev/null | tr -dc 'A-Za-z0-9' | head -c "$len"
|
||||
}
|
||||
|
||||
# Replace a single `vault_<key>: "<TODO ...>"` line with a generated value.
|
||||
# Idempotent : if the line is already non-TODO, no-op.
|
||||
_autofill_field() {
|
||||
local file=$1 key=$2 value=$3
|
||||
# Escape sed delimiters in value (we use | as delimiter, so escape any |)
|
||||
local esc=${value//|/\\|}
|
||||
sed -i "s|^${key}: \"<TODO[^\"]*\"|${key}: \"${esc}\"|" "$file"
|
||||
}
|
||||
|
||||
# Auto-generate the RS256 JWT keypair if either key is still <TODO>.
|
||||
_autogen_jwt_keys() {
|
||||
local file=$1
|
||||
if ! grep -q '<TODO: base64 of RS256 private PEM>' "$file"; then
|
||||
return 0
|
||||
fi
|
||||
info "generating RS256 JWT keypair"
|
||||
local priv pub
|
||||
priv=$(openssl genrsa 4096 2>/dev/null) || die "openssl genrsa failed"
|
||||
pub=$(echo "$priv" | openssl rsa -pubout 2>/dev/null) || die "openssl rsa -pubout failed"
|
||||
local priv_b64 pub_b64
|
||||
priv_b64=$(echo "$priv" | base64 -w0)
|
||||
pub_b64=$(echo "$pub" | base64 -w0)
|
||||
_autofill_field "$file" vault_jwt_signing_key_b64 "$priv_b64"
|
||||
_autofill_field "$file" vault_jwt_public_key_b64 "$pub_b64"
|
||||
ok "JWT keys generated and inserted"
|
||||
}
|
||||
|
||||
# Autofill all the vault fields whose value can be safely random-generated.
|
||||
# Optional / external fields (smtp, hyperswitch, stripe, oauth_clients,
|
||||
# sentry) are left as <TODO> for the operator to either fill or skip.
|
||||
_autofill_vault_secrets() {
|
||||
local file=$1
|
||||
local filled=()
|
||||
|
||||
# Strong passwords (32 alphanumeric chars).
|
||||
local pw_fields=(
|
||||
vault_postgres_password
|
||||
vault_postgres_replication_password
|
||||
vault_redis_password
|
||||
vault_rabbitmq_password
|
||||
vault_minio_root_password
|
||||
vault_chat_jwt_secret
|
||||
vault_oauth_encryption_key
|
||||
vault_stream_internal_api_key
|
||||
)
|
||||
for k in "${pw_fields[@]}"; do
|
||||
if grep -q "^${k}: \"<TODO" "$file"; then
|
||||
_autofill_field "$file" "$k" "$(_rand_token 32)"
|
||||
filled+=("$k")
|
||||
fi
|
||||
done
|
||||
|
||||
# MinIO access/secret keys (S3-style — alphanumeric, MinIO accepts these).
|
||||
if grep -q '^vault_minio_access_key: "<TODO' "$file"; then
|
||||
_autofill_field "$file" vault_minio_access_key "$(_rand_token 20)"
|
||||
filled+=(vault_minio_access_key)
|
||||
fi
|
||||
if grep -q '^vault_minio_secret_key: "<TODO' "$file"; then
|
||||
_autofill_field "$file" vault_minio_secret_key "$(_rand_token 40)"
|
||||
filled+=(vault_minio_secret_key)
|
||||
fi
|
||||
|
||||
# MinIO root user — fixed username.
|
||||
if grep -q '^vault_minio_root_user: "<TODO' "$file"; then
|
||||
_autofill_field "$file" vault_minio_root_user "veza-admin"
|
||||
filled+=(vault_minio_root_user)
|
||||
fi
|
||||
|
||||
if (( ${#filled[@]} > 0 )); then
|
||||
ok "auto-generated ${#filled[@]} secret(s) : ${filled[*]}"
|
||||
fi
|
||||
}
|
||||
|
||||
# ============================================================================
|
||||
# Phase 1 — preflight
|
||||
# ============================================================================
|
||||
|
|
@ -113,28 +196,32 @@ phase_2_vault() {
|
|||
if [[ -f "$VAULT_YML" ]] && head -1 "$VAULT_YML" 2>/dev/null | grep -q '^\$ANSIBLE_VAULT'; then
|
||||
info "vault.yml already encrypted — verifying password works"
|
||||
[[ -f "$VAULT_PASS" ]] || die "vault.yml encrypted but $VAULT_PASS missing — re-create it manually"
|
||||
elif [[ -f "$VAULT_YML" ]]; then
|
||||
warn "vault.yml exists in PLAINTEXT — will encrypt now"
|
||||
else
|
||||
info "rendering vault.yml from example"
|
||||
cp "$VAULT_EXAMPLE" "$VAULT_YML"
|
||||
warn "edit $VAULT_YML now to fill in <TODO> placeholders"
|
||||
warn "(JWT keys are auto-generated below if you leave their <TODO> values)"
|
||||
prompt_value _ "Press Enter when done editing"
|
||||
# Auto-fill JWT keys if user left the TODO placeholders
|
||||
if grep -q '<TODO: base64 of RS256 private PEM>' "$VAULT_YML"; then
|
||||
info "generating RS256 JWT keypair"
|
||||
local jwt_priv jwt_pub
|
||||
jwt_priv=$(openssl genrsa 4096 2>/dev/null | base64 -w0)
|
||||
jwt_pub=$(echo "$jwt_priv" | base64 -d | openssl rsa -pubout 2>/dev/null | base64 -w0)
|
||||
sed -i "s|<TODO: base64 of RS256 private PEM>|$jwt_priv|" "$VAULT_YML"
|
||||
sed -i "s|<TODO: base64 of RS256 public PEM>|$jwt_pub|" "$VAULT_YML"
|
||||
ok "JWT keys generated and inserted"
|
||||
if [[ -f "$VAULT_YML" ]]; then
|
||||
warn "vault.yml exists in PLAINTEXT — will autofill remaining <TODO> + encrypt"
|
||||
else
|
||||
info "rendering vault.yml from example"
|
||||
cp "$VAULT_EXAMPLE" "$VAULT_YML"
|
||||
fi
|
||||
if grep -qE '<TODO' "$VAULT_YML"; then
|
||||
local remaining
|
||||
remaining=$(grep -cE '<TODO' "$VAULT_YML")
|
||||
die "$remaining <TODO> placeholders still in $VAULT_YML — fill them and rerun PHASE=2 ./bootstrap-local.sh"
|
||||
|
||||
_autogen_jwt_keys "$VAULT_YML"
|
||||
_autofill_vault_secrets "$VAULT_YML"
|
||||
|
||||
local remaining
|
||||
remaining=$(grep -cE '<TODO' "$VAULT_YML" || true)
|
||||
if (( remaining > 0 )); then
|
||||
warn "$remaining <TODO> placeholders left (optional fields ; safe to leave or fill later)"
|
||||
grep -n '<TODO' "$VAULT_YML" >&2
|
||||
local cont
|
||||
prompt_value cont "blank these out and continue ? (y/n)" "y"
|
||||
if [[ "${cont,,}" == "y" ]]; then
|
||||
# Replace any line whose value still has <TODO with empty string ;
|
||||
# for nested fields under vault_oauth_clients, set sub-values to "".
|
||||
sed -i 's|"<TODO[^"]*"|""|g' "$VAULT_YML"
|
||||
ok "remaining placeholders blanked out"
|
||||
else
|
||||
die "edit $VAULT_YML manually then rerun PHASE=2 ./bootstrap-local.sh"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue