feat(inbound): Advanced XHTTP and external TLS proxy settings (#4491)

*  Introduce extended XHTTP and external proxy settings

*  Add custom SNI for proxy

*  Add previous changes into React version of app

* fix(sub): isolate per-proxy tlsSettings during external-proxy iteration

cloneMap (Clash) is shallow and `newStream := stream` (JSON) is an alias,
so tlsSettings was shared across iterations. The new applyExternalProxyTLSToStream
mutates it, leaking one proxy's serverName/fingerprint/alpn into the next
(only overwritten when the next proxy explicitly sets the same field).

Add cloneStreamForExternalProxy: shallow clones the top-level stream plus
deep clones tlsSettings and tlsSettings.settings. Regression test locks
in that proxy B does not inherit proxy A's fingerprint/alpn when B leaves
them unset.
This commit is contained in:
Maksim Alekseev
2026-05-24 22:54:26 +03:00
committed by GitHub
parent cfe1b25ca0
commit 1f90d2a6ee
9 changed files with 553 additions and 224 deletions
+7 -2
View File
@@ -174,7 +174,8 @@ func (s *SubJsonService) getConfig(inbound *model.Inbound, client model.Client,
}
externalProxies, ok := stream["externalProxy"].([]any)
if !ok || len(externalProxies) == 0 {
hasExternalProxy := ok && len(externalProxies) > 0
if !hasExternalProxy {
externalProxies = []any{
map[string]any{
"forceTls": "same",
@@ -191,7 +192,7 @@ func (s *SubJsonService) getConfig(inbound *model.Inbound, client model.Client,
extPrxy := ep.(map[string]any)
inbound.Listen = extPrxy["dest"].(string)
inbound.Port = int(extPrxy["port"].(float64))
newStream := stream
newStream := cloneStreamForExternalProxy(stream)
switch extPrxy["forceTls"].(string) {
case "tls":
if newStream["security"] != "tls" {
@@ -204,6 +205,10 @@ func (s *SubJsonService) getConfig(inbound *model.Inbound, client model.Client,
delete(newStream, "tlsSettings")
}
}
security, _ := newStream["security"].(string)
if hasExternalProxy {
applyExternalProxyTLSToStream(extPrxy, newStream, security)
}
streamSettings, _ := json.MarshalIndent(newStream, "", " ")
var newOutbounds []json_util.RawMessage