71 lines
3.4 KiB
Bash
71 lines
3.4 KiB
Bash
# Canonical paths follow ownership. Shared callers live under the receiving service.
|
|
secret_moves() {
|
|
cat <<'PATHS'
|
|
bff services/bff/jwt
|
|
caller services/notifications/callers/bff
|
|
email services/notifications/email/resend
|
|
nats infrastructure/nats/auth
|
|
bootstrap/mongo infrastructure/mongo/bootstrap
|
|
PATHS
|
|
}
|
|
# Archive readable version history privately before removing an obsolete KV path.
|
|
archive_secret() (
|
|
old=$1
|
|
metadata=$(vault "kv/metadata/$old" GET yes)
|
|
[ "$metadata" != null ] || exit 0
|
|
directory="$STATE/migrations/organized-v1/$old"
|
|
mkdir -p "$directory"
|
|
printf '%s' "$metadata" | write "$directory/metadata.json"
|
|
printf '%s' "$metadata" | jq -r '.data.versions|to_entries[]|select(.value.destroyed==false and .value.deletion_time=="")|.key' > "$WORK/versions"
|
|
while IFS= read -r version; do
|
|
vault "kv/data/$old?version=$version" | write "$directory/version-$version.json"
|
|
done < "$WORK/versions"
|
|
)
|
|
migrate_secrets() (
|
|
secret_moves > "$WORK/moves"
|
|
while read -r old new; do
|
|
kv "$old" > "$WORK/old"
|
|
[ "$(cat "$WORK/old")" != null ] || continue
|
|
archive_secret "$old"
|
|
kv "$new" > "$WORK/new"
|
|
if [ "$(cat "$WORK/new")" = null ]; then
|
|
# CAS=0 prevents overwriting a destination created concurrently.
|
|
jq '{data:.,options:{cas:0}}' "$WORK/old" | vault "kv/data/$new" POST >/dev/null
|
|
fi
|
|
kv "$new" > "$WORK/new"
|
|
jq -e -s '.[0]==.[1]' "$WORK/old" "$WORK/new" >/dev/null || fail "Conflicting Vault values at $old and $new; no source removed"
|
|
done < "$WORK/moves"
|
|
)
|
|
retire_secrets() (
|
|
# Call only after templates, policies and deployed images use canonical paths.
|
|
secret_moves > "$WORK/moves"
|
|
while read -r old new; do
|
|
kv "$old" > "$WORK/old"
|
|
[ "$(cat "$WORK/old")" != null ] || continue
|
|
kv "$new" > "$WORK/new"
|
|
jq -e -s '.[0]==.[1]' "$WORK/old" "$WORK/new" >/dev/null || fail "Vault migration verification failed for $old"
|
|
archive_secret "$old"
|
|
printf '{}' | vault "kv/metadata/$old" DELETE >/dev/null
|
|
done < "$WORK/moves"
|
|
for old in tls frontend-tls notifications-tls; do
|
|
archive_secret "$old"
|
|
if [ "$(vault "kv/metadata/$old" GET yes)" != null ]; then printf '{}' | vault "kv/metadata/$old" DELETE >/dev/null; fi
|
|
done
|
|
# The obsolete PKI mount is deliberately not disabled: its private signing key
|
|
# and history are retained until the owner explicitly chooses to destroy them.
|
|
echo 'PASS: Vault secrets migrated and verified; obsolete KV entries retired'
|
|
)
|
|
|
|
# Only credentials belong in Vault; driver/sender settings are runtime YAML.
|
|
normalize_email_secret() (
|
|
path=services/notifications/email/resend
|
|
vault "kv/data/$path" GET yes > "$WORK/email-current"
|
|
[ "$(cat "$WORK/email-current")" != null ] || exit 0
|
|
jq -e '(.data.data|keys)-["api_key","driver","sender_address","sender_name"]|length==0' "$WORK/email-current" >/dev/null || fail 'Unexpected Resend secret fields; refusing to discard them'
|
|
if jq -e '.data.data|keys==["api_key"]' "$WORK/email-current" >/dev/null; then exit 0; fi
|
|
archive_secret "$path"
|
|
jq '{data:{api_key:(.data.data.api_key // "")},options:{cas:.data.metadata.version}}' "$WORK/email-current" | vault "kv/data/$path" POST >/dev/null
|
|
vault "kv/data/$path" | jq -e --slurpfile original "$WORK/email-current" '.data.data|keys==["api_key"] and .api_key==($original[0].data.data.api_key // "")' >/dev/null || fail 'Resend migration verification failed'
|
|
echo 'Resend secret now contains only api_key; configure driver/sender in notifications runtime.yml'
|
|
)
|