From d9ac8f061845e57cd9b0501215804f3d0874c967 Mon Sep 17 00:00:00 2001 From: MHSanaei Date: Wed, 27 May 2026 22:00:47 +0200 Subject: [PATCH] fix(xray-config): strip panel-only fields from inbound config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fields the panel stores but Xray doesn't accept on the inbound side: - VMess clients[].security — panel persists it so the share-link generator can write `scy=...`, but xray's vmess inbound spec has no per-client security. The field was leaking into the inbound JSON pushed to xray-core. - VLESS settings.encryption — per the xray spec the inbound only takes `decryption`; `encryption` is for the matching client outbound. The panel keeps it for operator reference, but it must not appear in the inbound payload. Add two strip helpers next to HealShadowsocksClientMethods and wire them into GenXrayInboundConfig via a per-protocol switch, so both local and remote runtime paths get the cleaned config. --- database/model/model.go | 64 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/database/model/model.go b/database/model/model.go index 411b74c2..f9e333df 100644 --- a/database/model/model.go +++ b/database/model/model.go @@ -220,10 +220,19 @@ func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig { listen = fmt.Sprintf("\"%v\"", listen) protocol := string(i.Protocol) settings := i.Settings - if i.Protocol == Shadowsocks { + switch i.Protocol { + case Shadowsocks: if healed, ok := HealShadowsocksClientMethods(settings); ok { settings = healed } + case VMESS: + if stripped, ok := StripVmessClientSecurity(settings); ok { + settings = stripped + } + case VLESS: + if stripped, ok := StripVlessInboundEncryption(settings); ok { + settings = stripped + } } return &xray.InboundConfig{ Listen: json_util.RawMessage(listen), @@ -236,6 +245,59 @@ func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig { } } +func StripVmessClientSecurity(settings string) (string, bool) { + if settings == "" { + return settings, false + } + var parsed map[string]any + if err := json.Unmarshal([]byte(settings), &parsed); err != nil { + return settings, false + } + clients, ok := parsed["clients"].([]any) + if !ok { + return settings, false + } + changed := false + for i := range clients { + cm, ok := clients[i].(map[string]any) + if !ok { + continue + } + if _, has := cm["security"]; has { + delete(cm, "security") + clients[i] = cm + changed = true + } + } + if !changed { + return settings, false + } + out, err := json.MarshalIndent(parsed, "", " ") + if err != nil { + return settings, false + } + return string(out), true +} + +func StripVlessInboundEncryption(settings string) (string, bool) { + if settings == "" { + return settings, false + } + var parsed map[string]any + if err := json.Unmarshal([]byte(settings), &parsed); err != nil { + return settings, false + } + if _, has := parsed["encryption"]; !has { + return settings, false + } + delete(parsed, "encryption") + out, err := json.MarshalIndent(parsed, "", " ") + if err != nil { + return settings, false + } + return string(out), true +} + // HealShadowsocksClientMethods normalises the per-client `method` field // on a shadowsocks inbound's settings JSON before it leaves for xray-core: // - Legacy ciphers (aes-*, chacha20-*): every client must carry a