mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-08-20 10:00:58 +00:00
fix: preserve TLS cert file paths when deploying inbound to remote node
When creating a Hysteria (or any TLS-required) inbound from the central panel and deploying it to a remote node, sanitizeStreamSettingsForRemote was unconditionally stripping certificateFile / keyFile from the TLS settings. This left Xray on the remote node with a TLS block containing no certificate, causing Xray to crash and the inbounds page to hang. The fix: only strip cert file paths when inline certificate content (certificate / key arrays) is also present in the same entry — those file paths are then truly redundant. When only file paths are present the user explicitly entered paths that live on the remote node's filesystem; they are now passed through untouched. Fixes #4370
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
package runtime
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSanitizeStreamSettingsForRemote(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
// wantCertFile / wantKeyFile: expected presence after sanitize
|
||||
wantCertFile bool
|
||||
wantKeyFile bool
|
||||
}{
|
||||
{
|
||||
name: "file paths only — kept intact (remote node paths)",
|
||||
input: `{
|
||||
"tlsSettings": {
|
||||
"certificates": [{
|
||||
"certificateFile": "/etc/ssl/cert.crt",
|
||||
"keyFile": "/etc/ssl/key.key"
|
||||
}]
|
||||
}
|
||||
}`,
|
||||
wantCertFile: true,
|
||||
wantKeyFile: true,
|
||||
},
|
||||
{
|
||||
name: "inline content only — unchanged",
|
||||
input: `{
|
||||
"tlsSettings": {
|
||||
"certificates": [{
|
||||
"certificate": ["-----BEGIN CERTIFICATE-----"],
|
||||
"key": ["-----BEGIN PRIVATE KEY-----"]
|
||||
}]
|
||||
}
|
||||
}`,
|
||||
wantCertFile: false,
|
||||
wantKeyFile: false,
|
||||
},
|
||||
{
|
||||
name: "both file paths and inline content — file paths stripped (redundant)",
|
||||
input: `{
|
||||
"tlsSettings": {
|
||||
"certificates": [{
|
||||
"certificateFile": "/etc/ssl/cert.crt",
|
||||
"keyFile": "/etc/ssl/key.key",
|
||||
"certificate": ["-----BEGIN CERTIFICATE-----"],
|
||||
"key": ["-----BEGIN PRIVATE KEY-----"]
|
||||
}]
|
||||
}
|
||||
}`,
|
||||
wantCertFile: false,
|
||||
wantKeyFile: false,
|
||||
},
|
||||
{
|
||||
name: "empty stream settings",
|
||||
input: "",
|
||||
// empty input returns empty, nothing to check
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
if tc.input == "" {
|
||||
if got := sanitizeStreamSettingsForRemote(tc.input); got != "" {
|
||||
t.Errorf("expected empty string, got %q", got)
|
||||
}
|
||||
return
|
||||
}
|
||||
got := sanitizeStreamSettingsForRemote(tc.input)
|
||||
var out map[string]any
|
||||
if err := json.Unmarshal([]byte(got), &out); err != nil {
|
||||
t.Fatalf("output is not valid JSON: %v\noutput: %s", err, got)
|
||||
}
|
||||
|
||||
tls, _ := out["tlsSettings"].(map[string]any)
|
||||
certs, _ := tls["certificates"].([]any)
|
||||
if len(certs) == 0 {
|
||||
t.Fatal("certificates array missing in output")
|
||||
}
|
||||
cert, _ := certs[0].(map[string]any)
|
||||
|
||||
_, hasCertFile := cert["certificateFile"]
|
||||
_, hasKeyFile := cert["keyFile"]
|
||||
|
||||
if hasCertFile != tc.wantCertFile {
|
||||
t.Errorf("certificateFile present=%v, want %v", hasCertFile, tc.wantCertFile)
|
||||
}
|
||||
if hasKeyFile != tc.wantKeyFile {
|
||||
t.Errorf("keyFile present=%v, want %v", hasKeyFile, tc.wantKeyFile)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user