agent permissions: drop hive-c0re's swarm-agent-permissions.json fallback, fail closed before the first read
Removes the HiveCopy hand-off: hive-c0re no longer writes each agent's entry from swarm-agent-permissions.json into its runtime dir as swarm-permissions.json, and the harness and subagent daemon no longer read it. hive-c0re keeps the file as its own last read of the swarm for the manage_root_agent mounts. Also removes permissions_import::pending(), its only caller being the hand-off. No nix provisioned the copy, so no nix option is removed and no tombstone is needed. Rule after this change: until an agent's first successful read of its key it holds no permissions (no execution, so no bash), and a subagent it starts holds the same. After a read a set key wins, [] holds none and an unset key is the default. A failed read keeps the last value, so an unreachable queue at start keeps the agent closed until a read succeeds. The harness logs once at warn that it starts closed. Fixes #5315
This commit is contained in:
parent
6e7b5f84e9
commit
2f00f9041f
11 changed files with 121 additions and 283 deletions
|
|
@ -11,14 +11,12 @@
|
|||
//! hive-c0re enforces one member, [`Permission::ManageRootAgent`]: read by
|
||||
//! `lifecycle::host_config::set_nspawn_flags` on every container start, which
|
||||
//! is why a change to it takes a restart. The agent reads the tool members
|
||||
//! itself, and holds its entry here until its own first read succeeds:
|
||||
//! [`hand_at`] copies the entry into the agent's runtime dir.
|
||||
//! itself.
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use hive_types::Permission;
|
||||
use swarm_queue_client::agent_permissions::{HIVE_COPY_FILE, HiveCopy};
|
||||
use swarm_wire::agent_permissions::AgentPermissions;
|
||||
|
||||
const FILE: &str = "swarm-agent-permissions.json";
|
||||
|
|
@ -58,30 +56,6 @@ pub fn holds(path: &Path, agent: &str, perm: Permission) -> std::io::Result<bool
|
|||
Ok(effective(read_at(path)?.get(agent)).contains(&perm))
|
||||
}
|
||||
|
||||
/// Write `agent`'s [`HiveCopy`] into `runtime_dir`: its entry in the file at
|
||||
/// `stored` (normally [`path`]) and `import_pending`. Left alone when the
|
||||
/// copy there already says the same.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// As [`read_at`], or the write failed.
|
||||
pub fn hand_at(
|
||||
stored: &Path,
|
||||
import_pending: bool,
|
||||
agent: &str,
|
||||
runtime_dir: &Path,
|
||||
) -> std::io::Result<()> {
|
||||
let copy = HiveCopy {
|
||||
stored: read_at(stored)?.remove(agent).unwrap_or_default(),
|
||||
import_pending,
|
||||
};
|
||||
let dest = runtime_dir.join(HIVE_COPY_FILE);
|
||||
if dest.exists() && HiveCopy::read_at(&dest) == copy {
|
||||
return Ok(());
|
||||
}
|
||||
super::write_map(&dest, ©)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
|
@ -129,32 +103,6 @@ mod tests {
|
|||
assert!(!holds(&path, "atlas", Permission::ManageRootAgent).expect("read"));
|
||||
}
|
||||
|
||||
/// With the queue down nothing is read, so the agent starts on the copy
|
||||
/// written here: the stored grant, not the default.
|
||||
#[test]
|
||||
fn the_agent_is_handed_its_stored_grant_and_whether_the_import_is_pending() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
let stored = dir.path().join(FILE);
|
||||
let runtime = dir.path().join("run");
|
||||
write_at(
|
||||
&stored,
|
||||
&Stored::from([(
|
||||
"atlas".to_owned(),
|
||||
record(r#"{"permissions":["web_tools"]}"#),
|
||||
)]),
|
||||
)
|
||||
.expect("seed");
|
||||
|
||||
hand_at(&stored, true, "atlas", &runtime).expect("hand");
|
||||
let copy = HiveCopy::read_at(&runtime.join(HIVE_COPY_FILE));
|
||||
assert!(copy.import_pending);
|
||||
assert_eq!(copy.before_read(), vec![Permission::WebTools]);
|
||||
|
||||
hand_at(&stored, false, "iris", &runtime).expect("hand");
|
||||
let copy = HiveCopy::read_at(&runtime.join(HIVE_COPY_FILE));
|
||||
assert_eq!(copy, HiveCopy::default(), "no entry is the default");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_corrupt_file_is_an_error() {
|
||||
let dir = tempfile::tempdir().expect("tempdir");
|
||||
|
|
|
|||
Loading…
Reference in a new issue