fix(amneziawg): enable sniffing on the TPROXY bridge

Domain-based Routing rules could never match RouteThroughXray traffic: an
AmneziaWG peer resolves DNS itself, through the tunnel, before ever sending
a packet, so the decapsulated traffic TPROXY hands to the bridge is already
a bare destination IP with no domain name attached at the network layer.
Every other inbound recovers this via sniffing (confirmed working for the
stock wireguard inbound, which does have it configured); the bridge never
got a sniffing block at all, so only tag/IP/network-based rules could ever
match it -- any domain rule above it in the list was silently unreachable.
This commit is contained in:
Kuzz007
2026-07-28 00:27:45 +03:00
parent 5b58369af2
commit bb6a874dc5
2 changed files with 17 additions and 0 deletions
+14
View File
@@ -645,6 +645,19 @@ const amneziawgEgressDokodemoSettings = `{"allowedNetwork":"tcp,udp","followRedi
// socket.
const amneziawgEgressStreamSettings = `{"sockopt":{"tproxy":"tproxy"}}`
// amneziawgEgressSniffingSettings enables sniffing on the bridge, matching
// this fork's own normal per-inbound default (see default.json's "mixed"
// inbound). Without this, domain-based Routing rules can never match a
// single byte of RouteThroughXray traffic: an AmneziaWG peer resolves DNS
// itself, through the tunnel, before ever sending a packet — by the time
// TPROXY hands the decapsulated traffic to this bridge, the destination is
// already a bare IP, with no domain name attached at the network layer at
// all. Sniffing recovers it from the payload itself (TLS SNI / HTTP Host /
// QUIC) the same way it already does for every other inbound; without it,
// only tag/IP/network-based rules can ever match this bridge's traffic,
// and any domain rule above it in the list is silently unreachable.
const amneziawgEgressSniffingSettings = `{"enabled":true,"destOverride":["http","tls","quic","fakedns"]}`
// injectAmneziawgEgress gives every enabled, RouteThroughXray-opted-in
// AmneziaWG inbound with at least one qualifying peer its own loopback
// dokodemo-door bridge — tagged with that inbound's own real tag, so it's
@@ -707,6 +720,7 @@ func injectAmneziawgEgress(cfg *xray.Config, inbounds []*model.Inbound) {
Protocol: "dokodemo-door",
Settings: json_util.RawMessage(amneziawgEgressDokodemoSettings),
StreamSettings: json_util.RawMessage(amneziawgEgressStreamSettings),
Sniffing: json_util.RawMessage(amneziawgEgressSniffingSettings),
Tag: inbound.Tag,
})
}
@@ -590,6 +590,9 @@ func TestInjectAmneziawgEgress_CreatesBridgeTaggedWithInboundsOwnTag(t *testing.
if !strings.Contains(string(ib.Settings), `"followRedirect":true`) {
t.Fatalf("bridge must set followRedirect, got %s", ib.Settings)
}
if !strings.Contains(string(ib.Sniffing), `"enabled":true`) {
t.Fatalf("bridge must enable sniffing -- a peer's own DNS resolution means the decapsulated traffic never carries a domain at the network layer, so domain-based Routing rules can only ever match via sniffing the payload, got %s", ib.Sniffing)
}
// No auto-generated routing rule: it's entirely up to the admin's own
// Routing-page rules, same as any other protocol's inbound tag.
if string(cfg.RouterConfig) != before {