Exit nodes on macOS: pf anchor for offering, routes plus route-to for using
The exit-node host rules existed only for Linux (iptables + policy routing); macOS reported "only supported on Linux yet". Offering: a pf anchor com.apple/tsunagi-exit-<if> (evaluated by the stock pf.conf) holds a nat rule per offered range and a pass-in rule. pf is enabled if needed and the pfctl -E reference is released when the rules are removed. net.inet.ip.forwarding is only read, never written, as on Linux. Using: macOS has no uidrange policy routing and the agent is root, so the overlay gets 0.0.0.0/1 and 128.0.0.0/1 routes and a pf route-to rule (matched by the agent's uid) sends the agent's own QUIC/relay traffic back out the physical interface, translated to its address. The rules are loaded before the routes, so a failed load never sends traffic into a tunnel it cannot exempt the agent from. IPv6 is blocked with rejecting ::/1 and 8000::/1 routes. ExitHostPlan gains `overlay` (every network range) for the route-to exemption; Linux ignores it. Known limits: the egress is the default route's interface at apply time; other root processes also bypass the tunnel. Forwarding hints in the CLI and tray name the macOS sysctl. The rule text and command order are unit-tested with a scripted host; nothing was run against a live pf/route (needs root), so the route-to + nat second-pass behaviour is unverified. Checks: cargo fmt --check, cargo clippy --locked --workspace --all-targets -D warnings and cargo test --locked --workspace --all-targets, run on macOS only. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
0cfc1cbafc
commit
47c5d38d0c
9 files changed
+1028
-13
No files matched your search
@@ -354,6 +354,7 @@ impl Interface {
|
||||
interface: self.name.clone(),
|
||||
offer: ranges,
|
||||
client: self.routes.exit_via().is_some(),
|
||||
overlay: self.routes.overlay_ranges(),
|
||||
};
|
||||
|
||||
let first = !std::mem::replace(&mut self.exit_state().cleaned, true);
|
||||
@@ -398,9 +399,13 @@ impl Interface {
|
||||
tracing::warn!(
|
||||
interface = %self.name,
|
||||
"kernel forwarding is off for this interface, so nothing is forwarded for the \
|
||||
exit node yet: `sysctl -w net.ipv4.conf.{0}.forwarding=1` (or \
|
||||
`net.ipv4.ip_forward=1`), and set it in sysctl.d to keep it",
|
||||
self.name
|
||||
exit node yet: {}",
|
||||
if cfg!(target_os = "macos") {
|
||||
"`sysctl -w net.inet.ip.forwarding=1`, set again after every reboot"
|
||||
} else {
|
||||
"`sysctl -w net.ipv4.ip_forward=1` (or `net.ipv4.conf.<interface>.forwarding=1`), \
|
||||
and set it in sysctl.d to keep it"
|
||||
}
|
||||
);
|
||||
}
|
||||
match &report.client {
|
||||
@@ -443,7 +448,7 @@ impl Interface {
|
||||
"this overlay interface is not on the host (no TUN device), so there is \
|
||||
nothing to forward to",
|
||||
),
|
||||
Some(false) => Some("exit nodes are only supported on Linux yet"),
|
||||
Some(false) => Some("exit nodes are not supported on this platform yet"),
|
||||
Some(true) => None,
|
||||
};
|
||||
let failed = |detail: &str| RuleSetReport {
|
||||
|
||||
Reference in new issue
Block a user