mirror of
https://github.com/rustfs/rustfs.git
synced 2026-08-09 14:49:25 +00:00
fix(kms): fail closed on Local key records that cannot be interpreted (#5606)
* fix(kms): fail closed on Local key records this build cannot interpret The Local backend's protection marker is the only version discriminator its key records have, and three readers walked past it. `ensure_missing_salt_can_be_generated` skipped every record it could not read or parse, so a directory whose protection state is unknown still got a fresh salt published before startup validation failed. That write is the irreversible step: the next startup finds a salt file, never re-enters the guard, and the evidence that the real salt was lost is gone. Every record the guard now rejects already failed startup key validation a few lines later, so no directory that initializes today stops initializing. Backup export and restore folded an unknown marker into "material corrupt" / "bundle corrupted". The record is intact and a newer build reads it fine, so the operator response is a version change, not a disaster recovery. Both now classify the marker before their schema parse, sharing one probe with the backend reader. `list_keys` dropped any record it could not decode from the page. Concurrent removal stays a skip; anything else fails the listing rather than answering "these are your keys" with a set that silently omits one. * test(kms): cover every fail-closed path around the Local protection marker Each test fails on the pre-fix code in the way the fix is about: the salt cases because a replacement salt is published before startup validation fails, the export and restore cases because the verdict comes back as corruption, and the listing case because the record is edited out of the page. The restore commit marker's unknown-version branch had no test at all, unlike its Vault counterpart; it is now driven from the decoder, from the restore entry point, and from backend startup. Also states the widened salt guard in the Local backend operations doc, including the operator recovery path for an unrecognized record. * fix(kms): say 'not a readable JSON object' when the marker probe cannot parse The probe now fails on any input that is not a JSON object, not only on malformed JSON, so the message must cover both. * test(kms): assert the salt file before the error variant The replacement salt is written before the error the guard reports, so the file assertion is the one that fails on a regression. * test(kms): guard the new backup error variant's display string
This commit is contained in:
@@ -249,7 +249,7 @@ On startup the backend then:
|
||||
|
||||
- **Removes orphaned commit temp files.** The matcher is strict (`<prefix>.tmp-<uuid>`, never anything ending in `.key`), so published key files — including a key the user named to look like a temp file — are never touched. Publishing is atomic, so a matching leftover can only be an unpublished remnant of an interrupted commit.
|
||||
- **Validates every published `.key` file.** A record that fails to decode fails startup rather than being silently skipped.
|
||||
- **Guards the salt file.** If `.master-key.salt` is missing but the directory contains keys marked `encrypted-master-key`, initialization fails closed with a configuration error naming the salt path. A regenerated salt derives a different master key and can never decrypt those keys, so the correct recovery is to **restore the salt file (or the whole directory) from backup**, never to let a fresh salt be generated. An empty directory, or a legacy directory predating the salt file, still initializes normally.
|
||||
- **Guards the salt file.** If `.master-key.salt` is missing but the directory contains keys marked `encrypted-master-key`, initialization fails closed with a configuration error naming the salt path. A regenerated salt derives a different master key and can never decrypt those keys, so the correct recovery is to **restore the salt file (or the whole directory) from backup**, never to let a fresh salt be generated. The guard is equally strict about a record it cannot read or cannot interpret — for example one written by a newer RustFS that names an at-rest protection this build does not implement: such a directory's protection state is unknown, so no replacement salt is generated for it either. Recovery is to restore the salt file, run a build that understands the record, or move the unrecognized file out of `key_dir` after confirming it is not needed. An empty directory, or a legacy directory predating the salt file, still initializes normally.
|
||||
|
||||
### Backing up the key directory
|
||||
|
||||
|
||||
Reference in New Issue
Block a user