mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-10-20 05:15:31 +00:00
fix(egress): v4 round — bridge bind on Linux, listener-role split, fallback gate, audit.log truth
Addresses GodsBoy's May 30 follow-up review on PR #30179. P0 — Linux sandboxes could not reach the proxy: - _default_http_listen now binds the docker bridge gateway on Linux (host.docker.internal resolves to the bridge gateway there; the old loopback-only bind was unreachable from containers). Loopback stays the default on Docker Desktop platforms; bridge-less Linux falls back to loopback with a warning. - Live-testing the fix against the real v0.39 binary surfaced a second latent bug the host-side E2E had masked: v0.39's http_listen does NOT terminate CONNECT — tunnel_listen does. HTTPS_PROXY traffic through http_listen got 400s from upstream. build_proxy_config now binds tunnel_listen (CONNECT/MITM) on tunnel_port and http_listen (plain-HTTP forwards) on tunnel_port+1; docker.py points HTTPS_PROXY at tunnel_port and HTTP_PROXY at tunnel_port+1. - Liveness probes (start_proxy poll loop, get_status) now probe the CONFIGURED bind host via _read_http_listen_from_config() instead of hardcoded loopback, which would have killed a healthy bridge-bound daemon as 'never came up'. P2 — allow_env_fallback dead on the partial-secret path: - The missing-secret branch in _build_proxy_subprocess_env now honors proxy.allow_env_fallback exactly as its own error message promises. - cmd_start refuses (or warns, with the fallback flag) when credential_source=bitwarden but secrets.bitwarden is disabled/missing — closing the silent-degrade-to-host-env hole. P2 — audit.log decoy on v0.39: - Wizard pre-create failure downgraded to a warning (file is non-load-bearing until the version bump); success line qualified as 'reserved'; user + dev docs stop telling operators to wire monitoring to the path today. P3 — metrics comment no longer claims 'hermes egress status' surfaces the ephemeral metrics port (it can't; :0 is random and unrecorded). Validation: 180 unit tests pass (9 new), gated E2E passes, and a live end-to-end run against the real binary verified CONNECT-MITM with Authorization swap on tunnel_listen, plain-HTTP swap on tunnel_port+1, 403 on non-allowlisted hosts, and bind-host-aware status probing. Docs gain a Linux firewall troubleshooting section (container→docker0 INPUT drops, e.g. ufw default-deny).
This commit is contained in:
parent
42139c3cf0
commit
e2c2e41137
8 changed files with 500 additions and 128 deletions
|
|
@ -351,17 +351,19 @@ def cmd_setup(args: argparse.Namespace) -> int:
|
|||
]
|
||||
|
||||
audit_log_path = ip._proxy_state_dir() / "audit.log"
|
||||
# Pre-create the audit log with 0o600 so iron-proxy inherits private
|
||||
# perms instead of letting the daemon create it under the default
|
||||
# umask (potentially world-readable). Raises on failure (planted
|
||||
# symlink, immutable parent, full disk) — the wizard must surface
|
||||
# that rather than print "✓" for a file the daemon will create
|
||||
# under a slacker umask.
|
||||
# Pre-create the audit log with 0o600. On the pinned v0.39 the
|
||||
# daemon does NOT write to this file (no ``log.audit_path`` field in
|
||||
# its config schema) — it's reserved for the v0.40+ upgrade where
|
||||
# per-request records start flowing. Because the file is
|
||||
# non-load-bearing today, a pre-create failure (immutable parent,
|
||||
# pre-existing foreign-owned file, full disk) is a WARNING, not a
|
||||
# setup abort.
|
||||
audit_log_ok = True
|
||||
try:
|
||||
ip.ensure_audit_log(audit_log_path)
|
||||
except RuntimeError as exc:
|
||||
console.print(f" [red]✗ {exc}[/red]")
|
||||
return 1
|
||||
audit_log_ok = False
|
||||
console.print(f" [yellow]⚠ {exc}[/yellow]")
|
||||
|
||||
# Allow operator override of the deny list via
|
||||
# ``proxy.upstream_deny_cidrs`` — but the default (None) gives a safe
|
||||
|
|
@ -381,7 +383,12 @@ def cmd_setup(args: argparse.Namespace) -> int:
|
|||
mappings_path = ip.write_mappings(mappings)
|
||||
console.print(f" [green]✓[/green] config: {cfg_path}")
|
||||
console.print(f" [green]✓[/green] mappings: {mappings_path}")
|
||||
console.print(f" [green]✓[/green] audit log: {audit_log_path}")
|
||||
if audit_log_ok:
|
||||
console.print(
|
||||
f" [green]✓[/green] audit log: {audit_log_path} "
|
||||
f"[dim](reserved — not written by iron-proxy v0.39; "
|
||||
f"per-request records land in iron-proxy.log)[/dim]"
|
||||
)
|
||||
|
||||
# ------------------------------------------------------------------ enable
|
||||
proxy_cfg["enabled"] = True
|
||||
|
|
@ -451,6 +458,32 @@ def cmd_start(args: argparse.Namespace) -> int:
|
|||
and bw_cfg is not None
|
||||
and bool(bw_cfg.get("enabled"))
|
||||
)
|
||||
# Silent-degrade guard: the operator explicitly chose
|
||||
# ``credential_source: bitwarden``, but secrets.bitwarden has since
|
||||
# been disabled or removed. Proceeding would quietly start on host
|
||||
# env — exactly the bug class the BW mode is meant to defeat. Refuse
|
||||
# unless the documented escape hatch is set.
|
||||
if credential_source == "bitwarden" and not refresh_bw:
|
||||
if bool(proxy_cfg.get("allow_env_fallback", False)):
|
||||
console.print(
|
||||
"[yellow]⚠ credential_source=bitwarden but "
|
||||
"secrets.bitwarden is disabled or missing — falling back "
|
||||
"to host-env secrets (allow_env_fallback=true). Rotated "
|
||||
"Bitwarden keys will NOT propagate.[/yellow]"
|
||||
)
|
||||
else:
|
||||
console.print(
|
||||
"[red]✗ Refusing to start: proxy.credential_source is "
|
||||
"'bitwarden' but secrets.bitwarden is disabled or "
|
||||
"missing.[/red]"
|
||||
)
|
||||
console.print(
|
||||
" Re-enable it (`secrets.bitwarden.enabled: true`), switch "
|
||||
"back to env credentials with `hermes egress setup "
|
||||
"--no-bitwarden`, or set `proxy.allow_env_fallback: true` "
|
||||
"to opt into the host-env fallback."
|
||||
)
|
||||
return 1
|
||||
# Pass the proxy-side allow_env_fallback opt-in through to
|
||||
# start_proxy. This is a deliberate, documented escape hatch: when
|
||||
# set, the daemon silently falls back to host env if BWS is
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue