mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-09-16 15:17:14 +00:00
fix(link): read the vmess certificate checks on import (#6507)
* fix(link): read the vmess certificate checks on import applyVmessTLSParams writes ech, vcn and pcs into the vmess share object, but parseVmess only read sni, fp and alpn back. Importing a link the panel had just exported therefore dropped all three: no pinned certificate, no verify-by-name, no ECH. On a server whose certificate is only trusted through a pin, the imported outbound falls back to public-CA verification against the system roots and cannot connect to the inbound the link came from. The url-param protocols already read the same three in applySecurity, and the core takes pinnedPeerCertSha256 as one joined string there, so the vmess path now fills them the same way. * fix(frontend): read the vmess certificate checks on import The panel parses share links twice: link.ParseLink in Go and parseVmessLink in outbound-link-parser.ts, which is what the Add Outbound button calls. Reading ech, vcn and pcs in Go alone would have made the two sides disagree on one link, leaving the UI path — the one an operator uses by hand — still dropping the pin the panel had just exported.
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
package sub
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/database/model"
|
||||
"github.com/mhsanaei/3x-ui/v3/internal/util/link"
|
||||
)
|
||||
|
||||
// The vmess object carries the certificate checks the panel exported, so
|
||||
// importing that same link has to rebuild them instead of dropping them.
|
||||
func TestVmessTLSVerifyFieldsSurviveExportImport(t *testing.T) {
|
||||
in := &model.Inbound{
|
||||
Id: 940002, Listen: "203.0.113.1", Port: 8443, Protocol: model.VMESS,
|
||||
Settings: `{"clients":[{"id":"11111111-2222-4333-8444-555555555555","email":"user"}]}`,
|
||||
StreamSettings: `{"network":"tcp","security":"tls","tcpSettings":{"header":{"type":"none"}},` +
|
||||
`"tlsSettings":{"serverName":"vmess.example.com","alpn":["h2"],"settings":{` +
|
||||
`"fingerprint":"chrome","echConfigList":"AEX+DQBB","verifyPeerCertByName":"vcn.example.com",` +
|
||||
`"pinnedPeerCertSha256":["AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA="]}}}`,
|
||||
}
|
||||
exported := (&SubService{}).genVmessLink(in, "user")
|
||||
if exported == "" {
|
||||
t.Fatal("genVmessLink produced nothing")
|
||||
}
|
||||
|
||||
parsed, err := link.ParseLink(exported)
|
||||
if err != nil {
|
||||
t.Fatalf("ParseLink: %v", err)
|
||||
}
|
||||
raw, err := json.Marshal(parsed.Outbound["streamSettings"])
|
||||
if err != nil {
|
||||
t.Fatalf("marshal stream: %v", err)
|
||||
}
|
||||
var stream map[string]any
|
||||
if err := json.Unmarshal(raw, &stream); err != nil {
|
||||
t.Fatalf("stream json: %v", err)
|
||||
}
|
||||
tlsSettings, _ := stream["tlsSettings"].(map[string]any)
|
||||
if tlsSettings == nil {
|
||||
t.Fatalf("no tlsSettings: %s", raw)
|
||||
}
|
||||
|
||||
// The core reads these joined strings in its config, as applySecurity writes them.
|
||||
for field, want := range map[string]string{
|
||||
"serverName": "vmess.example.com",
|
||||
"fingerprint": "chrome",
|
||||
"echConfigList": "AEX+DQBB",
|
||||
"verifyPeerCertByName": "vcn.example.com",
|
||||
"pinnedPeerCertSha256": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
|
||||
} {
|
||||
if tlsSettings[field] != want {
|
||||
t.Errorf("tlsSettings[%q] = %v, want %q", field, tlsSettings[field], want)
|
||||
}
|
||||
}
|
||||
alpn, _ := tlsSettings["alpn"].([]any)
|
||||
if len(alpn) != 1 || alpn[0] != "h2" {
|
||||
t.Errorf("alpn = %v, want [h2]", tlsSettings["alpn"])
|
||||
}
|
||||
}
|
||||
@@ -205,6 +205,11 @@ func parseVmess(link string) (*ParseResult, error) {
|
||||
if alpn := getString(j, "alpn", ""); alpn != "" {
|
||||
tls["alpn"] = splitComma(alpn)
|
||||
}
|
||||
// The vmess object names the certificate checks v2rayN does the same way
|
||||
// the url-param protocols name them in applySecurity.
|
||||
tls["echConfigList"] = getString(j, "ech", "")
|
||||
tls["verifyPeerCertByName"] = getString(j, "vcn", "")
|
||||
tls["pinnedPeerCertSha256"] = getString(j, "pcs", "")
|
||||
}
|
||||
|
||||
port := num(j["port"])
|
||||
|
||||
Reference in New Issue
Block a user