From ad81649c169543c99e343e19e8a47a589439e3e6 Mon Sep 17 00:00:00 2001 From: Abdalrahman Date: Wed, 13 May 2026 15:47:09 +0300 Subject: [PATCH] fix: strip main-panel TLS cert file paths when sending inbound to remote node (#4339) When the main panel creates an inbound assigned to a remote node, the wireInbound helper sends StreamSettings as-is, including certificateFile/keyFile paths that only exist on the main panel's filesystem. The remote node's Xray then fails to load them and crashes. This adds sanitizeStreamSettingsForRemote() which strips file-based cert paths before forwarding to a remote node. Inline certificate content (certificate/key) is preserved unchanged. Closes #4335 --- web/runtime/remote.go | 43 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/web/runtime/remote.go b/web/runtime/remote.go index 184e632e..c71714e7 100644 --- a/web/runtime/remote.go +++ b/web/runtime/remote.go @@ -334,7 +334,7 @@ func wireInbound(ib *model.Inbound) url.Values { v.Set("port", strconv.Itoa(ib.Port)) v.Set("protocol", string(ib.Protocol)) v.Set("settings", ib.Settings) - v.Set("streamSettings", ib.StreamSettings) + v.Set("streamSettings", sanitizeStreamSettingsForRemote(ib.StreamSettings)) v.Set("tag", ib.Tag) v.Set("sniffing", ib.Sniffing) if ib.TrafficReset != "" { @@ -342,3 +342,44 @@ func wireInbound(ib *model.Inbound) url.Values { } return v } + +// sanitizeStreamSettingsForRemote strips file-based TLS certificate paths +// from the StreamSettings before sending to a remote node. File paths +// (certificateFile / keyFile) are local to the main panel's filesystem +// and will cause Xray on the remote node to crash if they don't exist there. +// Inline certificate content (certificate / key) is kept intact. +func sanitizeStreamSettingsForRemote(streamSettings string) string { + if streamSettings == "" { + return streamSettings + } + + var stream map[string]any + if err := json.Unmarshal([]byte(streamSettings), &stream); err != nil { + return streamSettings + } + + tlsSettings, ok := stream["tlsSettings"].(map[string]any) + if !ok { + return streamSettings + } + + certificates, ok := tlsSettings["certificates"].([]any) + if !ok { + return streamSettings + } + + for _, cert := range certificates { + c, ok := cert.(map[string]any) + if !ok { + continue + } + delete(c, "certificateFile") + delete(c, "keyFile") + } + + out, err := json.Marshal(stream) + if err != nil { + return streamSettings + } + return string(out) +}