mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-10-16 19:05:13 +03:00
fix(sub): emit host TLS verification at xray level in JSON subscription
The JSON subscription flattens tlsSettings into xray's client shape before the per-host loop, then applyExternalProxyTLSToStream writes the host's echConfigList, verifyPeerCertByName, pinnedPeerCertSha256 and allowInsecure into the panel-only tlsSettings.settings map. Xray ignores that map, so a host's ECH (and its pins / verify name) never reached the client and connections through the host failed. After the host overrides are applied, lift that map through the same writer tlsData uses for the inbound's own TLS: fields land at the top of tlsSettings, pins are joined into the comma string xray parses, and allowInsecure is dropped exactly as it is for the inbound (removed from xray). The Clash renderer still reads the nested map and is unchanged. The helper-level subtest that pinned the nested location as the "json" shape is replaced by an end-to-end GetJson test on a host-backed inbound. Closes #6743
This commit is contained in:
@@ -618,3 +618,49 @@ func TestSub_HostCipherSuitesJSON(t *testing.T) {
|
||||
t.Fatalf("a host with no cipher suites should inherit the inbound's:\n%s", out)
|
||||
}
|
||||
}
|
||||
|
||||
// Xray reads a client's TLS verification fields at the top of tlsSettings; a
|
||||
// nested "settings" map is panel-only shape and xray silently ignores it.
|
||||
func TestSub_HostTLSVerificationJSONAtXrayLevel(t *testing.T) {
|
||||
seedSubDB(t)
|
||||
ib := seedSubInbound(t, "s1", "ech", 4461, 1,
|
||||
`{"network":"xhttp","security":"tls","xhttpSettings":{"path":"/"},"tlsSettings":{"serverName":"base.sni","settings":{"fingerprint":"chrome"}}}`)
|
||||
seedHost(t, &model.Host{
|
||||
InboundId: ib.Id, SortOrder: 0, Remark: "ECH", Address: "ech.cdn.com", Port: 443, Security: "tls",
|
||||
EchConfigList: "cloudflare-ech.com+udp://1.1.1.1", VerifyPeerCertByName: "cert.example.com",
|
||||
PinnedPeerCertSha256: []string{"aa11", "bb22"}, AllowInsecure: true,
|
||||
})
|
||||
|
||||
out, _, err := NewSubJsonService("", "", "", "", NewSubService("")).GetJson("s1", "req.example.com", false)
|
||||
if err != nil {
|
||||
t.Fatalf("GetJson: %v", err)
|
||||
}
|
||||
var config map[string]any
|
||||
if err := json.Unmarshal([]byte(out), &config); err != nil {
|
||||
t.Fatalf("unmarshal JSON subscription: %v", err)
|
||||
}
|
||||
outbounds, _ := config["outbounds"].([]any)
|
||||
if len(outbounds) == 0 {
|
||||
t.Fatalf("JSON subscription has no outbounds: %s", out)
|
||||
}
|
||||
outbound, _ := outbounds[0].(map[string]any)
|
||||
stream, _ := outbound["streamSettings"].(map[string]any)
|
||||
tls, _ := stream["tlsSettings"].(map[string]any)
|
||||
want := map[string]any{
|
||||
"serverName": "base.sni",
|
||||
"fingerprint": "chrome",
|
||||
"echConfigList": "cloudflare-ech.com+udp://1.1.1.1",
|
||||
"verifyPeerCertByName": "cert.example.com",
|
||||
"pinnedPeerCertSha256": "aa11,bb22",
|
||||
}
|
||||
for key, value := range want {
|
||||
if tls[key] != value {
|
||||
t.Errorf("tlsSettings.%s = %#v, want %#v", key, tls[key], value)
|
||||
}
|
||||
}
|
||||
for _, key := range []string{"settings", "allowInsecure"} {
|
||||
if _, ok := tls[key]; ok {
|
||||
t.Errorf("tlsSettings.%s must not reach xray: %#v", key, tls)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user