feat(tls): add a dynamic DoH helper for echConfigList, with CDN guidance (#6869)

echConfigList already accepted Xray's dynamic form, target domain + DoH URL,
but nothing in the inbound or host forms explained or built it. A helper now
builds that value from presets or a custom domain and resolver, accepts only the
schemes Xray dials (https, h2c, udp; scheme case normalised), warns that a key
generated locally does not work behind a CDN, and asks before overwriting local
ECH keys. The helper's controls carry accessible labels, and the resolver list
holds only identified public resolvers.
This commit is contained in:
55575455
2026-10-24 12:07:11 +08:00
committed by GitHub
parent 41bafb3fd5
commit f43645656c
21 changed files with 1380 additions and 53 deletions
+30 -2
View File
@@ -613,9 +613,38 @@
"usageOption": "Usage Option",
"buildChain": "Build Chain",
"echKey": "ECH key",
"echConfig": "ECH config",
"echSockopt": "ECH Sockopt",
"echSockoptTip": "Socket options for the connection Xray uses to fetch the ECH config list (e.g. route the lookup through a dialerProxy outbound). Leave disabled to use defaults.",
"echCdnWarningTitle": "Locally generated ECH keys only work for direct connections",
"echCdnWarningBody": "A keypair made by \"Get New ECH Cert\" is only valid when clients connect straight to this VPS. If the inbound is proxied through Cloudflare CDN (VLESS + xhttp/WS behind the orange cloud), Cloudflare terminates the outer TLS with its own ECH keys and rotates them, so a local key always fails the handshake. Use the Dynamic ECH helper instead.",
"echDynamicTitle": "Cloudflare dynamic ECH (works behind the orange cloud)",
"echPresetLabel": "Preset combination",
"echPresetOtherDomains": "Other ECH target domains",
"echPresetAdvanced": "Custom combination…",
"echDynamicIntro": "Fills the ECH config list with a client-side dynamic lookup (target domain + DoH resolver). The client asks the resolver for the domain's HTTPS record on every connection, so it always sees the current ECH keys — including Cloudflare's rotated ones.",
"echDynamicDomain": "ECH target domain",
"echDynamicDoh": "DoH resolver",
"echDomainAuto": "Automatic — resolve the ECH record for the node's SNI",
"echDynamicApply": "Apply to ECH config list",
"echDynamicCustom": "Custom URL…",
"echDynamicInvalid": "Enter a valid domain and a resolver URL (https, h2c or udp) to build the value.",
"echDynamicUnsupportedScheme": "Xray only resolves the ECH config over https://, h2c:// or udp://. This endpoint will never answer.",
"echDynamicPlaceholder": "cloudflare-ech.com+https://dns.alidns.com/dns-query",
"echDynamicOverwriteTitle": "This inbound has a locally generated ECH keypair",
"echDynamicOverwriteBody": "Behind a CDN that keypair is never used: the CDN terminates the TLS handshake. Applying the dynamic value will replace the ECH config list and clear the local ECH key.",
"echDynamicOverwriteConfirm": "Replace and clear local key",
"echExistingNotDynamic": "This ECH config list is not in the dynamic form; filling it will replace what you set by hand.",
"echDohGroup": {
"china": "Mainland China (reachable from behind the GFW)",
"global": "Global",
"europe": "Europe",
"udp": "Plain DNS (udp://)"
},
"echRecommended": "recommended",
"echMainlandAdvisory": "Mainland resolvers need a target domain whose HTTPS record carries ECH; verify the pair before saving.",
"echConfigList": "ECH config list",
"echConfigListHint": "Sent to clients in share links and subscriptions. Accepts a local ECHConfigList (from the button below) or the dynamic form domain+DoH URL.",
"echKeyHint": "Only for direct connections — leave empty when the inbound sits behind a CDN.",
"curvePreferences": "Curve Preferences",
"curvePreferencesTip": "Restrict the TLS key-exchange curves the server offers, in preference order (e.g. X25519MLKEM768, X25519). Leave empty to use Xray-core defaults.",
"masterKeyLog": "Master Key Log",
@@ -1098,7 +1127,6 @@
"pins": "Pinned cert SHA-256",
"verifyPeerCertByName": "Verify peer cert by name",
"allowInsecure": "Allow insecure",
"echConfigList": "ECH config list",
"muxParams": "Mux",
"sockoptParams": "Sockopt",
"finalMask": "Final Mask",