feat(proxy): add ACL-backed global mode with local bypass

Global mode now deploys a minimal proxy_all ACL carrying only the fixed
local safety rules, so public targets default through Shadowsocks while
loopback/RFC1918/link-local/ULA/localhost/*.local/no-dot hosts stay
direct for SOCKS, HTTP, and LAN-sharing clients. System proxy exceptions
include that fixed scope; IPv6 system-exception limits are documented
with the ACL as the routing guarantee. Mode switches keep the
full-restart and rollback path.

Closes #62
This commit is contained in:
Qiu Yuzhou
2026-09-26 12:33:35 +08:00
parent 39e25477bb
commit f2e9cff69d
10 changed files with 532 additions and 21 deletions
+19
View File
@@ -0,0 +1,19 @@
# Implement global mode with a minimal ACL
**Status**: accepted
## Context
Global mode previously projected only the system SOCKS endpoint and relied on sslocal's default proxy-everything behaviour. That left local-network targets without a product-owned direct guarantee: LAN-sharing clients, IPv6 destinations whose system-proxy exceptions macOS does not reliably honour, and any request that reaches the SOCKS/HTTP inbounds still needed a defined routing policy. The ACL work (spec #59) requires global mode to mean "public targets default through Shadowsocks, local targets stay direct", using only the fixed local safety rules.
## Decision
- Global mode deploys an `sslocal` ACL with `[proxy_all]` and a `[bypass_list]` containing only the fixed local safety rules: IPv4 loopback, RFC 1918, link-local; IPv6 loopback, link-local, and ULA; `localhost`, `*.local`, and no-dot hostnames. No China list, GFWList, or custom rules enter this ACL. CGNAT is excluded.
- The same ACL is the routing policy for the local SOCKS inbound, the local HTTP inbound, and LAN-sharing clients that connect to those listeners directly. It is not a substitute for the system proxy exception list; both use the same fixed local scope, but the ACL is the verified bypass for requests that already entered `sslocal`.
- IPv6 CIDR strings in the macOS SystemConfiguration exceptions list are not a verified bypass guarantee (only exact IPv6 addresses were observed to match). The ACL's IPv6 rules are the routing guarantee; this limitation is recorded rather than papered over by the exception list.
- ACL path, content, digest, and summary are runtime identity, exactly as in ADR-0009. Switching into or out of global mode changes the ACL and therefore requires a full `sslocal` restart through the wrapper — never a bare SIGUSR1 server-list reload. On verification failure the controller restores the previous mode, runtime document, and applied system proxy.
- Without a valid active target the agent still listens with an empty server list under the global ACL; local targets remain reachable, public targets have no exit. System proxy intent stays pending and converges automatically once an active target is valid and the local endpoints are healthy.
## Consequences
Global mode and direct mode share one fixed local safety policy and one ACL deployment path. Global is no longer "no ACL": it is a minimal `proxy_all` ACL. System proxy writes still require the system proxy switch, agent health, and an available exit (an active target for global). PAC mode remains unchanged until the rules-mode work replaces it.