refactor(maestro): drop allow_inbound policy gate, keep JWT session binding (NAP-339)
The peer (T2) attach path had a per-pair access-control layer (`[daemon.policy].allow_inbound`): a target-side allow-list of daemon ids permitted to broker-attach, enforced inside JWT verification, hot-reloadable via SIGHUP / `POST /api/policy/reload`, with a `maestro policy` CLI. On a flat-trust perimeter (the operator's VPN/firewall is the boundary for which hosts connect) that granularity is unused, and deny-by-default made it config friction — cross-daemon attach silently failed until every peer was listed. Remove the allow-list; keep the registry-minted pair-attach JWT and ALL its binding checks (iss=registry, aud=target, sid=session, exp). The firewall gates host->port reachability; it does not stop a reachable host from attaching to a session it wasn't handed or replaying a token elsewhere — that's the JWT's job and it stays. Only the `sub in allow_inbound` check is dropped. Removed: DaemonPolicySection / [daemon.policy].allow_inbound (config + daemon), policy.rs (SIGHUP listener + GET/POST /api/policy), the `maestro policy` CLI, PairAttachVerifier's allow-list field/reload/snapshot + JwtError::NotAllowed, and the e2e broker_rejects_when_target_doesnt_allow_inbound test. The NAP-265 pre-upgrade-502 path stays covered by broker_attach_returns_502_on_perimeter_mismatch. Also dropped the unit's ExecReload=kill -HUP: with no SIGHUP handler left, default SIGHUP disposition is terminate, so wiring `systemctl reload` to it would kill the daemon. Re-introducing a per-pair gate later is a verify-time change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,8 @@
|
||||
//! * `list` reads the daemon's local catalog mirror (own ∪ peer sessions).
|
||||
//! * `spawn` / `kill` hit the daemon's local session endpoints.
|
||||
//! * `attach` opens the daemon's `WS /api/attach/:id` broker, which picks the
|
||||
//! transport and enforces `allow_inbound` on the target side.
|
||||
//! transport and dials the target (the target verifies the registry-minted
|
||||
//! pair-attach JWT — session binding, not a per-peer allow-list).
|
||||
//!
|
||||
//! If the home daemon is **unreachable** (connection refused / not running),
|
||||
//! every command falls back to the legacy registry path (registry HTTP +
|
||||
@@ -23,10 +24,9 @@
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
use crate::attach::{broker_attach_url, parse_detach_key, run_attach, run_attach_url};
|
||||
use crate::cli::{AttachArgs, Command, KillArgs, ListArgs, PolicyCmd, SetRegistryArgs, SpawnArgs};
|
||||
use crate::cli::{AttachArgs, Command, KillArgs, ListArgs, SetRegistryArgs, SpawnArgs};
|
||||
use crate::config::{default_config_path, CliConfig, ConfigError};
|
||||
use crate::http::{HttpError, RegistryClient};
|
||||
use crate::policy::{run_reload as run_policy_reload, run_show as run_policy_show};
|
||||
use crate::sessions::{fetch_connect_info, run_kill, run_list, run_spawn};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
@@ -40,8 +40,6 @@ pub enum CliError {
|
||||
Attach(#[from] crate::attach::AttachError),
|
||||
#[error("{0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
#[error("{0}")]
|
||||
Policy(#[from] crate::policy::PolicyError),
|
||||
}
|
||||
|
||||
/// Dispatch a parsed top-level subcommand. The umbrella binary passes the
|
||||
@@ -61,7 +59,6 @@ pub async fn dispatch(
|
||||
|
||||
match command {
|
||||
Command::SetRegistry(args) => set_registry(&config_path, args),
|
||||
Command::Policy(sub) => policy(&config_path, sub).await,
|
||||
Command::List(args) => {
|
||||
list(
|
||||
&config_path,
|
||||
@@ -109,16 +106,6 @@ fn set_registry(config_path: &Path, args: SetRegistryArgs) -> Result<(), CliErro
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::result_large_err)]
|
||||
async fn policy(config_path: &Path, sub: PolicyCmd) -> Result<(), CliError> {
|
||||
let mut stdout = std::io::stdout().lock();
|
||||
match sub {
|
||||
PolicyCmd::Reload => run_policy_reload(config_path, &mut stdout).await?,
|
||||
PolicyCmd::Show { json } => run_policy_show(config_path, json, &mut stdout).await?,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[allow(clippy::result_large_err)]
|
||||
async fn list(
|
||||
config_path: &Path,
|
||||
|
||||
Reference in New Issue
Block a user