Skip to main content

MigratingSecretStore

Struct MigratingSecretStore 

Source
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

Source

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

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl SecretStore for MigratingSecretStore

Source§

fn get(&self, key: &str) -> StoreResult<Option<Vec<u8>>>

Read a secret.
Source§

fn put(&self, key: &str, value: &[u8]) -> StoreResult<()>

Write a secret, replacing any previous value.
Source§

fn delete(&self, key: &str) -> StoreResult<()>

Remove a secret. Removing one that is not there is not an error.
Source§

fn get_or_create( &self, key: &str, mk: &mut dyn FnMut() -> StoreResult<Vec<u8>>, ) -> StoreResult<Vec<u8>>

Return the stored secret, or create it with mk and store it, atomically. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<'a, T, E> AsTaggedExplicit<'a, E> for T
where T: 'a,

Source§

fn explicit(self, class: Class, tag: u32) -> TaggedParser<'a, Explicit, Self, E>

Source§

impl<'a, T, E> AsTaggedImplicit<'a, E> for T
where T: 'a,

Source§

fn implicit( self, class: Class, constructed: bool, tag: u32, ) -> TaggedParser<'a, Implicit, Self, E>

Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<ST, DT> CastableFrom<ST, Initialized, Initialized> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<ST, DT> CastableFrom<ST, Uninit, Uninit> for DT
where ST: ?Sized, DT: ?Sized,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self> ⓘ

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self> ⓘ

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Read<Exclusive, BecauseExclusive> for T
where T: ?Sized,

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T> SecretStoreExt for T
where T: SecretStore + ?Sized,

Source§

fn get_or_create_with<F>(&self, key: &str, mk: F) -> Result<Vec<u8>, Error>
where F: FnOnce() -> Result<Vec<u8>, Error>,

SecretStore::get_or_create taking a FnOnce, as the spec writes it.
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self> ⓘ
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self> ⓘ

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more