mirror of
https://github.com/MHSanaei/3x-ui.git
synced 2026-07-20 03:26:08 +00:00
feat(tls,reality): port xray TLS/REALITY fields, cert-hash helpers, fallback UX
TLS: add verifyPeerCertByName (vcn) to inbound settings + emit in both share-link generators (frontend + Go sub) and outbound parser; the allowInsecure replacement xray removed after 2026-06-01. Add server-side curvePreferences, masterKeyLog, echSockopt (passthrough + form) at tlsSettings top-level so they survive the panel-only settings strip. REALITY: add limitFallbackUpload/Download (afterBytes/bytesPerSec/burstBytesPerSec) with per-field tooltips, plus masterKeyLog. Verified field names/semantics against pinned xray v1.260327.1 (bytesPerSec=0 disables). Hosts: fix verify_peer_cert_by_name column bool->string (xray expects comma-separated names) with an idempotent, history-gate-free migration (SQLite typeof blank; Postgres ALTER once); emit vcn for hosts/external proxies. Server: add getCertHash (local cert DER SHA-256) and getRemoteCertHash (xray tls ping) endpoints + api-docs; wire pinned-cert field buttons. Drop the meaningless random-hash button. Xray UI: metrics endpoint (listen/tag) config in Basics; import/export for routing rules and outbounds. Fallbacks card: compact empty state, header-aligned actions, responsive labeled grid rows. i18n: add all new keys to every locale; drop unused generateRandomPin.
This commit is contained in:
@@ -80,6 +80,9 @@ func hostToExternalProxyMap(h *model.Host, defaultDest string, defaultPort int)
|
||||
if h.EchConfigList != "" {
|
||||
ep["echConfigList"] = h.EchConfigList
|
||||
}
|
||||
if h.VerifyPeerCertByName != "" {
|
||||
ep["verifyPeerCertByName"] = h.VerifyPeerCertByName
|
||||
}
|
||||
if h.AllowInsecure {
|
||||
ep["allowInsecure"] = true
|
||||
}
|
||||
|
||||
@@ -309,6 +309,9 @@ func (s *SubJsonService) tlsData(tData map[string]any) map[string]any {
|
||||
if ech, ok := tlsClientSettings["echConfigList"].(string); ok && ech != "" {
|
||||
tlsData["echConfigList"] = ech
|
||||
}
|
||||
if vcn, ok := verifyPeerCertByNameValue(tlsClientSettings); ok {
|
||||
tlsData["verifyPeerCertByName"] = vcn
|
||||
}
|
||||
// xray-core now parses pinnedPeerCertSha256 as a comma-separated string, not
|
||||
// an array; emit the joined form so v2ray clients can import the config (#5401).
|
||||
if pins, ok := pinnedSha256List(tlsClientSettings); ok {
|
||||
|
||||
@@ -813,6 +813,9 @@ func (s *SubService) genHysteriaLink(inbound *model.Inbound, email string) strin
|
||||
params["ech"] = ech
|
||||
}
|
||||
}
|
||||
if vcn, ok := verifyPeerCertByNameValue(tlsSettings); ok {
|
||||
params["vcn"] = vcn
|
||||
}
|
||||
if pins, ok := pinnedSha256List(tlsSettings); ok {
|
||||
for i, p := range pins {
|
||||
pins[i] = hysteriaPinHex(p)
|
||||
@@ -1120,6 +1123,9 @@ func applyShareTLSParams(stream map[string]any, params map[string]string) {
|
||||
params["ech"] = ech
|
||||
}
|
||||
}
|
||||
if vcn, ok := verifyPeerCertByNameValue(tlsSettings); ok {
|
||||
params["vcn"] = vcn
|
||||
}
|
||||
if pins, ok := pinnedSha256List(tlsSettings); ok {
|
||||
params["pcs"] = strings.Join(pins, ",")
|
||||
}
|
||||
@@ -1150,12 +1156,34 @@ func applyVmessTLSParams(stream map[string]any, obj map[string]any) {
|
||||
obj["ech"] = ech
|
||||
}
|
||||
}
|
||||
if vcn, ok := verifyPeerCertByNameValue(tlsSettings); ok {
|
||||
obj["vcn"] = vcn
|
||||
}
|
||||
if pins, ok := pinnedSha256List(tlsSettings); ok {
|
||||
obj["pcs"] = strings.Join(pins, ",")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// verifyPeerCertByNameValue extracts tlsSettings.settings.verifyPeerCertByName
|
||||
// (the v2rayN `vcn` param) as a trimmed string. Like pinnedPeerCertSha256 it is
|
||||
// panel-only and flows into share links so clients verify the server
|
||||
// certificate by this name — the replacement for the removed allowInsecure.
|
||||
func verifyPeerCertByNameValue(tlsClientSettings any) (string, bool) {
|
||||
raw, ok := searchKey(tlsClientSettings, "verifyPeerCertByName")
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
s, ok := raw.(string)
|
||||
if !ok {
|
||||
return "", false
|
||||
}
|
||||
if s = strings.TrimSpace(s); s == "" {
|
||||
return "", false
|
||||
}
|
||||
return s, true
|
||||
}
|
||||
|
||||
// pinnedSha256List extracts tlsSettings.settings.pinnedPeerCertSha256 as a
|
||||
// []string. The field is panel-only (stripped before the run-config reaches
|
||||
// xray-core via internal/web/service/xray.go) but flows into share links so clients
|
||||
@@ -1274,6 +1302,9 @@ func applyExternalProxyTLSObj(ep map[string]any, obj map[string]any, security st
|
||||
if pins, ok := externalProxyPins(ep["pinnedPeerCertSha256"]); ok {
|
||||
obj["pcs"] = joinAnyStrings(pins)
|
||||
}
|
||||
if vcn, ok := ep["verifyPeerCertByName"].(string); ok && vcn != "" {
|
||||
obj["vcn"] = vcn
|
||||
}
|
||||
if ech, ok := ep["echConfigList"].(string); ok && ech != "" {
|
||||
obj["ech"] = ech
|
||||
}
|
||||
@@ -1295,6 +1326,9 @@ func applyExternalProxyTLSParams(ep map[string]any, params map[string]string, se
|
||||
if pins, ok := externalProxyPins(ep["pinnedPeerCertSha256"]); ok {
|
||||
params["pcs"] = joinAnyStrings(pins)
|
||||
}
|
||||
if vcn, ok := ep["verifyPeerCertByName"].(string); ok && vcn != "" {
|
||||
params["vcn"] = vcn
|
||||
}
|
||||
if ech, ok := ep["echConfigList"].(string); ok && ech != "" {
|
||||
params["ech"] = ech
|
||||
}
|
||||
@@ -1378,6 +1412,14 @@ func applyExternalProxyTLSToStream(ep map[string]any, stream map[string]any, sec
|
||||
}
|
||||
settings["echConfigList"] = ech
|
||||
}
|
||||
if vcn, ok := ep["verifyPeerCertByName"].(string); ok && vcn != "" {
|
||||
settings, _ := tlsSettings["settings"].(map[string]any)
|
||||
if settings == nil {
|
||||
settings = map[string]any{}
|
||||
tlsSettings["settings"] = settings
|
||||
}
|
||||
settings["verifyPeerCertByName"] = vcn
|
||||
}
|
||||
if ai, ok := ep["allowInsecure"].(bool); ok && ai {
|
||||
settings, _ := tlsSettings["settings"].(map[string]any)
|
||||
if settings == nil {
|
||||
|
||||
Reference in New Issue
Block a user