#!/bin/bash set -euo pipefail umask 077 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=scripts/init-security-env.sh . "$SCRIPT_DIR/init-security-env.sh" mode="${1:-}"; keyfile="${2:-}"; envfile="${3:-$SCRIPT_DIR/../.env}" case "$mode" in export|restore) ;; *) echo '用法: credential-keys.sh export|restore 私有密钥备份文件 [部署.env]' >&2; exit 1 ;; esac [ -n "$keyfile" ] && [ -f "$envfile" ] || { echo '请指定密钥备份文件,并先准备部署 .env' >&2; exit 1; } [ "$keyfile" != "$envfile" ] || { echo '密钥备份必须与部署配置分开' >&2; exit 1; } sourcefile="$envfile" if [ "$mode" = restore ]; then sourcefile="$keyfile"; fi [ -f "$sourcefile" ] || { echo '密钥备份不存在' >&2; exit 1; } active="$(read_env_value "$sourcefile" CONFIG_CREDENTIAL_ENCRYPTION_ACTIVE_KEY_ID)" ring="$(read_env_value "$sourcefile" CONFIG_CREDENTIAL_ENCRYPTION_KEYRING)" legacy="$(read_env_value "$sourcefile" CONFIG_CREDENTIAL_ALLOW_LEGACY_PLAINTEXT)" [ -n "$active" ] && [ -n "$ring" ] || { echo '没有完整加密密钥,不能导出或恢复;不会生成替代密钥' >&2; exit 1; } tmp="$(mktemp "${envfile}.credential-keys.XXXXXX")" trap 'rm -f "$tmp"' EXIT printf 'CONFIG_CREDENTIAL_ENCRYPTION_ACTIVE_KEY_ID=%s\nCONFIG_CREDENTIAL_ENCRYPTION_KEYRING=%s\nCONFIG_CREDENTIAL_ALLOW_LEGACY_PLAINTEXT=%s\n' "$active" "'$ring'" "${legacy:-false}" > "$tmp" init_credential_encryption_env "$tmp" upgrade >/dev/null # Preserve the independent master and JWT fallback used by earlier encrypted settings. for name in CONFIG_SECURITY_CONFIG_ENCRYPTION_KEY CONFIG_JWT_SECRET; do if [ "$mode" = export ]; then awk -v key="$name" 'index($0, key "=") == 1 { print; found=1; exit } END { if (!found) print key "=" }' "$sourcefile" >> "$tmp" else awk -v key="$name" 'index($0, key "=") == 1 { print; exit }' "$sourcefile" >> "$tmp" fi done if [ "$mode" = export ]; then # noclobber reserves an immutable backup instead of truncating an earlier keyring. ( set -o noclobber; cat "$tmp" > "$keyfile" ) || { echo '密钥备份已存在或路径不可写,请使用新文件' >&2; exit 1; } chmod 600 "$keyfile" echo '已导出完整 keyring(含旧密钥);请与数据库备份分开私有保管。' else previous="${envfile}.pre-credential-restore.$(date +%Y%m%dT%H%M%S).$$" cp "$envfile" "$previous"; chmod 600 "$previous" key_content="$(cat "$tmp")" awk 'index($0,"CONFIG_CREDENTIAL_ENCRYPTION_ACTIVE_KEY_ID=")==1 || index($0,"CONFIG_CREDENTIAL_ENCRYPTION_KEYRING=")==1 || index($0,"CONFIG_CREDENTIAL_ALLOW_LEGACY_PLAINTEXT=")==1 { next } { print }' "$envfile" > "$tmp" for name in CONFIG_SECURITY_CONFIG_ENCRYPTION_KEY CONFIG_JWT_SECRET; do if printf '%s\n' "$key_content" | awk -v key="$name" 'index($0,key "=")==1 { found=1 } END { exit !found }'; then filtered="$(mktemp "${envfile}.filtered.XXXXXX")" awk -v key="$name" 'index($0,key "=")!=1 { print }' "$tmp" > "$filtered" mv "$filtered" "$tmp" fi done printf '%s\n' "$key_content" >> "$tmp" chmod 600 "$tmp"; mv "$tmp" "$envfile" echo '已恢复原 keyring,其他配置保留;重建全部相关容器后再恢复数据库。' fi