fix(tor): never emit a SOCKS listener the host cannot bind
regenerate_torrc widened SOCKS to the archy-net gateway whenever `podman
network inspect archy-net` reported one. Podman reporting a gateway is not
proof the host can bind it: under rootless podman the bridge and its gateway
live inside a network namespace and never appear on a host interface.
Tor binds listeners at STARTUP, not at config-check time — `--verify-config`
passes happily — so the failure lands as a refusal to start. austin-sapien,
2026-08-09:
[warn] Could not bind to 10.89.0.1:9050: Cannot assign requested address
[warn] Failed to parse/validate config: Failed to bind one of the listener ports.
[err] Reading config failed--see warnings above.
Tor then died completely — loopback SOCKS and every hidden service with it —
having run 3d 18h before something restarted it. The node's Home tab still
read "connected" because tor.service is Debian's multi-instance MASTER unit,
which is active(exited) by design; the real instance, tor@default, was failed.
Whether a node is affected is pure timing: archi-dev-box's torrc has only
`SocksPort 9050` because archy-net was not discoverable when its torrc was
last regenerated, so it fell into the loopback branch and Tor starts fine.
Nodes whose torrc was generated while archy-net was visible are one Tor
restart away from the same outage.
The gateway is now probed the way Tor will use it — bind an ephemeral port on
that exact address — and a failure falls through to the existing loopback-only
branch, which was already written and commented "fail closed". Widening SOCKS
must never be able to take the whole daemon down.
Verified on this host: 127.0.0.1 binds, 10.89.0.1 returns EADDRNOTAVAIL.
Tor restored on austin-sapien and confirmed end to end — bootstrapped 100%,
and a request through the proxy returns {"IsTor":true}.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
791e6e5ed9
commit
2090cef3bf
@@ -184,9 +184,42 @@ async fn archy_net_gateway_and_subnet() -> Option<(String, String)> {
|
||||
if gateway.is_empty() || subnet.is_empty() {
|
||||
return None;
|
||||
}
|
||||
// Podman REPORTING a gateway is not proof the host can bind it. Under
|
||||
// rootless podman the bridge and its gateway live inside a network
|
||||
// namespace, so the address never appears on a host interface — and Tor
|
||||
// binds it at startup, not at config-check time (`--verify-config` passes
|
||||
// happily). The result on austin-sapien, 2026-08-09:
|
||||
//
|
||||
// [warn] Could not bind to 10.89.0.1:9050: Cannot assign requested address
|
||||
// [warn] Failed to parse/validate config: Failed to bind one of the listener ports.
|
||||
// [err] Reading config failed--see warnings above.
|
||||
//
|
||||
// Tor then refuses to start AT ALL — loopback SOCKS and every hidden
|
||||
// service go with it. Widening SOCKS must never be able to take the whole
|
||||
// daemon down, so probe the address exactly the way Tor will and fall back
|
||||
// to the loopback-only branch when it is not bindable.
|
||||
if !host_can_bind(&gateway) {
|
||||
tracing::warn!(
|
||||
%gateway,
|
||||
"archy-net gateway is not bindable on this host (rootless podman \
|
||||
namespaces the bridge) — keeping Tor SOCKS loopback-only"
|
||||
);
|
||||
return None;
|
||||
}
|
||||
Some((gateway, subnet))
|
||||
}
|
||||
|
||||
/// Can this host actually bind `addr`? Binds an ephemeral port, the same
|
||||
/// operation Tor performs, so the answer matches Tor's own behaviour rather
|
||||
/// than inferring it from interface listings.
|
||||
fn host_can_bind(addr: &str) -> bool {
|
||||
use std::net::{IpAddr, SocketAddr, TcpListener};
|
||||
match addr.parse::<IpAddr>() {
|
||||
Ok(ip) => TcpListener::bind(SocketAddr::new(ip, 0)).is_ok(),
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(in crate::api::rpc) async fn regenerate_torrc(config: &ServicesConfig) -> Result<()> {
|
||||
let base = detect_hidden_service_base();
|
||||
let mut lines = vec![
|
||||
|
||||
Reference in New Issue
Block a user