Browse Source

add ech support (#3310)

Co-authored-by: Alireza Ahmadi <[email protected]>
Sanaei 2 weeks ago
parent
commit
e4ba5ba53a

+ 22 - 1
web/assets/js/model/inbound.js

@@ -560,6 +560,8 @@ class TlsStreamSettings extends XrayCommonClass {
         enableSessionResumption = false,
         certificates = [new TlsStreamSettings.Cert()],
         alpn = [ALPN_OPTION.H2, ALPN_OPTION.HTTP1],
+        echServerKeys = '',
+        echForceQuery = 'none',
         settings = new TlsStreamSettings.Settings()
     ) {
         super();
@@ -573,6 +575,8 @@ class TlsStreamSettings extends XrayCommonClass {
         this.enableSessionResumption = enableSessionResumption;
         this.certs = certificates;
         this.alpn = alpn;
+        this.echServerKeys = echServerKeys;
+        this.echForceQuery = echForceQuery;
         this.settings = settings;
     }
 
@@ -592,7 +596,7 @@ class TlsStreamSettings extends XrayCommonClass {
         }
 
         if (!ObjectUtil.isEmpty(json.settings)) {
-            settings = new TlsStreamSettings.Settings(json.settings.allowInsecure, json.settings.fingerprint, json.settings.serverName, json.settings.domains);
+            settings = new TlsStreamSettings.Settings(json.settings.allowInsecure, json.settings.fingerprint, json.settings.echConfigList);
         }
         return new TlsStreamSettings(
             json.serverName,
@@ -605,6 +609,8 @@ class TlsStreamSettings extends XrayCommonClass {
             json.enableSessionResumption,
             certs,
             json.alpn,
+            json.echServerKeys,
+            json.echForceQuery,
             settings,
         );
     }
@@ -621,6 +627,8 @@ class TlsStreamSettings extends XrayCommonClass {
             enableSessionResumption: this.enableSessionResumption,
             certificates: TlsStreamSettings.toJsonArray(this.certs),
             alpn: this.alpn,
+            echServerKeys: this.echServerKeys,
+            echForceQuery: this.echForceQuery,
             settings: this.settings,
         };
     }
@@ -701,21 +709,25 @@ TlsStreamSettings.Settings = class extends XrayCommonClass {
     constructor(
         allowInsecure = false,
         fingerprint = UTLS_FINGERPRINT.UTLS_CHROME,
+        echConfigList = '',
     ) {
         super();
         this.allowInsecure = allowInsecure;
         this.fingerprint = fingerprint;
+        this.echConfigList = echConfigList;
     }
     static fromJson(json = {}) {
         return new TlsStreamSettings.Settings(
             json.allowInsecure,
             json.fingerprint,
+            json.echConfigList,
         );
     }
     toJson() {
         return {
             allowInsecure: this.allowInsecure,
             fingerprint: this.fingerprint,
+            echConfigList: this.echConfigList
         };
     }
 };
@@ -1375,6 +1387,9 @@ class Inbound extends XrayCommonClass {
                 if (!ObjectUtil.isEmpty(this.stream.tls.sni)) {
                     params.set("sni", this.stream.tls.sni);
                 }
+                if (this.stream.tls.settings.echConfigList?.length > 0) {
+                    params.set("ech", this.stream.tls.settings.echConfigList);
+                }
                 if (type == "tcp" && !ObjectUtil.isEmpty(flow)) {
                     params.set("flow", flow);
                 }
@@ -1474,6 +1489,9 @@ class Inbound extends XrayCommonClass {
                 if (this.stream.tls.settings.allowInsecure) {
                     params.set("allowInsecure", "1");
                 }
+                if (this.stream.tls.settings.echConfigList?.length > 0) {
+                    params.set("ech", this.stream.tls.settings.echConfigList);
+                }
                 if (!ObjectUtil.isEmpty(this.stream.tls.sni)) {
                     params.set("sni", this.stream.tls.sni);
                 }
@@ -1552,6 +1570,9 @@ class Inbound extends XrayCommonClass {
                 if (this.stream.tls.settings.allowInsecure) {
                     params.set("allowInsecure", "1");
                 }
+                if (this.stream.tls.settings.echConfigList?.length > 0) {
+                    params.set("ech", this.stream.tls.settings.echConfigList);
+                }
                 if (!ObjectUtil.isEmpty(this.stream.tls.sni)) {
                     params.set("sni", this.stream.tls.sni);
                 }

+ 7 - 2
web/assets/js/model/outbound.js

@@ -354,13 +354,15 @@ class TlsStreamSettings extends CommonClass {
         serverName = '',
         alpn = [],
         fingerprint = '',
-        allowInsecure = false
+        allowInsecure = false,
+        echConfigList = '',
     ) {
         super();
         this.serverName = serverName;
         this.alpn = alpn;
         this.fingerprint = fingerprint;
         this.allowInsecure = allowInsecure;
+        this.echConfigList = echConfigList;
     }
 
     static fromJson(json = {}) {
@@ -369,6 +371,7 @@ class TlsStreamSettings extends CommonClass {
             json.alpn,
             json.fingerprint,
             json.allowInsecure,
+            json.echConfigList,
         );
     }
 
@@ -378,6 +381,7 @@ class TlsStreamSettings extends CommonClass {
             alpn: this.alpn,
             fingerprint: this.fingerprint,
             allowInsecure: this.allowInsecure,
+            echConfigList: this.echConfigList
         };
     }
 }
@@ -782,7 +786,8 @@ class Outbound extends CommonClass {
             let alpn = url.searchParams.get('alpn');
             let allowInsecure = url.searchParams.get('allowInsecure');
             let sni = url.searchParams.get('sni') ?? '';
-            stream.tls = new TlsStreamSettings(sni, alpn ? alpn.split(',') : [], fp, allowInsecure == 1);
+            let ech = url.searchParams.get('ech') ?? '';
+            stream.tls = new TlsStreamSettings(sni, alpn ? alpn.split(',') : [], fp, allowInsecure == 1, ech);
         }
 
         if (security == 'reality') {

+ 11 - 0
web/controller/server.go

@@ -51,6 +51,7 @@ func (a *ServerController) initRouter(g *gin.RouterGroup) {
 	g.POST("/importDB", a.importDB)
 	g.POST("/getNewX25519Cert", a.getNewX25519Cert)
 	g.POST("/getNewmldsa65", a.getNewmldsa65)
+	g.POST("/getNewEchCert", a.getNewEchCert)
 }
 
 func (a *ServerController) refreshStatus() {
@@ -208,3 +209,13 @@ func (a *ServerController) getNewmldsa65(c *gin.Context) {
 	}
 	jsonObj(c, cert, nil)
 }
+
+func (a *ServerController) getNewEchCert(c *gin.Context) {
+	sni := c.PostForm("sni")
+	cert, err := a.serverService.GetNewEchCert(sni)
+	if err != nil {
+		jsonMsg(c, "get ech certificate", err)
+		return
+	}
+	jsonObj(c, cert, nil)
+}

+ 15 - 0
web/html/form/tls_settings.html

@@ -106,6 +106,21 @@
         <a-switch v-model="cert.buildChain"></a-switch>
       </a-form-item>
     </template>
+    <a-form-item label='ECH key'>
+        <a-input v-model="inbound.stream.tls.echServerKeys"></a-input>
+    </a-form-item>
+    <a-form-item label='ECH config'>
+        <a-input v-model="inbound.stream.tls.settings.echConfigList"></a-input>
+    </a-form-item>
+    <a-form-item label='ECH force query'>
+        <a-select v-model="inbound.stream.tls.echForceQuery"
+            :dropdown-class-name="themeSwitcher.currentTheme">
+            <a-select-option v-for="key in ['none', 'half', 'full']" :value="key">[[ key ]]</a-select-option>
+        </a-select>
+    </a-form-item>
+    <a-form-item label=" ">
+        <a-button type="primary" icon="import" @click="getNewEchCert">Get New ECH Cert</a-button>
+    </a-form-item>
   </template>
 
   <!-- reality settings -->

+ 10 - 0
web/html/modals/inbound_modal.html

@@ -152,6 +152,16 @@
                 inModal.inbound.stream.reality.mldsa65Seed = msg.obj.seed;
                 inModal.inbound.stream.reality.settings.mldsa65Verify = msg.obj.verify;
             },
+            async getNewEchCert() {
+                inModal.loading(true);
+                const msg = await HttpUtil.post('/server/getNewEchCert', {sni: inModal.inbound.stream.tls.sni});
+                inModal.loading(false);
+                if (!msg.success) {
+                    return;
+                }
+                inModal.inbound.stream.tls.echServerKeys = msg.obj.echServerKeys;
+                inModal.inbound.stream.tls.settings.echConfigList = msg.obj.echConfigList;
+            },
         },
     });
 

+ 24 - 0
web/service/server.go

@@ -743,3 +743,27 @@ func (s *ServerService) GetNewmldsa65() (any, error) {
 
 	return keyPair, nil
 }
+
+func (s *ServerService) GetNewEchCert(sni string) (interface{}, error) {
+	// Run the command
+	cmd := exec.Command(xray.GetBinaryPath(), "tls", "ech", "--serverName", sni)
+	var out bytes.Buffer
+	cmd.Stdout = &out
+	err := cmd.Run()
+	if err != nil {
+		return nil, err
+	}
+
+	lines := strings.Split(out.String(), "\n")
+	if len(lines) < 4 {
+		return nil, common.NewError("invalid ech cert")
+	}
+
+	configList := lines[1]
+	serverKeys := lines[3]
+
+	return map[string]interface{}{
+		"echServerKeys": serverKeys,
+		"echConfigList": configList,
+	}, nil
+}