hive-c0re pulls closures through the nix daemon; hive-priv verifies the swarm signature before applying
This commit is contained in:
parent
448b703995
commit
ecd4445680
1 changed files with 1257 additions and 584 deletions
|
|
@ -110,9 +110,10 @@ pub fn valid_meta_rev(rev: &str) -> bool {
|
|||
|
||||
/// Whether `path` is a store path in the shape nix prints one: `/nix/store/`,
|
||||
/// 32 lowercase letters or digits, `-`, then a name of the characters store
|
||||
/// names allow. The path crosses the wire in [`PrivRequest::RealiseClosure`],
|
||||
/// [`PrivRequest::UpdateContainer`] and [`PrivRequest::CreateContainer`], so
|
||||
/// `hive-priv` checks it before it hands it to nix.
|
||||
/// names allow. The path crosses the wire in [`PrivRequest::UpdateContainer`]
|
||||
/// and [`PrivRequest::CreateContainer`] (and the older
|
||||
/// [`PrivRequest::RealiseClosure`]), so `hive-priv` checks it before it hands
|
||||
/// it to nix, and hive-c0re before it names it to nix.
|
||||
#[must_use]
|
||||
pub fn valid_closure_path(path: &str) -> bool {
|
||||
let Some(rest) = path.strip_prefix("/nix/store/") else {
|
||||
|
|
@ -131,11 +132,6 @@ pub fn valid_closure_path(path: &str) -> bool {
|
|||
})
|
||||
}
|
||||
|
||||
/// What a failed [`PrivRequest::RealiseClosure`] says when nix refused a path
|
||||
/// because no trusted key signed it, so a caller can tell that apart from a
|
||||
/// cache that is down.
|
||||
pub const SIGNATURE_REFUSED: &str = "no signature by the swarm cache's key";
|
||||
|
||||
/// Root of per-agent state directories on the host.
|
||||
/// Subdirectory layout: `<AGENT_STATE_ROOT>/<name>/state/<file>`.
|
||||
/// Used by `WriteAgentStateFile` to derive the write path so the
|
||||
|
|
@ -269,8 +265,8 @@ pub enum PrivRequest {
|
|||
KillContainer { name: String },
|
||||
|
||||
/// Point `name`'s container profile at `toplevel`, then reload the unit if
|
||||
/// it runs. hive-priv never builds: `toplevel` must be the path the last
|
||||
/// successful `RealiseClosure` for `name` returned, and it refuses any other.
|
||||
/// it runs. hive-priv never builds: `toplevel` must already be in the store
|
||||
/// and signed by the swarm cache's key, and it refuses any other.
|
||||
/// When `stream` is `true`, hive-priv sends `PrivEvent::Line` messages
|
||||
/// as the process runs, then a terminal `PrivEvent::Done`.
|
||||
UpdateContainer {
|
||||
|
|
@ -292,18 +288,24 @@ pub enum PrivRequest {
|
|||
toplevel: String,
|
||||
},
|
||||
|
||||
/// `nix-store --realise <store_path>` from the swarm's binary cache, never
|
||||
/// building: the closure the swarm built for `agent`, downloaded into the
|
||||
/// store ahead of the update that applies it.
|
||||
/// Kept for an older hive-c0re that still asks hive-priv to download:
|
||||
/// hive-c0re now pulls through the nix daemon and hive-priv downloads
|
||||
/// nothing. The answer is `Ok` when `store_path` is already in the store
|
||||
/// and signed by the swarm cache's key (what `CreateContainer` and
|
||||
/// `UpdateContainer` require), else an error that starts `not available: `.
|
||||
/// `store_path` is checked by [`valid_closure_path`].
|
||||
///
|
||||
/// The request names no cache and no key: hive-priv takes the cache's
|
||||
/// address from its own unit's environment and the key and the pull token
|
||||
/// from files only root can write, and nix accepts the path only with a
|
||||
/// signature by that key. `store_path` is checked by
|
||||
/// [`valid_closure_path`]. A path nix could not substitute answers with an
|
||||
/// error that starts `not available: `.
|
||||
/// An older hive-priv answers the same request by downloading, and pins the
|
||||
/// path for the apply that follows; hive-c0re sends it to one only when
|
||||
/// that older hive-priv refused an apply.
|
||||
RealiseClosure { agent: String, store_path: String },
|
||||
|
||||
/// `systemctl start hive-priv-cache-facts.service`, bounded at 60 s: the
|
||||
/// root unit that reads this hive's cache facts from the store and writes
|
||||
/// the nix.conf fragment the daemon trusts. hive-c0re asks for it when the
|
||||
/// fragment is missing. The request names no unit.
|
||||
RefreshCacheFacts,
|
||||
|
||||
/// `nixos-container destroy <name>`
|
||||
DestroyContainer { name: String },
|
||||
|
||||
|
|
@ -880,6 +882,21 @@ mod tests {
|
|||
assert!(serde_json::from_str::<PrivRequest>(without).is_err());
|
||||
}
|
||||
|
||||
/// The facts refresh names no unit and carries no field: a stray field is
|
||||
/// dropped when it is read, so a request cannot choose what to start.
|
||||
#[test]
|
||||
fn the_cache_facts_refresh_carries_nothing() {
|
||||
let line = r#"{"op":"refresh_cache_facts","unit":"sshd.service"}"#;
|
||||
assert!(matches!(
|
||||
serde_json::from_str::<PrivRequest>(line),
|
||||
Ok(PrivRequest::RefreshCacheFacts)
|
||||
));
|
||||
assert_eq!(
|
||||
serde_json::to_string(&PrivRequest::RefreshCacheFacts).unwrap(),
|
||||
r#"{"op":"refresh_cache_facts"}"#
|
||||
);
|
||||
}
|
||||
|
||||
/// `toplevel` is required: hive-priv has no flake to build one from.
|
||||
#[test]
|
||||
fn a_request_without_a_toplevel_is_refused() {
|
||||
|
|
|
|||
Loading…
Reference in a new issue