mirror of
https://github.com/Awuqing/BackupX.git
synced 2026-05-28 07:49:35 +08:00
feat(agent): Agent 远程恢复同样校验备份 SHA-256(补全 #75) (#76)
AgentRestoreSpec/RestoreSpec 新增 Checksum 并由 GetAgentRestoreSpec 透传;Agent ExecuteRestore 在解压前比对 SHA-256,不匹配即中止还原。两条恢复路径完整性保证一致。
This commit is contained in:
@@ -190,6 +190,7 @@ type RestoreSpec struct {
|
||||
Storage StorageTargetConfig `json:"storage"`
|
||||
StoragePath string `json:"storagePath"`
|
||||
FileName string `json:"fileName"`
|
||||
Checksum string `json:"checksum,omitempty"`
|
||||
}
|
||||
|
||||
// RestoreUpdate 与 service.AgentRestoreUpdate 对齐
|
||||
|
||||
@@ -379,6 +379,24 @@ func (e *Executor) ExecuteRestore(ctx context.Context, restoreRecordID uint) err
|
||||
return err
|
||||
}
|
||||
|
||||
// 2.5) 完整性校验:还原前比对下载对象的 SHA-256(与 Master 本地恢复路径一致)。
|
||||
// 拒绝还原损坏或被篡改的备份;早期无 checksum 的备份跳过(向后兼容)。
|
||||
if strings.TrimSpace(spec.Checksum) != "" {
|
||||
e.appendRestoreLog(ctx, restoreRecordID, "[agent] 校验备份完整性(SHA-256)\n")
|
||||
actual, sumErr := computeFileSHA256(artifactPath)
|
||||
if sumErr != nil {
|
||||
msg := fmt.Sprintf("计算校验和失败: %v", sumErr)
|
||||
e.reportRestoreFailure(ctx, restoreRecordID, msg)
|
||||
return fmt.Errorf("%s", msg)
|
||||
}
|
||||
if !strings.EqualFold(actual, spec.Checksum) {
|
||||
msg := "备份文件完整性校验失败:SHA-256 不匹配,文件可能已损坏或被篡改"
|
||||
e.reportRestoreFailure(ctx, restoreRecordID, msg)
|
||||
return fmt.Errorf("%s(期望 %s,实际 %s)", msg, spec.Checksum, actual)
|
||||
}
|
||||
e.appendRestoreLog(ctx, restoreRecordID, "[agent] 完整性校验通过\n")
|
||||
}
|
||||
|
||||
// 3) 解压(Agent 不支持加密,遇到 .enc 会直接失败)
|
||||
preparedPath := artifactPath
|
||||
if strings.HasSuffix(strings.ToLower(preparedPath), ".enc") {
|
||||
|
||||
@@ -463,6 +463,8 @@ type AgentRestoreSpec struct {
|
||||
Storage AgentStorageTargetConfig `json:"storage"`
|
||||
StoragePath string `json:"storagePath"`
|
||||
FileName string `json:"fileName"`
|
||||
// Checksum 源备份对象的 SHA-256(小写 hex);Agent 在还原前据此校验完整性。
|
||||
Checksum string `json:"checksum,omitempty"`
|
||||
}
|
||||
|
||||
// AgentRestoreUpdate Agent 回传的增量更新。
|
||||
@@ -549,6 +551,7 @@ func (s *RestoreService) GetAgentRestoreSpec(ctx context.Context, node *model.No
|
||||
},
|
||||
StoragePath: backupRecord.StoragePath,
|
||||
FileName: backupRecord.FileName,
|
||||
Checksum: backupRecord.Checksum,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -379,6 +379,7 @@ func TestRestoreServiceAgentRestoreAccessUsesRestoreRecordNode(t *testing.T) {
|
||||
Status: model.BackupRecordStatusSuccess,
|
||||
FileName: "remote.tar.gz",
|
||||
StoragePath: "file/2026/05/09/remote.tar.gz",
|
||||
Checksum: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef",
|
||||
StartedAt: startedAt,
|
||||
CompletedAt: &completedAt,
|
||||
}
|
||||
@@ -404,6 +405,10 @@ func TestRestoreServiceAgentRestoreAccessUsesRestoreRecordNode(t *testing.T) {
|
||||
if spec.RestoreRecordID != restore.ID || spec.StoragePath != backupRecord.StoragePath {
|
||||
t.Fatalf("unexpected restore spec: %#v", spec)
|
||||
}
|
||||
// Agent 端完整性校验依赖 spec 透传源备份 checksum。
|
||||
if spec.Checksum != backupRecord.Checksum {
|
||||
t.Fatalf("expected spec.Checksum=%q, got %q", backupRecord.Checksum, spec.Checksum)
|
||||
}
|
||||
if _, err := h.service.GetAgentRestoreSpec(ctx, other, restore.ID); err == nil {
|
||||
t.Fatal("expected non-owner node to be forbidden from restore spec")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user