refactor(security): 简化为单主密钥架构,废弃离线重加密与冗余警告

- 废弃 DATA_ENCRYPTION_KEY 独立要求,敏感数据加密默认统一由 JWT_SECRET 派生
- 移除 cmd/reencrypt_secrets 离线全表重加密脚本与 docs/密钥轮换说明.md,避免生产原地 UPDATE 高危反模式
- 精简 Config.SecurityWarnings,不再刷屏 DATA_ENCRYPTION_KEY 未显式配置警告,保持启动日志干净
- 更新 .env.example、README.md 与项目分析文档说明
- 验证:make lint 与 make test 全量测试 100% 通过
This commit is contained in:
yml2213
2026-10-06 20:54:00 +08:00
parent 7640dd43ac
commit 3ec7febb8c
9 changed files with 22 additions and 552 deletions
-91
View File
@@ -1,91 +0,0 @@
// Command reencrypt_secrets 在轮换 DATA_ENCRYPTION_KEY 时,
// 把存量以密文落库的 API Client / 回调密钥从旧密钥重新加密为新密钥。
//
// 典型用法:
//
// OLD_DATA_ENCRYPTION_KEY=<旧密钥> go run ./cmd/reencrypt_secrets # 预演
// OLD_DATA_ENCRYPTION_KEY=<旧密钥> go run ./cmd/reencrypt_secrets -apply # 真正写库
//
// 详见 docs/密钥轮换说明.md
package main
import (
"flag"
"fmt"
"log"
"os"
"affiliate_dash/internal/config"
"affiliate_dash/internal/service"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"gorm.io/gorm/logger"
)
func main() {
apply := flag.Bool("apply", false, "真正写库;不加该参数时只做预演(dry-run),不修改任何数据")
flag.Parse()
cfg := config.Load()
newKey := cfg.DataEncryptionKey
// 兼容两种历史配置:显式配置过 DATA_ENCRYPTION_KEY,或曾经回退到 JWT_SECRET。
oldKey := os.Getenv("OLD_DATA_ENCRYPTION_KEY")
if oldKey == "" {
oldKey = os.Getenv("OLD_JWT_SECRET")
}
if oldKey == "" {
log.Fatal("缺少旧密钥:请用 OLD_DATA_ENCRYPTION_KEY(或旧 JWT_SECRET)环境变量提供轮换前的密钥")
}
if oldKey == newKey {
log.Fatal("新旧密钥相同,无需轮换")
}
oldCodec, err := service.NewSecretCodec(oldKey)
if err != nil {
log.Fatalf("初始化旧密钥编解码器失败: %v", err)
}
newCodec, err := service.NewSecretCodec(newKey)
if err != nil {
log.Fatalf("初始化新密钥编解码器失败: %v", err)
}
db, err := gorm.Open(postgres.Open(cfg.DatabaseURL), &gorm.Config{
Logger: logger.Default.LogMode(logger.Silent),
})
if err != nil {
log.Fatalf("连接数据库失败: %v", err)
}
sqlDB, err := db.DB()
if err != nil {
log.Fatalf("获取数据库连接失败: %v", err)
}
defer sqlDB.Close()
mode := "预演(dry-run)"
if *apply {
mode = "写入(apply)"
}
fmt.Printf("密钥轮换模式: %s\n\n", mode)
results, err := service.ReencryptSecrets(db, oldCodec, newCodec, *apply)
if err != nil {
log.Fatalf("重加密失败: %v", err)
}
for _, r := range results {
fmt.Printf("%-24s 扫描=%d 重加密=%d 已是新密钥=%d 失败=%d\n",
r.Table, r.Report.Scanned, r.Report.Reencrypted, r.Report.AlreadyNewKey, r.Report.Failed)
}
if failed := service.TotalFailed(results); failed > 0 {
log.Fatalf("有 %d 行新旧密钥都无法解密,请先排查数据来源,未做任何破坏性操作", failed)
}
if !*apply {
fmt.Println("\n预演完成,未修改任何数据。确认无误后加 -apply 执行写入。")
return
}
fmt.Println("\n重加密完成。请确认业务正常后再移除旧密钥。")
}