不可变备份实战:Object Lock 防勒索篡改

不可变备份是抵御勒索软件的最后一道防线。多数勒索事件中,攻击者会先枚举并删除备份,再加密生产数据;只要备份可以被删改,恢复能力就形同虚设。不可变备份通过 WORM(Write Once Read Many)机制,在保留期内禁止任何账号删除或覆盖备份对象,即便管理员凭据泄露也无法绕过。本文给出基于 S3 Object Lock 与本地只追加保护的完整配置方案与验证命令。

适用场景

  • 勒索软件风险较高,需要确保备份在保留期内不可删除、不可篡改
  • 数据库、代码仓库、文件服务器等关键数据的离线保护
  • 需要满足等保或行业审计对备份不可否认性的要求
  • 运维账号权限较大,需要技术手段约束人为误删
  • 已有常规备份,但担心备份服务器与生产环境同域沦陷

前置条件

  • 支持 Object Lock 的对象存储(MinIO、AWS S3、阿里云 OSS、腾讯云 COS 等)
  • Object Lock 只能在创建桶时开启,已存在的桶无法事后启用
  • 独立的备份专用账号与访问密钥,权限与生产账号分离
  • 对象存储客户端:mc(MinIO Client)、awscli 或 rclone
  • 备份主机与生产主机网络隔离,备份数据流向为单向
  • 已明确保留期要求(如 30 天、90 天、1 年)与合规模式选择
# 安装客户端
curl -O https://dl.min.io/client/mc/release/linux-amd64/mc
chmod +x mc && mv mc /usr/local/bin/
apt-get install -y awscli rclone    # Debian/Ubuntu
# 配置别名
mc alias set backup https://oss.example.com BACKUP_AK BACKUP_SK

原理说明

不可变备份依赖三个机制叠加,缺一不可:

  • 版本控制(Versioning):对象的每次写入生成新版本,删除操作只是新增一个删除标记,历史版本仍然可读。没有版本控制,Object Lock 无法生效。
  • 对象锁(Object Lock):为对象或桶设置保留期,在 retain-until-date 之前,该对象版本无法被覆盖或删除。
  • 权限收敛:备份写入账号被策略显式拒绝 Delete 类操作,避免保留期设置被绕过。

对象锁提供两种模式,区别在能否提前解除:

  • GOVERNANCE 模式:具有 s3:BypassGovernanceRetention 权限的账号可提前解除保留,适合内部治理,灵活性高但存在被高权限账号绕过的可能。
  • COMPLIANCE 模式:任何账号(含 root)在保留期内都无法删除或缩短保留期,合规要求场景必须使用此模式。选择前务必确认保留期,设错无法回退。

配合 3-2-1-1-0 原则使用效果最佳:3 份副本、2 种介质、1 份异地、1 份不可变或离线、0 次验证失败。

操作步骤

步骤 1:创建启用对象锁的桶

# 使用 mc 创建桶并启用版本控制与对象锁
mc mb --with-lock backup/backup-immutable
mc version enable backup/backup-immutable

# 或使用 awscli(PutBucketObjectLockConfiguration 需在桶创建时生效)
aws --endpoint-url https://oss.example.com s3api create-bucket \
  --bucket backup-immutable --object-lock-enabled-for-bucket
aws --endpoint-url https://oss.example.com s3api put-bucket-versioning \
  --bucket backup-immutable --versioning-configuration Status=Enabled

创建后确认输出包含 ObjectLockEnabled: Enabled,若缺失说明桶未正确启用对象锁,需删除重建。

步骤 2:设置桶级默认保留策略

# mc 方式:设置桶默认保留 90 天,COMPLIANCE 模式
mc retention set --default COMPLIANCE "90d" backup/backup-immutable

# awscli 方式:等价配置
cat > /tmp/lock.json <<'EOF'
{
  "ObjectLockEnabled": "Enabled",
  "Rule": {
    "DefaultRetention": { "Mode": "COMPLIANCE", "Days": 90 }
  }
}
EOF
aws --endpoint-url https://oss.example.com s3api put-object-lock-configuration \
  --bucket backup-immutable --object-lock-configuration file:///tmp/lock.json

桶级默认策略对之后写入的新对象自动生效,无需在每次上传时单独指定,是降低运维遗漏的关键配置。存量对象需单独补设。

步骤 3:写入备份并核对保留状态

# 生成数据库备份
mysqldump --single-transaction --routines --triggers app \
  | gzip > /backup/app_$(date +%F).sql.gz

# 上传(桶默认保留策略自动生效)
mc cp /backup/app_$(date +%F).sql.gz backup/backup-immutable/db/

# 显式指定保留期(覆盖默认值,示例保留至 2027-01-02)
aws --endpoint-url https://oss.example.com s3api put-object \
  --bucket backup-immutable --key db/app_20261004.sql.gz \
  --body /backup/app_20261004.sql.gz \
  --object-lock-mode COMPLIANCE \
  --object-lock-retain-until-date 2027-01-02T00:00:00Z

# 核对对象保留状态
aws --endpoint-url https://oss.example.com s3api get-object-retention \
  --bucket backup-immutable --key db/app_20261004.sql.gz
mc retention info backup/backup-immutable/db/app_20261004.sql.gz

返回结果应包含 Mode: COMPLIANCE 与正确的 RetainUntilDate。若返回 NoSuchObjectLockConfiguration,说明对象是在桶默认策略生效前写入的。

步骤 4:收紧备份账号权限

仅依赖对象锁不够,还需防止权限被滥用去修改桶策略。为备份写入账号附加显式拒绝删除的策略:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DenyDeleteAndLockRemoval",
      "Effect": "Deny",
      "Action": [
        "s3:DeleteObject",
        "s3:DeleteObjectVersion",
        "s3:PutBucketObjectLockConfiguration",
        "s3:PutLifecycleConfiguration"
      ],
      "Resource": [
        "arn:aws:s3:::backup-immutable",
        "arn:aws:s3:::backup-immutable/*"
      ]
    }
  ]
}

写入账号与读取/恢复账号必须分离:写入账号只有 PutObject 权限,恢复账号只有 GetObject 权限,两侧都不具备删除能力。

步骤 5:本地备份目录的只追加保护

对象存储之外的本地副本可用文件属性锁定,注意 +i 属性需 root 才能设置,且在保留期内必须由流程约束不得解除:

# 锁定归档文件,禁止删除、重命名与写入
chattr +i /backup/archive/app_20261004.sql.gz
lsattr /backup/archive/app_20261004.sql.gz    # 期望输出含 ----i---------

# 目录级锁定(新文件需先解锁目录再写入)
chattr +i /backup/archive/

# ZFS 快照加 hold,防止被自动清理策略删除
zfs snapshot tank/backup@20261004
zfs hold keep tank/backup@20261004
zfs holds tank/backup

使用 rclone 同步到远端不可变桶时,加 --immutable 参数可拒绝任何覆盖已有对象的操作:

rclone sync /backup remote:backup-immutable \
  --immutable --transfers 4 --checkers 8 --log-file /var/log/rclone-backup.log

步骤 6:恢复演练与完整性校验

# 每月至少一次恢复演练:下载并校验
mc cp backup/backup-immutable/db/app_20261004.sql.gz /restore/
gunzip -c /restore/app_20261004.sql.gz | head -20

# 校验备份与源库一致性:对比表数量与行数
mysql -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='app';"
gunzip -c /restore/app_20261004.sql.gz | grep -c "CREATE TABLE"

# 生成恢复演练记录,保留时间、耗时、结果
echo "$(date +%F' '%T) restore-ok app_20261004.sql.gz 4m12s" >> /var/log/restore-drill.log

配置验证

# 1. 桶对象锁状态
aws --endpoint-url https://oss.example.com s3api get-object-lock-configuration \
  --bucket backup-immutable

# 2. 版本控制状态
mc version info backup/backup-immutable

# 3. 删除测试(预期被拒绝,返回 AccessDenied)
aws --endpoint-url https://oss.example.com s3api delete-object \
  --bucket backup-immutable --key db/app_20261004.sql.gz --version-id <VID>

# 4. 本地不可变属性
lsattr /backup/archive/app_20261004.sql.gz

# 5. 备份任务与恢复演练记录是否连续
tail -5 /var/log/rclone-backup.log
tail -5 /var/log/restore-drill.log

第 3 项验证是核心:删除请求必须失败。若返回 204 说明保留期未生效或使用了 GOVERNANCE 模式且有绕过权限,需立即排查。建议将此验证脚本纳入定期巡检。

常见问题

FAQ 1:桶已存在,能否事后开启 Object Lock?

不能。Object Lock 必须在创建桶时通过 --object-lock-enabled-for-bucket 或 --with-lock 开启,已存在的桶无法追加该配置。正确做法是新建一个启用对象锁的桶,用 rclone 或 mc 将历史备份迁移过去,并对迁移后的对象补设保留策略。迁移期间保留原桶,确认新桶验证通过后再下线旧桶。

FAQ 2:COMPLIANCE 模式选错保留期,能提前释放吗?

不能。COMPLIANCE 模式下,保留期内任何账号(包括超级管理员)都无法删除对象或缩短保留期,这是该模式的设计目的,也是它适合合规场景的原因。选错保留期只能等待自然到期。因此建议:日常备份先用 GOVERNANCE 模式验证流程,确认无误后再对关键数据使用 COMPLIANCE,并按最小必要原则设置保留天数。

FAQ 3:不可变备份能否替代离线(air gap)备份?

两者互补,不能完全互相替代。不可变备份的存储系统仍需在线、需凭据访问,若存储平台自身被攻破或凭据与保留期配置同时被篡改,仍存在风险;离线备份(磁带、断电异机、定期拔盘)在网络层面彻底不可达。推荐组合为:本地快照 + 不可变对象存储 + 周期性离线副本,三者保留期错开,形成纵深。

FAQ 4:备份文件上传后体积异常或上传中断怎么办?

上传中断会在桶中留下不完整对象,配合对象锁后同样无法删除。建议上传前启用分片上传并在客户端做完整性校验,或在对象元数据中记录源文件 SHA256,恢复演练时比对。若已产生垃圾对象,只能等待保留期结束后清理,这也是需要提前设定合理默认保留天数的原因之一。

总结

不可变备份的落地要点是三个机制叠加:版本控制 + 对象锁 + 权限显式拒绝,任一缺失都会留下删除路径。实施顺序建议为:确认保留期 → 新建启用锁的桶 → 设置桶级默认保留 → 分离读写账号 → 配置本地只追加保护 → 定期演练与删除验证。合规场景使用 COMPLIANCE 模式并做好保留期评估,内部治理场景可用 GOVERNANCE 模式换取灵活性。备份体系的价值不在于备份动作本身,而在于勒索发生后能否在规定时间内完成一次可验证的恢复,因此删除验证与恢复演练必须纳入常态化巡检。