fix(backup): 备份链路纳入 master_key.escrow(覆盖式单份+S3)

escrow 是 MASTER_KEY 遗失恢复链的一环:整机丢失时仅有库备份+离线 RESTORE_KEY 仍无法恢复,缺的正是托管档。随 assets 周期备份,固定文件名覆盖(多版本无意义),未建立托管时 skip。
This commit is contained in:
gouki
2026-09-05 23:10:46 +00:00
parent d711ad5827
commit 6865bb7914
+32 -2
View File
@@ -1,9 +1,13 @@
#!/usr/bin/env bash
# 数据库分库备份脚本(本地 + S3 兼容对象存储)
# 用法:backup.sh [assets|metrics|all]
# assets -> 备份 assets.db,保留 14 份(建议每日)
# assets -> 备份 assets.db + master_key.escrow,保留 14 份(建议每日)
# metrics -> 备份 metrics.db,保留 4 份(建议每周)
# all -> 两者都备份
# all -> 上述都备份
#
# master_key.escrow 随 assets 周期备份(覆盖式单份):它是被离线 RESTORE_KEY
# 加密的 MASTER_KEY 托管档,进备份/S3 安全;缺了它,整机丢失时即使有库备份
# 和离线钥匙也无法恢复。
#
# S3 上传(可选):配置 .env 中 S3_* 项后,备份文件自动上传到
# 任意 S3 兼容对象存储(Cloudflare R2 / MinIO / 阿里云 OSS / 腾讯云 COS 等),
@@ -188,8 +192,34 @@ def run(db_name: str, keep: int) -> None:
sys.exit(1)
def backup_escrow() -> None:
"""备份 MASTER_KEY 托管档(覆盖式单份 + S3)
escrow 多版本无意义(解出的都是当前 MASTER_KEY),故固定文件名覆盖;
重建托管(--force)后下次备份自动带上新档。
"""
import shutil
src = data_dir / "master_key.escrow"
if not src.exists():
print("[skip] master_key.escrow 不存在(尚未建立托管)")
return
out_dir = backup_root / "escrow"
out_dir.mkdir(parents=True, exist_ok=True)
dst = out_dir / "master_key.escrow"
shutil.copy2(src, dst)
dst.chmod(0o600)
print(f"[ok] master_key.escrow -> {dst}(覆盖式单份)")
try:
env = load_env(Path(".env"))
s3_upload(env, dst, "escrow", 14)
except Exception as e:
print(f"[s3][error] escrow 上传失败: {e}", file=sys.stderr)
sys.exit(1)
if target in ("assets", "all"):
run("assets.db", 14)
backup_escrow()
if target in ("metrics", "all"):
run("metrics.db", 4)
PYEOF