Add proxy subscription runtime support
Add daemon RPCs, Apple and GTK import flows, packet proxy runtime support, diagnostics, and BEPs for proxy subscription handling. Redact subscription URL secrets from fetch errors before they reach logs or UI surfaces.
This commit is contained in:
parent
97c569fb35
commit
d1638726ca
46 changed files with 15079 additions and 456 deletions
|
|
@ -31,6 +31,11 @@ Burrow should formalize one Apple/runtime boundary: Apple clients speak only to
|
|||
- login/session lifecycle brokering
|
||||
- runtime start/stop/reconcile
|
||||
- translating helper or bridge processes into stable daemon RPCs
|
||||
- Apple uses two daemon sockets with one shared app-group database:
|
||||
- the app-facing control socket, `burrow.sock`, is for UI management RPCs such as network list, import, preview, refresh, node selection, account/login control, and other non-packet workflows
|
||||
- the packet-tunnel socket, `burrow-packet-tunnel.sock`, is opened by `PacketTunnelProvider` and owns active tunnel runtime RPCs such as `TunnelStart`, `TunnelConfiguration`, `TunnelPackets`, and `TunnelStop`
|
||||
- The Network Extension must host or connect to the packet-tunnel daemon socket while a VPN session is active. The app must not hot-swap packet runtime state underneath an already-running Apple `NEPacketTunnelProvider`.
|
||||
- UI-side network mutations may persist desired state through the control daemon. If those mutations affect active packet-tunnel settings or runtime, the Apple client must restart or otherwise reassert the Network Extension so settings and packet runtime are installed from the same selected network state.
|
||||
- `burrow/src/control/` owns transport-neutral control-plane semantics such as discovery, authority normalization, and request/response shaping.
|
||||
- Apple UI owns presentation only:
|
||||
- forms
|
||||
|
|
@ -43,7 +48,7 @@ Burrow should formalize one Apple/runtime boundary: Apple clients speak only to
|
|||
|
||||
- Keeping control-plane I/O out of Swift UI reduces accidental secret, token, and callback sprawl across app code.
|
||||
- The daemon boundary makes testing and kill-switch behavior tractable because runtime integration is localized.
|
||||
- Apple daemon lifecycle ownership must be explicit: either the app ensures the daemon is running before RPC or the extension owns it and the UI surfaces daemon-unavailable state clearly.
|
||||
- Apple daemon lifecycle ownership must be explicit: the app owns the control daemon used for presentation workflows, and the Network Extension owns the packet daemon used for active tunnel runtime. Both use the app-group database as the durable desired-state store.
|
||||
- Non-Apple presentation clients should follow the same daemon-first lifecycle pattern: connect to a managed daemon when present, or start a user-scoped embedded daemon before issuing RPCs, without adding platform-local control-plane paths.
|
||||
|
||||
## Contributor Playbook
|
||||
|
|
|
|||
|
|
@ -0,0 +1,131 @@
|
|||
# `BEP-0009` - Trojan Proxy and Subscription Import
|
||||
|
||||
```text
|
||||
Status: Superseded
|
||||
Proposal: BEP-0009
|
||||
Authors: gpt-5.5
|
||||
Coordinator: gpt-5.5
|
||||
Reviewers: Pending
|
||||
Constitution Sections: II, IV, V
|
||||
Implementation PRs: Pending
|
||||
Decision Date: 2026-05-27
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
Superseded by `evolution/proposals/BEP-0010-proxy-subscriptions-as-packet-tunnel-networks.md`.
|
||||
|
||||
Burrow should add daemon-owned Trojan proxy support as a proxy egress transport, plus a subscription importer that can parse base64 URI subscriptions containing `trojan://` nodes. This proposal covers Trojan profile ingestion, validation, storage, runtime boundaries, and user-facing import behavior without claiming broader Clash or Mihomo protocol parity.
|
||||
|
||||
Trojan support should be implemented as an explicit Burrow protocol family with tested URI parsing and daemon-managed runtime behavior. Apple clients may present imported profiles and statuses, but they must continue to talk only to the daemon over gRPC.
|
||||
|
||||
## Motivation
|
||||
|
||||
- Real-world proxy subscriptions commonly expose Trojan nodes as base64-encoded lists of `trojan://` URIs rather than static Clash YAML.
|
||||
- Users should be able to import a subscription URL and let Burrow identify compatible nodes without leaking tokens into logs, source files, or UI copy.
|
||||
- Trojan is different from WireGuard, Tailnet, and future MASQUE `CONNECT-IP`; it needs an explicit profile model, TLS validation policy, and proxy egress runtime.
|
||||
- Supporting one proxy protocol first gives Burrow a concrete path for future proxy families without adding an unbounded "Clash compatible" claim.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
- Add a new daemon-owned network/profile type for Trojan proxy egress.
|
||||
- The stable code/protocol identifier should use `trojan`.
|
||||
- User-facing copy should spell the project name as `Burrow` and should name the protocol as `Trojan`.
|
||||
- The profile type should be distinct from `WireGuard` and `Tailnet`.
|
||||
- Define a `TrojanProfile` payload with at least:
|
||||
- display name
|
||||
- server host
|
||||
- server port
|
||||
- password or credential reference
|
||||
- TLS server name indication
|
||||
- optional peer/host metadata when present in imported subscriptions
|
||||
- transport, initially restricted to `tcp`
|
||||
- certificate verification policy
|
||||
- source metadata such as imported subscription name and refresh timestamp
|
||||
- Add a subscription import flow owned by the daemon.
|
||||
- Fetch subscription URLs in daemon code, not Swift UI.
|
||||
- Redact query tokens and credentials from logs and errors.
|
||||
- Accept base64 URI subscriptions whose decoded body is a newline-separated URI list.
|
||||
- Parse `trojan://` URIs and reject unknown schemes unless future proposals add them.
|
||||
- Preserve node labels from URI fragments as display names after normalization.
|
||||
- Treat `allowInsecure=1`, `skip-cert-verify=true`, or equivalent flags as an explicit insecure TLS policy.
|
||||
- Deduplicate imported nodes by stable profile fields rather than display label alone.
|
||||
- Add daemon gRPC methods before any Apple UI work.
|
||||
- A minimal shape is `SubscriptionImportPreview`, `SubscriptionImportApply`, and `SubscriptionRefresh`.
|
||||
- Preview should return compatible profile counts, rejected entry counts, redacted warnings, and normalized display labels.
|
||||
- Apply should write only selected compatible profiles.
|
||||
- Refresh should preserve local enable/order choices where possible.
|
||||
- Add runtime support in stages.
|
||||
- Stage 1: parser, preview, storage, and tests only.
|
||||
- Stage 2: daemon-managed Trojan outbound connector for TCP proxy egress.
|
||||
- Evaluate `trojan-rust/trojan-rust` as the first implementation dependency for this stage.
|
||||
- Prefer the narrowest usable crate, likely `trojan-proto` for protocol parsing/serialization or `trojan-client` for a SOCKS5-backed client runtime.
|
||||
- Avoid depending on the top-level `trojan` crate with default features unless Burrow needs the bundled CLI, certificate, SQL, or agent features.
|
||||
- Wrap any external runtime API behind a Burrow-owned adapter so profile storage, logging, kill-switch behavior, and daemon lifecycle remain local.
|
||||
- Stage 3: route/DNS integration with Burrow's tunnel and proxy selection model.
|
||||
- Stage 4: UI controls for import, refresh, certificate policy warnings, and per-profile status.
|
||||
- Keep the runtime boundary explicit.
|
||||
- Apple UI may display forms, previews, warning states, and daemon statuses.
|
||||
- Apple UI must not fetch subscription URLs directly.
|
||||
- Apple UI must not open Trojan sockets directly.
|
||||
- Any helper process must be brokered and supervised by the daemon.
|
||||
|
||||
## Security and Operational Considerations
|
||||
|
||||
- Subscription URLs often contain bearer tokens. Burrow must treat full subscription URLs as secrets.
|
||||
- Imported Trojan URIs contain passwords. Burrow must avoid writing decoded raw subscriptions to logs, crash reports, test snapshots, or telemetry.
|
||||
- Certificate verification defaults should be secure. Insecure imported nodes may be stored only with an explicit warning field and UI-visible risk state.
|
||||
- Passwords should move into the same secret-storage path Burrow uses for other long-lived credentials instead of remaining in plain payload blobs.
|
||||
- The importer must set fetch limits for response size, redirect depth, and timeout.
|
||||
- The parser must be independent and fuzzable enough to handle malformed URI lists without panics.
|
||||
- Runtime support should include a kill-switch path so disabling a profile tears down active sockets and route state.
|
||||
- Burrow must not present Trojan as a VPN with the same security or routing semantics as WireGuard. It is a proxy egress transport unless later proposals define full-device routing behavior.
|
||||
|
||||
## Contributor Playbook
|
||||
|
||||
1. Add parser-only fixtures for representative `trojan://` URIs, including TLS SNI, host, peer, TCP transport, fragments, duplicate labels, malformed ports, unsupported schemes, and insecure certificate flags.
|
||||
2. Add a redacted subscription fixture that mirrors a real base64 URI-list response without preserving live tokens, passwords, or hostnames that identify a paid account.
|
||||
3. Extend `proto/burrow.proto` with daemon-owned subscription preview/apply/refresh RPCs before adding Apple UI.
|
||||
4. Add daemon storage for `trojan` profiles and migrate old payload storage only if the profile model requires it.
|
||||
5. Verify generated Swift and Rust bindings compile after proto changes.
|
||||
6. Implement runtime behind a feature flag or internal capability gate until end-to-end proxy egress tests exist.
|
||||
7. Run:
|
||||
|
||||
```bash
|
||||
cargo test --workspace --all-features
|
||||
python3 Scripts/check-bep-metadata.py
|
||||
```
|
||||
|
||||
8. For Apple work, verify no Swift UI or support code fetches subscription URLs or talks to Trojan endpoints directly.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Import full Clash or Mihomo YAML first. Rejected for this proposal because it would imply a broad compatibility contract across many protocols before Burrow has a proxy abstraction.
|
||||
- Shell out to Mihomo as a helper. Rejected as the default path because it would create a large external runtime dependency and make Burrow's protocol guarantees harder to test. A future proposal may revisit helper-based compatibility as an explicit adapter.
|
||||
- Vendor or reimplement the Trojan wire protocol immediately. Rejected as the default path while `trojan-rust/trojan-rust` appears to provide GPL-compatible Rust crates for protocol parsing and client behavior. Burrow should still keep an adapter boundary so it can replace the dependency if maintenance, API, or security review fails.
|
||||
- Treat Trojan nodes as WireGuard-like networks. Rejected because Trojan is proxy egress over TLS, not a packet VPN with WireGuard peer semantics.
|
||||
- Let Apple UI fetch and parse subscriptions for convenience. Rejected because it violates the daemon IPC boundary and spreads subscription tokens into UI code.
|
||||
|
||||
## Impact on Other Work
|
||||
|
||||
- Depends on the daemon boundary in BEP-0005.
|
||||
- Should align with the transport-neutral route and policy work described in BEP-0003, but should not block parser-only subscription import.
|
||||
- Creates a path for future Shadowsocks or VLESS proposals by forcing shared subscription parsing and redaction rules.
|
||||
- May require a follow-up secret-storage proposal if current network payload storage cannot safely hold proxy credentials.
|
||||
|
||||
## Decision
|
||||
|
||||
Superseded by `evolution/proposals/BEP-0010-proxy-subscriptions-as-packet-tunnel-networks.md` on 2026-05-27.
|
||||
|
||||
## References
|
||||
|
||||
- Trojan protocol: https://trojan-gfw.github.io/trojan/protocol.html
|
||||
- Trojan config: https://trojan-gfw.github.io/trojan/config.html
|
||||
- Trojan reference implementation: https://github.com/trojan-gfw/trojan
|
||||
- Rust Trojan implementation candidate: https://github.com/trojan-rust/trojan-rust
|
||||
- Rust `trojan-client` crate: https://crates.io/crates/trojan-client
|
||||
- Rust `trojan-proto` crate: https://crates.io/crates/trojan-proto
|
||||
- Mihomo Trojan proxy configuration: https://wiki.metacubex.one/en/config/proxies/trojan/
|
||||
- BEP-0003: `evolution/proposals/BEP-0003-connect-ip-and-negotiation-roadmap.md`
|
||||
- BEP-0005: `evolution/proposals/BEP-0005-daemon-ipc-and-apple-boundary.md`
|
||||
- Burrow protocol schema: `proto/burrow.proto`
|
||||
|
|
@ -0,0 +1,271 @@
|
|||
# `BEP-0010` - Proxy Subscriptions as Packet Tunnel Networks
|
||||
|
||||
```text
|
||||
Status: Draft
|
||||
Proposal: BEP-0010
|
||||
Authors: Codex
|
||||
Coordinator: Codex
|
||||
Reviewers: Pending
|
||||
Constitution Sections: II, IV, V
|
||||
Implementation PRs: Pending
|
||||
Decision Date: Pending
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
Burrow should support Trojan and Shadowsocks subscription imports as packet-tunnel networks, not as local SOCKS or system proxy mode. The user-facing add action should be named `Import Proxy Subscription` on every UI surface. Imported proxy subscriptions should participate in Burrow's existing tunnel model: platform tunnel capture feeds daemon-owned routing and proxy execution, and a selected proxy outbound carries TCP and UDP flows.
|
||||
|
||||
This proposal supersedes `BEP-0009`. The earlier SOCKS-backed Trojan runtime was useful as a smoke-test path, but Burrow's product semantics are VPN-shaped. Keeping a local SOCKS mode would add another behavior surface, confuse how proxy networks relate to WireGuard and Tailnet, and delay the packet-tunnel work Burrow actually needs.
|
||||
|
||||
## Motivation
|
||||
|
||||
- Users expect the main Burrow tunnel switch to affect full-device traffic, as it does for packet-shaped WireGuard and Tailnet integrations.
|
||||
- Trojan and Shadowsocks subscriptions are commonly distributed as Clash/Mihomo YAML, proxy-provider content, plain URI lists, or base64 URI lists. Burrow's current Trojan parser only covers a narrow `trojan://` URI-list subset.
|
||||
- Mihomo's TUN mode is the closest reference behavior: it captures packets, runs a userspace IP stack, hijacks DNS, resolves rules and proxy selections, then dials protocol-specific outbounds.
|
||||
- Burrow should avoid a second user-facing "proxy mode" based on `HTTP_PROXY`, SOCKS settings, or a loopback listener because that is not equivalent to VPN behavior and creates platform-specific state outside Burrow's tunnel contract.
|
||||
- Subscription URLs and proxy node credentials are secret-bearing. Fetching, parsing, storing, refreshing, and redacting them belong in the daemon boundary described by `BEP-0005`.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
### User Model
|
||||
|
||||
- Add a UI action named `Import Proxy Subscription`.
|
||||
- Imported subscriptions are stored as `Network` records because they represent routeable connectivity sources.
|
||||
- Proxy nodes inside a subscription are selectable endpoints within that network.
|
||||
- Node selection should mirror Mihomo selector behavior in `adapter/outboundgroup/selector.go`: persist an explicit selected node when the user chooses one, validate that it exists, and fall back to the first runtime-supported node only when no explicit supported selection is available.
|
||||
- Proxy subscriptions do not create `Account` records by default. Accounts remain for identity and sign-in state such as Tailnet login or future provider identity flows.
|
||||
- Apple SwiftUI, Linux GTK, and future UI surfaces should expose the same import, preview, warning, selection, refresh, and node status behavior over the daemon API.
|
||||
- UI clients may show subscription name, refresh state, selected node, node health, warnings, and import preview results. They must not fetch subscription URLs or open Trojan/Shadowsocks sockets directly.
|
||||
- Import preview UIs must expose the runtime-supported Trojan and Shadowsocks nodes as a selectable list and pass the selected ordinal to the daemon `ApplyImport` request. Parsed-but-unsupported nodes should remain visible as warnings or counts, not selectable runtime choices.
|
||||
|
||||
### Removed SOCKS Mode
|
||||
|
||||
- Remove the daemon runtime path that starts a stored Trojan profile as a local SOCKS5 proxy.
|
||||
- Remove CLI and smoke entrypoints whose primary purpose is operating Burrow as a SOCKS proxy.
|
||||
- Keep parser tests and sanitized live-comparison tooling only when they validate subscription import or packet-tunnel behavior.
|
||||
- Do not expose system `HTTP_PROXY`, system SOCKS, PAC, or "set system proxy" behavior as part of this proposal.
|
||||
- If a temporary developer-only loopback proxy is needed for debugging, it must be clearly separate from product behavior and must not be the stored network runtime.
|
||||
|
||||
### Subscription Import Compatibility
|
||||
|
||||
Implement the importer in the daemon and mirror Mihomo's behavior as closely as Burrow's narrower protocol scope allows. Path references in this section are relative to the Mihomo repository root. When behavior is ambiguous, contributors should inspect the referenced Mihomo code path and either match it or document a deliberate deviation in this BEP before implementation.
|
||||
|
||||
- Full config parsing should recognize subscription-relevant top-level fields analogous to `proxies`, `proxy-providers`, and `proxy-groups` in `config/config.go`.
|
||||
- Provider parsing should support `file`, `http`, and `inline` style sources analogous to `adapter/provider/parser.go`.
|
||||
- Provider content parsing should first try YAML with a `proxies` field. If YAML cannot be parsed, fall back to V2Ray-style URI conversion, matching the shape in `adapter/provider/provider.go`.
|
||||
- Plain and base64 URI-list inputs should follow Mihomo's behavior in `common/convert/converter.go` and `common/convert/base64.go`: attempt raw/std base64 decoding, otherwise treat the body as plaintext.
|
||||
- Trojan URI conversion should closely follow Mihomo's `trojan` case in `common/convert/converter.go` and the accepted outbound fields in `adapter/outbound/trojan.go`. Burrow should preserve Mihomo-compatible names and semantics internally where practical, then adapt them into Burrow's storage model. It should map at least:
|
||||
- URI fragment to display name
|
||||
- host, port, and username to server, port, and password
|
||||
- `allowInsecure` to certificate verification policy
|
||||
- `sni`, `alpn`, `type=ws`, `type=grpc`, client fingerprint, and pinned certificate fingerprint where supported
|
||||
- Mihomo's default client fingerprint behavior where applicable
|
||||
- Mihomo's ws and grpc option shapes before translating them into Burrow-owned structs
|
||||
- Shadowsocks URI conversion should map at least:
|
||||
- legacy base64 host form
|
||||
- base64 `cipher:password` userinfo
|
||||
- server, port, cipher, password, and node name
|
||||
- `udp-over-tcp` or `uot`
|
||||
- common `obfs` and `v2ray-plugin` plugin options when support is implemented
|
||||
- Unsupported protocols, unsupported plugin modes, malformed nodes, duplicate node names, and insecure TLS flags must be returned in import preview warnings without logging credentials.
|
||||
|
||||
Burrow should not claim full Clash or full Mihomo compatibility in this proposal. The compatibility target is subscription import for Trojan and Shadowsocks nodes plus the minimum provider and group metadata needed to select and refresh outbounds. Within that target, subscription parsing and Trojan normalization should be intentionally Mihomo-like so a subscription that imports cleanly in Mihomo has the same node set, labels, security warnings, and supported Trojan transport metadata in Burrow unless the BEP records a specific exception.
|
||||
|
||||
### Data Model
|
||||
|
||||
Add a proxy-subscription network payload with:
|
||||
|
||||
- subscription display name
|
||||
- redacted subscription URL or a credential reference to the full URL
|
||||
- provider format detected during import
|
||||
- refresh policy and last refresh result
|
||||
- ordered proxy nodes
|
||||
- selected node or selection strategy
|
||||
- certificate and transport warnings
|
||||
- per-node protocol type, server metadata, transport metadata, and secret references
|
||||
|
||||
Proxy node secrets must move out of plain network payload blobs when Burrow's secret-storage path exists. Until then, payload storage must be treated as sensitive and logs/tests must use redacted fixtures.
|
||||
|
||||
### Daemon APIs
|
||||
|
||||
Add daemon gRPC APIs before platform UI work:
|
||||
|
||||
- preview import from subscription URL
|
||||
- apply selected preview result as a network
|
||||
- refresh an existing subscription network
|
||||
- list nodes for a proxy subscription network
|
||||
- set selected node or selection strategy
|
||||
|
||||
Preview responses should include compatible count, rejected count, detected format, sanitized warnings, suggested network name, and redacted node labels. Apply and refresh must preserve local node order and selected-node state where possible.
|
||||
|
||||
### Packet Tunnel Runtime
|
||||
|
||||
Burrow should route proxy subscriptions through the existing tunnel architecture:
|
||||
|
||||
1. A platform tunnel provider starts the tunnel and applies daemon-provided network settings.
|
||||
2. Platform packet capture forwards packets through the daemon packet interface. Apple uses `PacketTunnelProvider` and the existing `TunnelPackets` gRPC stream against the packet-tunnel daemon socket owned by the Network Extension; Linux should use the daemon-owned TUN path already aligned with the GTK client.
|
||||
3. The daemon owns a proxy packet engine for active proxy-subscription networks.
|
||||
4. The packet engine reconstructs TCP and UDP flows from IP packets.
|
||||
5. DNS packets are handled by a daemon-owned DNS path so hostname metadata can be preserved for rule selection and proxy dialing.
|
||||
6. Each flow is matched to the selected proxy node or selection strategy.
|
||||
7. The daemon opens a Trojan or Shadowsocks outbound connection and relays flow bytes.
|
||||
8. Return packets are serialized back to Apple through `TunnelPackets`.
|
||||
|
||||
Mihomo's equivalent kernel path is:
|
||||
|
||||
- TUN recreation and listener lifecycle in `listener/listener.go`
|
||||
- TUN option construction and stack startup in `listener/sing_tun/server.go`
|
||||
- DNS hijack in `listener/sing_tun/dns.go`
|
||||
- metadata conversion in `listener/sing/sing.go`
|
||||
- TCP and UDP handling in `tunnel/tunnel.go`
|
||||
- outbound protocol construction in `adapter/parser.go`, `adapter/outbound/trojan.go`, and `adapter/outbound/shadowsocks.go`
|
||||
|
||||
Burrow should use these as architecture references, not as code to vendor blindly. The Burrow implementation must fit the daemon IPC boundary, Rust runtime, Apple NetworkExtension path, Linux daemon/TUN path, GTK client, and existing network selection model.
|
||||
|
||||
### Implemented Runtime Shape
|
||||
|
||||
The first packet-tunnel implementation follows the Mihomo adapter shape but keeps Burrow's narrower protocol surface:
|
||||
|
||||
- `burrow/src/proxy_runtime.rs` owns the daemon packet engine.
|
||||
- Apple uses the existing `TunnelPackets` stream, matching the Tailnet packet-stream boundary.
|
||||
- Linux uses the daemon TUN bridge path.
|
||||
- Tunnel configuration excludes resolved selected proxy server host routes from the captured default route so proxy-server dials cannot be trapped by the full tunnel.
|
||||
- On Apple, the proxy packet runtime binds Trojan and Shadowsocks outbound sockets to a non-tunnel physical interface before connect and also installs resolved Network Extension excluded routes for the selected proxy server. Loopback proxy-server addresses are not physical-interface bound so controlled local tests and developer-only local endpoints can still connect. The excluded route is intentionally redundant: it prevents proxy-server dials and server DNS artifacts from recursively entering the full tunnel when socket binding is unavailable, delayed, or ignored by a platform path.
|
||||
- On Apple, node selection and refresh are applied to both the app-facing control daemon and, while connected, the packet-tunnel daemon over `burrow-packet-tunnel.sock`. The packet daemon swaps the active proxy outbound inside the existing packet bridge, so new TCP and UDP flows use the selected node without restarting the Network Extension. Existing flows may continue on the outbound they opened with until they close.
|
||||
- TCP and UDP packets are reconstructed with the existing smoltcp userspace stack.
|
||||
- Trojan TCP nodes are dialed over TLS and use Trojan TCP and UDP request framing directly.
|
||||
- Shadowsocks AEAD nodes are dialed directly for TCP and UDP using SIP004-style salt, HKDF subkeys, and AEAD chunks/datagrams.
|
||||
- Unsupported runtime transports such as Trojan `ws`/`grpc`, Shadowsocks plugins, and Shadowsocks UDP-over-TCP are still parsed and preserved from subscriptions, but they are rejected by runtime selection until those adapter transports are implemented. If no node is explicitly selected, Burrow chooses the first runtime-supported node instead of starting an unsupported transport.
|
||||
- Apple SwiftUI and Linux GTK import flows preview nodes, restrict the picker to runtime-supported nodes, and pass the chosen node ordinal to the daemon when applying an import.
|
||||
- Proxy-subscription tunnel settings point DNS at the daemon-owned tunnel address. The packet runtime answers ordinary A queries with fake IPs and records the fake-IP to hostname mapping so later TCP flows can send hostname-form proxy requests. This avoids making website DNS depend on outbound UDP support while preserving hostname metadata for Trojan and Shadowsocks dialing.
|
||||
|
||||
### Routing, DNS, and Apple Runtime Ownership
|
||||
|
||||
For full-tunnel proxy networks, daemon tunnel configuration should include:
|
||||
|
||||
- a virtual IPv4 address from a documentation or carrier-grade NAT range suitable for the packet engine
|
||||
- a virtual IPv6 address once the packet engine is enabled for IPv6 flows
|
||||
- MTU selected for userspace packet handling
|
||||
- default IPv4 route when full-tunnel is enabled
|
||||
- default IPv6 route when the platform tunnel surface supports it
|
||||
- a daemon-owned DNS server address reachable inside the tunnel
|
||||
|
||||
DNS handling should be explicit. Proxy-subscription tunnel configuration should use the daemon-owned DNS address inside the tunnel, not ambient physical resolvers and not a public upstream that requires outbound UDP before TCP can start. The daemon DNS path may answer with fake IPs and must keep those mappings scoped to the active proxy runtime so hostname-preserving proxy dialing does not leak between networks.
|
||||
|
||||
On Apple, the app-facing daemon and packet-tunnel daemon are intentionally separate in-process daemon instances over different app-group Unix sockets. The app-facing daemon handles subscription preview, import, refresh, node selection, network list, and account/login control through `burrow.sock`. The Network Extension hosts the packet-tunnel daemon over `burrow-packet-tunnel.sock` while the VPN session is active; `TunnelStart`, `TunnelConfiguration`, `TunnelPackets`, and `TunnelStop` for the active session use that socket.
|
||||
|
||||
This split keeps macOS packet runtime ownership aligned with `NEPacketTunnelProvider`: routes, DNS settings, packet stream lifetime, and proxy runtime are created from the same selected network state when the extension starts. UI mutations persist desired state in the shared app-group database. If a mutation changes active packet-tunnel behavior, the Apple client should update the packet daemon's desired state and only restart or reassert the Network Extension when the platform tunnel settings themselves change.
|
||||
|
||||
Proxy-subscription routing must avoid recursively routing the proxy outbound through the full tunnel. Burrow uses daemon-side outbound socket binding, analogous to Mihomo's default-interface outbound behavior, and installs resolved proxy-server excluded routes as a second guardrail. Selector changes remain daemon-local when they do not require platform route changes; if the selected node's resolved proxy-server routes change, the Network Extension should be restarted or have its settings reapplied so the excluded routes match the active outbound.
|
||||
|
||||
Selector changes should be daemon-local whenever socket binding is available. The Network Extension packet stream and route/DNS settings should stay up; the packet daemon updates its proxy outbound handle, and future flows read the new selection from that shared handle. This mirrors Mihomo's selector model more closely than restarting TUN for every node change.
|
||||
|
||||
Daemon-owned DNS/fake-IP metadata is part of the proxy packet runtime. The proxy-server route exclusion protects the selected outbound itself; ordinary website DNS is answered by the daemon and converted into hostname metadata for future flows. Mihomo-style rule selection can build on the same mapping without depending on ambient system DNS after the tunnel is active.
|
||||
|
||||
### Rollout Stages
|
||||
|
||||
1. Proposal and model cleanup.
|
||||
- Supersede `BEP-0009`.
|
||||
- Remove SOCKS runtime from the target design.
|
||||
2. Parser and preview.
|
||||
- Implement daemon-owned import preview for Mihomo-compatible YAML and URI-list inputs.
|
||||
- Add fixtures for Trojan and Shadowsocks.
|
||||
3. Storage and refresh.
|
||||
- Store proxy subscriptions as networks.
|
||||
- Preserve selected node and local ordering across refreshes.
|
||||
4. Packet engine foundation.
|
||||
- Add daemon packet-stream support for proxy networks.
|
||||
- Reconstruct TCP flows first with controlled DNS behavior.
|
||||
5. Protocol outbounds.
|
||||
- Add Trojan outbound.
|
||||
- Add Shadowsocks outbound.
|
||||
- Add UDP once NAT, timeout, and protocol support are explicit.
|
||||
6. Platform UI.
|
||||
- Add `Import Proxy Subscription` to Apple SwiftUI and Linux GTK add flows.
|
||||
- Add preview, warning, selection, refresh, and node status views to Apple SwiftUI, Linux GTK, and any future first-party UI surface.
|
||||
7. Compatibility and operations.
|
||||
- Add Mihomo comparison fixtures and sanitized live-check scripts.
|
||||
- Document remaining incompatibilities.
|
||||
|
||||
## Security and Operational Considerations
|
||||
|
||||
- Subscription URLs frequently contain bearer tokens. Full URLs must not appear in logs, screenshots, fixtures, crash reports, or generated docs.
|
||||
- Proxy node credentials must be redacted in all errors and diagnostics.
|
||||
- Insecure TLS settings imported from subscriptions must be visible in preview and stored as explicit risk metadata.
|
||||
- Refresh must have timeout, redirect, response-size, and content-type limits.
|
||||
- Parser failures must be non-fatal per node; malformed input should never panic the daemon.
|
||||
- Packet-tunnel activation must have a kill-switch path: when the tunnel stops or a proxy network is disabled, packet handling, DNS state, NAT state, and outbound sockets must be torn down.
|
||||
- If DNS mapping is fake-IP based, stale mappings must expire and must not leak between networks in ways that route traffic through the wrong node.
|
||||
- Full-tunnel mode must avoid recursive routing of the proxy server connection through the tunnel. On Apple, preferred bypass behavior is outbound socket binding to a non-tunnel interface. Route exclusions are fallback behavior for platforms or environments where socket binding is not available.
|
||||
- Removing SOCKS mode simplifies the threat model by leaving one supported runtime behavior for proxy networks.
|
||||
|
||||
## Contributor Playbook
|
||||
|
||||
1. Keep all UI subscription operations behind daemon gRPC.
|
||||
2. Update or remove current Trojan SOCKS runtime code instead of layering the packet-tunnel runtime beside it.
|
||||
3. Add parser fixtures for:
|
||||
- Clash/Mihomo YAML with `proxies`
|
||||
- proxy-provider content
|
||||
- base64 URI list
|
||||
- plaintext URI list
|
||||
- Trojan TCP, ws, grpc metadata
|
||||
- Shadowsocks legacy and SIP002-style links
|
||||
- unsupported protocols and malformed nodes
|
||||
- Mihomo parity cases copied from the behavior of `common/convert/converter.go`, especially Trojan `allowInsecure`, `sni`, `alpn`, `type=ws`, `type=grpc`, `fp`, and `pcs`
|
||||
4. Add packet-engine tests with synthetic TCP and DNS packets before full platform UI integration.
|
||||
5. Add a migration or cleanup path for any existing stored `NetworkType::Trojan` payloads created by the SOCKS-era implementation.
|
||||
6. Validate metadata and affected builds:
|
||||
|
||||
```bash
|
||||
uv run python Scripts/check-bep-metadata.py
|
||||
cargo test -p burrow proxy_subscription
|
||||
cargo check -p burrow
|
||||
```
|
||||
|
||||
7. For platform UI work, regenerate bindings after proto changes and verify Apple SwiftUI and Linux GTK still talk only to daemon gRPC for subscription operations.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Keep local SOCKS mode. Rejected because it is not Burrow's VPN behavior, adds another support surface, and makes imported proxy subscriptions feel unlike WireGuard and Tailnet.
|
||||
- Use system `HTTP_PROXY` or SOCKS settings. Rejected because only proxy-aware apps participate, DNS behavior is inconsistent, and cleanup/rollback becomes OS-setting specific.
|
||||
- Shell out to Mihomo. Rejected as the default implementation because Burrow would inherit a large external runtime and a separate control plane. Mihomo remains the behavioral reference for import and TUN semantics.
|
||||
- Claim full Mihomo compatibility immediately. Rejected because Burrow should first support Trojan and Shadowsocks imports plus packet-tunnel routing correctly.
|
||||
- Treat every proxy node as a separate account. Rejected because nodes are endpoints under one imported network source, not identities.
|
||||
|
||||
## Impact on Other Work
|
||||
|
||||
- Supersedes `BEP-0009`.
|
||||
- Depends on the daemon IPC boundary in `BEP-0005`.
|
||||
- Aligns with the route and policy direction in `BEP-0003`.
|
||||
- May require a follow-up secret-storage BEP if existing network payload storage remains insufficient for long-lived proxy credentials.
|
||||
- Creates shared import and packet-engine foundations for future proxy protocols, but this proposal only covers Trojan and Shadowsocks.
|
||||
|
||||
## Decision
|
||||
|
||||
Pending.
|
||||
|
||||
## References
|
||||
|
||||
- Burrow daemon IPC boundary: `evolution/proposals/BEP-0005-daemon-ipc-and-apple-boundary.md`
|
||||
- Burrow route and negotiation roadmap: `evolution/proposals/BEP-0003-connect-ip-and-negotiation-roadmap.md`
|
||||
- Superseded Trojan SOCKS proposal: `evolution/proposals/BEP-0009-trojan-proxy-and-subscription-import.md`
|
||||
- Burrow tunnel schema: `proto/burrow.proto`
|
||||
- Burrow Apple tunnel provider: `Apple/NetworkExtension/PacketTunnelProvider.swift`
|
||||
- Burrow Apple UI: `Apple/UI/`
|
||||
- Burrow GTK UI: `burrow-gtk/src/`
|
||||
- Burrow current Trojan parser/runtime: `burrow/src/trojan.rs`
|
||||
- Burrow daemon runtime: `burrow/src/daemon/runtime.rs`
|
||||
- Mihomo config model: `config/config.go`
|
||||
- Mihomo proxy provider parsing: `adapter/provider/parser.go`
|
||||
- Mihomo provider content parsing: `adapter/provider/provider.go`
|
||||
- Mihomo URI conversion: `common/convert/converter.go`
|
||||
- Mihomo base64 fallback: `common/convert/base64.go`
|
||||
- Mihomo TUN listener lifecycle: `listener/listener.go`
|
||||
- Mihomo TUN stack setup: `listener/sing_tun/server.go`
|
||||
- Mihomo DNS hijack: `listener/sing_tun/dns.go`
|
||||
- Mihomo listener metadata bridge: `listener/sing/sing.go`
|
||||
- Mihomo TCP/UDP tunnel handling: `tunnel/tunnel.go`
|
||||
- Mihomo outbound parser: `adapter/parser.go`
|
||||
- Mihomo selector group: `adapter/outboundgroup/selector.go`
|
||||
- Mihomo Trojan outbound: `adapter/outbound/trojan.go`
|
||||
- Mihomo Shadowsocks outbound: `adapter/outbound/shadowsocks.go`
|
||||
110
evolution/proposals/BEP-0011-mihomo-compatible-trojan-runtime.md
Normal file
110
evolution/proposals/BEP-0011-mihomo-compatible-trojan-runtime.md
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
# `BEP-0011` - Mihomo-Compatible Trojan Runtime
|
||||
|
||||
```text
|
||||
Status: Draft
|
||||
Proposal: BEP-0011
|
||||
Authors: Codex
|
||||
Coordinator: Jett Chen
|
||||
Reviewers: Pending
|
||||
Constitution Sections: 2, 4, 5
|
||||
Implementation PRs: Pending
|
||||
Decision Date: Pending
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
Burrow's proxy subscription runtime should match Mihomo's Trojan wire behavior
|
||||
for imported nodes wherever Burrow already claims runtime support. This proposal
|
||||
covers TCP Trojan nodes in the packet tunnel runtime: ALPN defaults, DNS-backed
|
||||
hostname preservation, UDP gating, UDP packet fragmentation, certificate
|
||||
pinning, and diagnostics for Mihomo-only TLS fingerprint behavior.
|
||||
|
||||
The goal is compatibility without overstating support. Burrow should apply
|
||||
Mihomo semantics that can be implemented correctly today, and it should make
|
||||
unsupported Mihomo features visible instead of silently pretending to support
|
||||
them.
|
||||
|
||||
## Motivation
|
||||
|
||||
- Imported Trojan subscriptions commonly target Mihomo-compatible clients.
|
||||
- A stored node can carry `client-fingerprint`, ALPN, UDP, and certificate
|
||||
pinning metadata that materially changes whether the proxy server accepts the
|
||||
connection.
|
||||
- Burrow previously flattened absent ALPN and explicit empty ALPN into the same
|
||||
value, ignored Trojan UDP enablement, rejected oversized Trojan UDP payloads
|
||||
instead of fragmenting them, and stored certificate pins without enforcing
|
||||
them.
|
||||
|
||||
## Detailed Design
|
||||
|
||||
- Store an `alpn_present` marker alongside the existing Trojan `alpn` vector.
|
||||
Missing ALPN uses Mihomo's TCP Trojan default `h2,http/1.1`; explicit ALPN,
|
||||
including an explicit empty YAML list, is honored as written.
|
||||
- Store Trojan `udp`. URI imports default to enabled to match Mihomo's URI
|
||||
converter. YAML imports default to disabled unless `udp` is present.
|
||||
- Point proxy-network DNS at the daemon-owned tunnel address. The packet runtime
|
||||
answers A queries with fake IPs, records fake-IP to hostname mappings, and
|
||||
uses hostname-form SOCKS addresses in Trojan TCP requests when a later TCP
|
||||
flow targets one of those fake IPs.
|
||||
- Enforce Trojan UDP gating at runtime and fragment UDP payloads above 8192
|
||||
bytes into multiple Trojan UDP frames.
|
||||
- Treat `fingerprint` as SHA-256 certificate pinning. Browser fingerprint names
|
||||
are rejected in `fingerprint` with guidance to use `client-fingerprint`.
|
||||
- Keep using rustls for non-Apple TLS and OpenSSL for the macOS Trojan TLS path.
|
||||
`client-fingerprint` is logged when present because Mihomo's uTLS ClientHello
|
||||
impersonation is not implemented in Burrow yet.
|
||||
- Coalesce the Trojan request header into one TLS write, matching Mihomo's
|
||||
`trojan.WriteHeader` behavior.
|
||||
- Continue rejecting Trojan `ws` and `grpc` at runtime until those transports
|
||||
are implemented with their complete TLS and framing behavior.
|
||||
|
||||
## Security and Operational Considerations
|
||||
|
||||
- Certificate pinning changes TLS verification semantics. When a pin is
|
||||
configured, Burrow verifies the presented chain against the pin instead of the
|
||||
WebPKI root store, matching Mihomo's pinning model.
|
||||
- The runtime must not log proxy passwords or subscription tokens.
|
||||
- uTLS/browser fingerprinting should not be faked with partial ClientHello
|
||||
tweaks. Until Burrow has a complete implementation, the runtime should say
|
||||
that it is using platform TLS rather than a Mihomo uTLS impersonation.
|
||||
- Rollback is a code rollback plus re-importing any subscriptions whose stored
|
||||
JSON schema has changed during testing.
|
||||
|
||||
## Contributor Playbook
|
||||
|
||||
1. Compare Burrow behavior against `/Users/jettchen/dev/vpn-ref/mihomo` before
|
||||
changing protocol details.
|
||||
2. Run `cargo fmt -p burrow`.
|
||||
3. Run `cargo test -p burrow proxy_subscription::tests::`.
|
||||
4. Run `cargo test -p burrow proxy_runtime::tests::`.
|
||||
5. Run `cargo check -p burrow`.
|
||||
6. For live validation, use a Trojan node with omitted ALPN and
|
||||
`client-fingerprint: chrome`; confirm the logs show Mihomo default ALPN, the
|
||||
physical-interface bind, the macOS OpenSSL handshake, and the platform TLS
|
||||
fingerprint warning.
|
||||
|
||||
## Alternatives Considered
|
||||
|
||||
- Implement full uTLS immediately. Rejected for this change because rustls does
|
||||
not expose enough ClientHello control, and adding a second TLS stack needs
|
||||
separate review for Apple builds and static library consumers.
|
||||
- Treat all empty ALPN vectors as explicit empty ALPN. Rejected because older
|
||||
Burrow imports already stored omitted URI ALPN as `[]`, which would preserve
|
||||
the compatibility bug.
|
||||
- Ignore `fingerprint` until uTLS exists. Rejected because certificate pinning
|
||||
is independent of browser ClientHello impersonation.
|
||||
|
||||
## Impact on Other Work
|
||||
|
||||
- BEP-0010 remains the packet tunnel direction for proxy subscriptions.
|
||||
- A future BEP or revision should cover uTLS/browser fingerprint support,
|
||||
websocket Trojan, gRPC Trojan, and daemon-owned proxy-server DNS resolution.
|
||||
|
||||
## Decision
|
||||
|
||||
Pending review.
|
||||
|
||||
## References
|
||||
|
||||
- `docs/trojan-mihomo-discrepancies.md`
|
||||
- `evolution/proposals/BEP-0010-proxy-subscriptions-as-packet-tunnel-networks.md`
|
||||
Loading…
Add table
Add a link
Reference in a new issue