workload_spec/secrets.rs
1//! Pluggable secret resolver for [`crate::SecretRef`] values.
2//!
3//! The trait lives in `workload-spec` so consumers can construct specs and
4//! invoke the resolver without linking yubaba's containerd client. Yubaba
5//! provides the production impl in `crates/yah/yubaba/src/secrets.rs`.
6
7use std::path::PathBuf;
8
9use thiserror::Error;
10
11use crate::SecretRef;
12
13/// Errors returned by [`SecretResolver::resolve`].
14#[derive(Debug, Error)]
15pub enum SecretError {
16 /// The referenced secret file does not exist in the yubaba secret store.
17 #[error("secret not found at {path}")]
18 NotFound { path: PathBuf },
19
20 /// `SecretRef::Cluster` reached a resolver that has no cluster backing —
21 /// e.g. the per-machine `LocalFileResolver`, which cannot decrypt cluster
22 /// secrets. The fleet resolver (yubaba's `ClusterResolver`) handles the
23 /// `Cluster` arm; this error means the wrong resolver was used.
24 #[error("cluster secrets require a cluster-backed resolver")]
25 ClusterNotImplemented,
26
27 /// The referenced cluster secret is not present in the local raft replica
28 /// (never written, or deleted). Fails closed — nothing is served.
29 #[error("cluster secret {name} not found in the local raft replica")]
30 ClusterNotFound { name: String },
31
32 /// Decryption or authentication of a cluster secret failed — a wrong
33 /// node-local KEK, a truncated/tampered record, or a malformed nonce. Fails
34 /// closed; the message carries only the logical name, never key or
35 /// ciphertext bytes.
36 #[error("cluster secret {name} failed to decrypt")]
37 ClusterDecrypt { name: String },
38
39 /// The node-local cluster KEK could not be loaded (missing, unreadable, or
40 /// not exactly 32 bytes). Fails closed; `reason` is a generic diagnostic
41 /// and never contains key material.
42 #[error("cluster KEK unavailable: {reason}")]
43 Kek { reason: String },
44
45 /// I/O error reading the secret file.
46 #[error("I/O error reading {path}: {source}")]
47 Io {
48 path: PathBuf,
49 #[source]
50 source: std::io::Error,
51 },
52}
53
54/// Resolves a [`SecretRef`] to its raw byte content.
55///
56/// The trait is defined here (in `workload-spec`) so callers don't need to
57/// link yubaba. Yubaba's `LocalFileResolver` reads from the per-machine secret
58/// store at `/var/lib/yah/yubaba/secrets/`. Tests use an inline `FakeResolver`.
59pub trait SecretResolver {
60 fn resolve(&self, r: &SecretRef) -> Result<Vec<u8>, SecretError>;
61}