fix(backup): 备份链路纳入 master_key.escrow(覆盖式单份+S3)
escrow 是 MASTER_KEY 遗失恢复链的一环:整机丢失时仅有库备份+离线 RESTORE_KEY 仍无法恢复,缺的正是托管档。随 assets 周期备份,固定文件名覆盖(多版本无意义),未建立托管时 skip。
This commit is contained in:
+32
-2
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user