feat(admin): expose KMS backup and restore behind explicit guards (#5579)

* feat(kms): add backup and restore admin API

Wires the merged KMS backup contract, Local export and Local restore into
the admin API: export a sealed bundle, run a zero-write restore preflight,
execute a confirmed restore, roll an interrupted restore back, and report
subsystem readiness.

- Dedicated kms:Backup / kms:Restore actions, recorded in the admin route
  matrix. Neither is reachable through any other KMS action.
- Restore requires two independent confirmations: an echo of the bundle
  manifest's backup id, and an explicitly named conflict policy (the
  default never writes).
- The backup KEK comes from the environment and is refused when it reuses
  a secret of the configured backend, compared both as the literal value
  and as raw key bytes.
- No endpoint accepts a path: bundles are addressed by a validated name
  under a configured root, and the restore target is always the server's
  own configured key directory.
- Bundles now carry a sanitized configuration artifact built as an
  allowlist projection, so a future backend credential field cannot leak
  into a bundle by default. Restore verifies it and never applies it.
- Audit entries go through the existing KMS admin wiring and carry
  identifiers only.

* test(kms): pin the backup admin API gates

Fixes the test KEK to a real 32-byte value and drives the export refusal
from the configured backend rather than from the handle that happens to
be available, so a Local handle cannot export on behalf of a backend
whose material RustFS does not own.
This commit is contained in:
Zhengchao An
2026-08-01 22:31:25 +08:00
committed by GitHub
parent 02aa383598
commit f2d09d1426
14 changed files with 1847 additions and 1 deletions
+9
View File
@@ -730,6 +730,13 @@ pub enum KmsAction {
DescribeKeyAction,
#[strum(serialize = "kms:Decrypt")]
DecryptAction,
/// Export a KMS backup bundle. Separate from every key action because a
/// bundle carries the material of every key at once.
#[strum(serialize = "kms:Backup")]
BackupAction,
/// Preflight or execute a KMS restore.
#[strum(serialize = "kms:Restore")]
RestoreAction,
}
#[cfg(test)]
@@ -767,6 +774,8 @@ mod tests {
("kms:ListKeys", KmsAction::ListKeysAction),
("kms:DescribeKey", KmsAction::DescribeKeyAction),
("kms:Decrypt", KmsAction::DecryptAction),
("kms:Backup", KmsAction::BackupAction),
("kms:Restore", KmsAction::RestoreAction),
] {
let action = Action::try_from(raw).expect("Should parse KMS action");
assert_eq!(action, Action::KmsAction(expected));