agent UDS: chmod per-agent socket dir 0777 + log web_ui::serve errors
The harness runs as the non-root agent user; the per-agent /run/hive-agent/<name>/ dir lands at 0755 root:root after create_dir_all, so bind(2) of web.sock failed with EACCES. The error was invisible because the web_ui::serve future was tokio::spawn'd with its JoinHandle dropped — no log, no socket, agent looks unreachable through the gateway.
This commit is contained in:
parent
c7360cf0bb
commit
8b238bbfaf
2 changed files with 29 additions and 3 deletions
|
|
@ -564,15 +564,29 @@ async fn serve_main<S: Surface>(socket: &Path, poll_ms: u64) -> Result<()> {
|
||||||
socket.to_path_buf(),
|
socket.to_path_buf(),
|
||||||
S::FORGE_IS_MANAGER,
|
S::FORGE_IS_MANAGER,
|
||||||
));
|
));
|
||||||
tokio::spawn(web_ui::serve(
|
// Log web_ui::serve's error instead of dropping it. A bare
|
||||||
label,
|
// `tokio::spawn(web_ui::serve(...))` discards the JoinHandle, so
|
||||||
|
// any Err (e.g. EACCES from `bind_unix` when HIVE_WEB_SOCKET points
|
||||||
|
// at a dir the agent user can't write) vanishes — leaving an
|
||||||
|
// operator with no log line and no socket, debuggable only by
|
||||||
|
// staring at lifecycle.rs.
|
||||||
|
let web_ui_args = (
|
||||||
|
label.clone(),
|
||||||
port,
|
port,
|
||||||
login_state.clone(),
|
login_state.clone(),
|
||||||
bus.clone(),
|
bus.clone(),
|
||||||
socket.to_path_buf(),
|
socket.to_path_buf(),
|
||||||
files.clone(),
|
files.clone(),
|
||||||
turn_lock.clone(),
|
turn_lock.clone(),
|
||||||
));
|
);
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let (label, port, login_state, bus, socket, files, turn_lock) = web_ui_args;
|
||||||
|
if let Err(e) =
|
||||||
|
web_ui::serve(label, port, login_state, bus, socket, files, turn_lock).await
|
||||||
|
{
|
||||||
|
tracing::error!(error = %e, "web_ui::serve exited with error");
|
||||||
|
}
|
||||||
|
});
|
||||||
if matches!(initial, LoginState::NeedsLogin) {
|
if matches!(initial, LoginState::NeedsLogin) {
|
||||||
turn::wait_for_login(&claude_dir, login_state.clone(), &bus, poll_ms).await;
|
turn::wait_for_login(&claude_dir, login_state.clone(), &bus, poll_ms).await;
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1219,6 +1219,18 @@ fn set_nspawn_flags(
|
||||||
let socket_dir = crate::agent_sockets::agent_dir_for(agent_name);
|
let socket_dir = crate::agent_sockets::agent_dir_for(agent_name);
|
||||||
std::fs::create_dir_all(&socket_dir)
|
std::fs::create_dir_all(&socket_dir)
|
||||||
.with_context(|| format!("create {}", socket_dir.display()))?;
|
.with_context(|| format!("create {}", socket_dir.display()))?;
|
||||||
|
// 0777 so the in-container agent user (`sock`, `iris`, …) can
|
||||||
|
// `bind(2)` the `web.sock` file here. `create_dir_all` lands
|
||||||
|
// the dir at 0755 root:root; the harness runs as the non-root
|
||||||
|
// agent user inside the container and otherwise fails the
|
||||||
|
// bind with EACCES — silently, since web_ui::serve's spawn
|
||||||
|
// drops its JoinHandle (the gateway then sees an empty
|
||||||
|
// agent-sockets.json and the agent appears unreachable).
|
||||||
|
// Per-agent dir + per-agent bind-mount means no other
|
||||||
|
// container ever sees this path, so the wide mode is contained.
|
||||||
|
use std::os::unix::fs::PermissionsExt;
|
||||||
|
std::fs::set_permissions(&socket_dir, std::fs::Permissions::from_mode(0o777))
|
||||||
|
.with_context(|| format!("chmod 0777 {}", socket_dir.display()))?;
|
||||||
let _ = write!(
|
let _ = write!(
|
||||||
binds,
|
binds,
|
||||||
" --bind={socket_dir}:{socket_dir}",
|
" --bind={socket_dir}:{socket_dir}",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue