pub struct MigratingSecretStore { /* private fields */ }Expand description
Read the old place, write the new one. The migration, and it is not a flag day.
get(k) redb row? ── value ──▶ it
│
tombstone ──▶ None (never falls through)
│
no row ──▶ legacy Arrow log ──▶ adopt into redb
get_or_create(k) redb row? ── value ──▶ it
│
no row ──▶ legacy? ── yes ──▶ adopt it, return it
└──── no ───▶ mk(), store, return§Why this is the shape, and what it costs to get it wrong
serve.rs calls ensure_host_key on every boot. The host key is the
one secret whose whole purpose is to be the same bytes it was yesterday: a
server that mints a fresh one gives every client
REMOTE HOST IDENTIFICATION HAS CHANGED, which is indistinguishable from an
attack. So the adoption of a legacy key happens inside the same
get_or_create that would otherwise mint, not in a separate migration step
that could be skipped, reordered or half-run:
- an appliance with a host key in the Arrow log boots, finds no redb row, reads the Arrow log, writes those exact bytes into redb and returns them — same key, same fingerprint, no warning;
- the next boot finds the redb row and never opens the Arrow log at all;
- a fresh installation has neither, mints once, and never grows an Arrow log in the first place;
- a boot that is interrupted between reading the old value and committing the new one has changed nothing, and the next boot repeats the adoption.
The Arrow log is never written and never deleted by this type. It stays on disk, byte for byte, as the rollback: a binary from before this change, pointed at the same data root, still finds its host key exactly where it left it. Removing it is a later, separate decision.
§The legacy handle is Option, and absent is the normal case
It is None when there is no secrets.arrow under the control directory,
which is every installation created after this change. A None legacy is
not a degraded MigratingSecretStore; it is the steady state, and the
type collapses to RedbSecretStore with one extra Option check.
Implementations§
Source§impl MigratingSecretStore
impl MigratingSecretStore
Sourcepub fn new(new: RedbSecretStore, legacy: Option<Arc<dyn SecretStore>>) -> Self
pub fn new(new: RedbSecretStore, legacy: Option<Arc<dyn SecretStore>>) -> Self
Wrap new, falling back to legacy for keys redb has never seen.
Trait Implementations§
Source§impl Debug for MigratingSecretStore
impl Debug for MigratingSecretStore
Source§impl SecretStore for MigratingSecretStore
impl SecretStore for MigratingSecretStore
Source§fn put(&self, key: &str, value: &[u8]) -> StoreResult<()>
fn put(&self, key: &str, value: &[u8]) -> StoreResult<()>
Source§fn delete(&self, key: &str) -> StoreResult<()>
fn delete(&self, key: &str) -> StoreResult<()>
Source§fn get_or_create(
&self,
key: &str,
mk: &mut dyn FnMut() -> StoreResult<Vec<u8>>,
) -> StoreResult<Vec<u8>>
fn get_or_create( &self, key: &str, mk: &mut dyn FnMut() -> StoreResult<Vec<u8>>, ) -> StoreResult<Vec<u8>>
mk and store it, atomically. Read more