fix(xray-config): strip panel-only fields from inbound config

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.
This commit is contained in:
MHSanaei 2026-05-27 22:00:47 +02:00
parent a9b8458bde
commit d9ac8f0618
No known key found for this signature in database
GPG key ID: 7E4060F2FBE5AB7A

View file

@ -220,10 +220,19 @@ func (i *Inbound) GenXrayInboundConfig() *xray.InboundConfig {
listen = fmt.Sprintf("\"%v\"", listen) listen = fmt.Sprintf("\"%v\"", listen)
protocol := string(i.Protocol) protocol := string(i.Protocol)
settings := i.Settings settings := i.Settings
if i.Protocol == Shadowsocks { switch i.Protocol {
case Shadowsocks:
if healed, ok := HealShadowsocksClientMethods(settings); ok { if healed, ok := HealShadowsocksClientMethods(settings); ok {
settings = healed 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{ return &xray.InboundConfig{
Listen: json_util.RawMessage(listen), 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 // HealShadowsocksClientMethods normalises the per-client `method` field
// on a shadowsocks inbound's settings JSON before it leaves for xray-core: // on a shadowsocks inbound's settings JSON before it leaves for xray-core:
// - Legacy ciphers (aes-*, chacha20-*): every client must carry a // - Legacy ciphers (aes-*, chacha20-*): every client must carry a