fix: declare the agent socket dir's owner in tmpfiles, not by chown after
/run/hive-agent/<name> was 0777 root root in steady state, not just during first spawn. A directory without the sticky bit lets any user unlink files in it, and the gateway container has all of /run/hive-agent bind-mounted in, so anything that could reach the path could delete an agent's agent.sock, bind its own, and receive that agent's todos from hive-c0re. Two mechanisms were writing the dir and undoing each other: the tmpfiles.d entry wrote 0777 root root, then hive-c0re round-tripped through hive-priv's ChownSocketDir to narrow it. `d` re-asserts mode and owner on every apply and the file is regenerated on any agent's spawn or destroy, so every such event reset every agent's dir back to world-writable. SyncAgentTmpfiles now carries each agent's container uid/gid and the entry declares the answer: 0751 <uid> <gid>. Three principals need the dir and no two share a group -- the harness binds its sockets (owner rwx), hive-c0re dials agent.sock and the gateway's nginx dials web.sock (both only need traverse, and both sockets are already 0666). Deletes ChownSocketDir and ChmodSocketDir, both priv_client wrappers, the either/or in host_config with its two swallowed warn!s, and the now-dead socket_dir_path -- two verbs off the privileged helper's surface and one round-trip off every agent spawn. Also makes the two tmpfiles rules for /run/hive-agent itself agree: the gateway module said hive-core, the generated file said root, and which won depended on the order systemd read them in.
This commit is contained in:
parent
642377c5e0
commit
3fc1588e83
8 changed files with 110 additions and 103 deletions
|
|
@ -375,19 +375,6 @@ pub enum PrivRequest {
|
|||
/// namespace via the machine bus (forbidden for unprivileged users).
|
||||
ReloadGatewayNginx,
|
||||
|
||||
// --- Socket dir ownership ---
|
||||
/// Set ownership of `/run/hive-agent/<agent_name>/` to `uid:gid`.
|
||||
/// Called by `lifecycle::set_nspawn_flags` after `create_dir_all`.
|
||||
ChownSocketDir {
|
||||
agent_name: String,
|
||||
uid: u32,
|
||||
gid: u32,
|
||||
},
|
||||
|
||||
/// Set mode of `/run/hive-agent/<agent_name>/`.
|
||||
/// Fallback when uid lookup returns `None` on first spawn.
|
||||
ChmodSocketDir { agent_name: String, mode: u32 },
|
||||
|
||||
// --- Forge admin CLI ---
|
||||
/// Run `forgejo admin <args>` inside the `hive-forge` container as the
|
||||
/// `forgejo` unix user. hive-priv executes:
|
||||
|
|
@ -754,12 +741,34 @@ pub enum PrivRequest {
|
|||
/// Called at hive-c0re startup and after every agent spawn / destroy.
|
||||
/// Agents are logical names (validated by `validate_agent_name`).
|
||||
SyncAgentTmpfiles {
|
||||
/// Logical agent names (e.g. `"atlas"`, `"ruth"`). hive-priv validates
|
||||
/// each name before writing any path component derived from it.
|
||||
agents: Vec<String>,
|
||||
/// One entry per live agent. hive-priv validates each name before
|
||||
/// writing any path component derived from it.
|
||||
agents: Vec<AgentTmpfilesEntry>,
|
||||
},
|
||||
}
|
||||
|
||||
/// One agent's runtime-dir declaration for `SyncAgentTmpfiles`.
|
||||
///
|
||||
/// Carries the container uid/gid so the tmpfiles entry can *declare* who owns
|
||||
/// `/run/hive-agent/<name>` instead of having it corrected afterwards by a
|
||||
/// privileged chown. The two mechanisms used to fight: the tmpfiles line wrote
|
||||
/// `0777 root root` and a follow-up `ChownSocketDir` narrowed it, but any
|
||||
/// later spawn or destroy re-applied the file and reset *every* agent's dir
|
||||
/// back to world-writable.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct AgentTmpfilesEntry {
|
||||
/// Logical agent name (e.g. `"atlas"`, `"ruth"`).
|
||||
pub name: String,
|
||||
/// Container uid/gid of the agent user, when known.
|
||||
///
|
||||
/// `None` only before the container's `/etc/passwd` has been rendered
|
||||
/// (first boot). The dir must stay writable by the not-yet-identifiable
|
||||
/// harness in that window, so hive-priv falls back to the historical
|
||||
/// permissive mode for that one agent; the next sync tightens it.
|
||||
pub uid: Option<u32>,
|
||||
pub gid: Option<u32>,
|
||||
}
|
||||
|
||||
/// Response from the privileged helper.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct PrivResponse {
|
||||
|
|
|
|||
Loading…
Reference in a new issue