Browse Source

[feature] wireguard inbound

Co-Authored-By: Alireza Ahmadi <[email protected]>
MHSanaei 1 year ago
parent
commit
722f5e716f

+ 81 - 44
web/assets/js/model/xray.js

@@ -6,6 +6,7 @@ const Protocols = {
     DOKODEMO: 'dokodemo-door',
     DOKODEMO: 'dokodemo-door',
     SOCKS: 'socks',
     SOCKS: 'socks',
     HTTP: 'http',
     HTTP: 'http',
+    WIREGUARD: 'wireguard',
 };
 };
 
 
 const SSMethods = {
 const SSMethods = {
@@ -765,16 +766,18 @@ class RealityStreamSettings extends XrayCommonClass {
 }
 }
 
 
 RealityStreamSettings.Settings = class extends XrayCommonClass {
 RealityStreamSettings.Settings = class extends XrayCommonClass {
-    constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, spiderX= '/') {
+    constructor(publicKey = '', fingerprint = UTLS_FINGERPRINT.UTLS_FIREFOX, serverName = '', spiderX= '/') {
         super();
         super();
         this.publicKey = publicKey;
         this.publicKey = publicKey;
         this.fingerprint = fingerprint;
         this.fingerprint = fingerprint;
+        this.serverName = serverName;
         this.spiderX = spiderX;
         this.spiderX = spiderX;
     }
     }
     static fromJson(json = {}) {
     static fromJson(json = {}) {
         return new RealityStreamSettings.Settings(
         return new RealityStreamSettings.Settings(
             json.publicKey,
             json.publicKey,
             json.fingerprint,
             json.fingerprint,
+            json.serverName,
             json.spiderX,
             json.spiderX,
         );
         );
     }
     }
@@ -782,6 +785,7 @@ RealityStreamSettings.Settings = class extends XrayCommonClass {
         return {
         return {
             publicKey: this.publicKey,
             publicKey: this.publicKey,
             fingerprint: this.fingerprint,
             fingerprint: this.fingerprint,
+            serverName: this.serverName,
             spiderX: this.spiderX,
             spiderX: this.spiderX,
         };
         };
     }
     }
@@ -795,6 +799,7 @@ class SockoptStreamSettings extends XrayCommonClass {
         this.mark = mark;
         this.mark = mark;
         this.tproxy = tproxy;
         this.tproxy = tproxy;
     }
     }
+    
     static fromJson(json = {}) {
     static fromJson(json = {}) {
         if (Object.keys(json).length === 0) return undefined;
         if (Object.keys(json).length === 0) return undefined;
         return new SockoptStreamSettings(
         return new SockoptStreamSettings(
@@ -996,42 +1001,6 @@ class Inbound extends XrayCommonClass {
         }
         }
     }
     }
 
 
-    get tls() {
-        return this.stream.security === 'tls';
-    }
-
-    set tls(isTls) {
-        if (isTls) {
-            this.stream.security = 'tls';
-        } else {
-            this.stream.security = 'none';
-        }
-    }
-
-    get xtls() {
-        return this.stream.security === 'xtls';
-    }
-
-    set xtls(isXtls) {
-        if (isXtls) {
-            this.stream.security = 'xtls';
-        } else {
-            this.stream.security = 'none';
-        }
-    }
-
-    get reality() {
-        return this.stream.security === 'reality';
-    }
-
-    set reality(isReality) {
-        if (isReality) {
-            this.stream.security = 'reality';
-        } else {
-            this.stream.security = 'none';
-        }
-    }
-
     get network() {
     get network() {
         return this.stream.network;
         return this.stream.network;
     }
     }
@@ -1143,11 +1112,6 @@ class Inbound extends XrayCommonClass {
         return ["tcp", "ws", "http", "quic", "grpc"].includes(this.network);
         return ["tcp", "ws", "http", "quic", "grpc"].includes(this.network);
     }
     }
 
 
-    canEnableReality() {
-        if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
-        return ["tcp", "http", "grpc"].includes(this.network);
-    }
-
     //this is used for xtls-rprx-vision
     //this is used for xtls-rprx-vision
     canEnableTlsFlow() {
     canEnableTlsFlow() {
         if (((this.stream.security === 'tls') || (this.stream.security === 'reality')) && (this.network === "tcp")) {
         if (((this.stream.security === 'tls') || (this.stream.security === 'reality')) && (this.network === "tcp")) {
@@ -1156,6 +1120,11 @@ class Inbound extends XrayCommonClass {
         return false;
         return false;
     }
     }
 
 
+    canEnableReality() {
+        if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
+        return ["tcp", "http", "grpc"].includes(this.network);
+    }
+
     canEnableXtls() {
     canEnableXtls() {
         if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
         if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false;
         return this.network === "tcp";
         return this.network === "tcp";
@@ -1608,7 +1577,7 @@ class Inbound extends XrayCommonClass {
             });
             });
             return links.join('\r\n');
             return links.join('\r\n');
         } else {
         } else {
-            if(this.protocol == Protocols.SHADOWSOCKS && !this.isSSMultiUser) return this.genSSLink(this.listen, this.port, remark);
+            if(this.protocol == Protocols.SHADOWSOCKS && !this.isSSMultiUser) return this.genSSLink(this.listen, this.port, 'same', remark);
             return '';
             return '';
         }
         }
     }
     }
@@ -1658,7 +1627,8 @@ Inbound.Settings = class extends XrayCommonClass {
             case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings(protocol);
             case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings(protocol);
             case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(protocol);
             case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(protocol);
             case Protocols.SOCKS: return new Inbound.SocksSettings(protocol);
             case Protocols.SOCKS: return new Inbound.SocksSettings(protocol);
-            case Protocols.HTTP: return new Inbound.HttpSettings(protocol);
+            case Protocols.HTTP: return new Inbound.HttpSettings(protocol);            
+            case Protocols.WIREGUARD: return new Inbound.WireguardSettings(protocol);
             default: return null;
             default: return null;
         }
         }
     }
     }
@@ -1672,6 +1642,7 @@ Inbound.Settings = class extends XrayCommonClass {
             case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json);
             case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json);
             case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json);
             case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json);
             case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json);
             case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json);
+            case Protocols.WIREGUARD: return Inbound.WireguardSettings.fromJson(json);
             default: return null;
             default: return null;
         }
         }
     }
     }
@@ -2274,3 +2245,69 @@ Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass {
         return new Inbound.HttpSettings.HttpAccount(json.user, json.pass);
         return new Inbound.HttpSettings.HttpAccount(json.user, json.pass);
     }
     }
 };
 };
+
+Inbound.WireguardSettings = class extends XrayCommonClass {
+    constructor(protocol, mtu=1420, secretKey=Wireguard.generateKeypair().privateKey, peers=[new Inbound.WireguardSettings.Peer()], kernelMode=false) {
+        super(protocol);
+        this.mtu = mtu;
+        this.secretKey = secretKey;
+        this.pubKey = secretKey.length>0 ? Wireguard.generateKeypair(secretKey).publicKey : '';
+        this.peers = peers;
+        this.kernelMode = kernelMode;
+    }
+
+    addPeer() {
+        this.peers.push(new Inbound.WireguardSettings.Peer());
+    }
+
+    delPeer(index) {
+        this.peers.splice(index, 1);
+    }
+
+    static fromJson(json={}){
+        return new Inbound.WireguardSettings(
+            Protocols.WIREGUARD,
+            json.mtu,
+            json.secretKey,
+            json.peers.map(peer => Inbound.WireguardSettings.Peer.fromJson(peer)),
+            json.kernelMode,
+        );
+    }
+
+    toJson() {
+        return {
+            mtu: this.mtu?? undefined,
+            secretKey: this.secretKey,
+            peers: Inbound.WireguardSettings.Peer.toJsonArray(this.peers),
+            kernelMode: this.kernelMode,
+        };
+    }
+};
+
+Inbound.WireguardSettings.Peer = class extends XrayCommonClass {
+    constructor(publicKey='', psk='', allowedIPs=['0.0.0.0/0','::/0'], keepAlive=0) {
+        super();
+        this.publicKey = publicKey;
+        this.psk = psk;
+        this.allowedIPs = allowedIPs;
+        this.keepAlive = keepAlive;
+    }
+
+    static fromJson(json={}){
+        return new Inbound.WireguardSettings.Peer(
+            json.publicKey,
+            json.preSharedKey,
+            json.allowedIPs,
+            json.keepAlive
+        );
+    }
+
+    toJson() {
+        return {
+            publicKey: this.publicKey,
+            preSharedKey: this.psk.length>0 ? this.psk : undefined,
+            allowedIPs: this.allowedIPs,
+            keepAlive: this.keepAlive?? undefined,
+        };
+    }
+};

+ 187 - 0
web/assets/js/util/utils.js

@@ -295,3 +295,190 @@ class ObjectUtil {
         return true;
         return true;
     }
     }
 }
 }
+
+class Wireguard {
+	static gf(init) {
+		var r = new Float64Array(16);
+		if (init) {
+			for (var i = 0; i < init.length; ++i)
+				r[i] = init[i];
+		}
+		return r;
+	}
+
+	static pack(o, n) {
+		var b, m = this.gf(), t = this.gf();
+		for (var i = 0; i < 16; ++i)
+			t[i] = n[i];
+		this.carry(t);
+		this.carry(t);
+		this.carry(t);
+		for (var j = 0; j < 2; ++j) {
+			m[0] = t[0] - 0xffed;
+			for (var i = 1; i < 15; ++i) {
+				m[i] = t[i] - 0xffff - ((m[i - 1] >> 16) & 1);
+				m[i - 1] &= 0xffff;
+			}
+			m[15] = t[15] - 0x7fff - ((m[14] >> 16) & 1);
+			b = (m[15] >> 16) & 1;
+			m[14] &= 0xffff;
+			this.cswap(t, m, 1 - b);
+		}
+		for (var i = 0; i < 16; ++i) {
+			o[2 * i] = t[i] & 0xff;
+			o[2 * i + 1] = t[i] >> 8;
+		}
+	}
+
+	static carry(o) {
+		var c;
+		for (var i = 0; i < 16; ++i) {
+			o[(i + 1) % 16] += (i < 15 ? 1 : 38) * Math.floor(o[i] / 65536);
+			o[i] &= 0xffff;
+		}
+	}
+
+	static cswap(p, q, b) {
+		var t, c = ~(b - 1);
+		for (var i = 0; i < 16; ++i) {
+			t = c & (p[i] ^ q[i]);
+			p[i] ^= t;
+			q[i] ^= t;
+		}
+	}
+
+	static add(o, a, b) {
+		for (var i = 0; i < 16; ++i)
+			o[i] = (a[i] + b[i]) | 0;
+	}
+
+	static subtract(o, a, b) {
+		for (var i = 0; i < 16; ++i)
+			o[i] = (a[i] - b[i]) | 0;
+	}
+
+	static multmod(o, a, b) {
+		var t = new Float64Array(31);
+		for (var i = 0; i < 16; ++i) {
+			for (var j = 0; j < 16; ++j)
+				t[i + j] += a[i] * b[j];
+		}
+		for (var i = 0; i < 15; ++i)
+			t[i] += 38 * t[i + 16];
+		for (var i = 0; i < 16; ++i)
+			o[i] = t[i];
+		this.carry(o);
+		this.carry(o);
+	}
+
+	static invert(o, i) {
+		var c = this.gf();
+		for (var a = 0; a < 16; ++a)
+			c[a] = i[a];
+		for (var a = 253; a >= 0; --a) {
+			this.multmod(c, c, c);
+			if (a !== 2 && a !== 4)
+                this.multmod(c, c, i);
+		}
+		for (var a = 0; a < 16; ++a)
+			o[a] = c[a];
+	}
+
+	static clamp(z) {
+		z[31] = (z[31] & 127) | 64;
+		z[0] &= 248;
+	}
+
+	static generatePublicKey(privateKey) {
+		var r, z = new Uint8Array(32);
+		var a = this.gf([1]),
+			b = this.gf([9]),
+			c = this.gf(),
+			d = this.gf([1]),
+			e = this.gf(),
+			f = this.gf(),
+			_121665 = this.gf([0xdb41, 1]),
+			_9 = this.gf([9]);
+		for (var i = 0; i < 32; ++i)
+			z[i] = privateKey[i];
+		this.clamp(z);
+		for (var i = 254; i >= 0; --i) {
+			r = (z[i >>> 3] >>> (i & 7)) & 1;
+			this.cswap(a, b, r);
+			this.cswap(c, d, r);
+			this.add(e, a, c);
+			this.subtract(a, a, c);
+			this.add(c, b, d);
+			this.subtract(b, b, d);
+			this.multmod(d, e, e);
+			this.multmod(f, a, a);
+			this.multmod(a, c, a);
+			this.multmod(c, b, e);
+			this.add(e, a, c);
+			this.subtract(a, a, c);
+			this.multmod(b, a, a);
+			this.subtract(c, d, f);
+			this.multmod(a, c, _121665);
+			this.add(a, a, d);
+			this.multmod(c, c, a);
+			this.multmod(a, d, f);
+			this.multmod(d, b, _9);
+			this.multmod(b, e, e);
+			this.cswap(a, b, r);
+			this.cswap(c, d, r);
+		}
+		this.invert(c, c);
+		this.multmod(a, a, c);
+		this.pack(z, a);
+		return z;
+	}
+
+	static generatePresharedKey() {
+		var privateKey = new Uint8Array(32);
+		window.crypto.getRandomValues(privateKey);
+		return privateKey;
+	}
+
+	static generatePrivateKey() {
+		var privateKey = this.generatePresharedKey();
+		this.clamp(privateKey);
+		return privateKey;
+	}
+
+	static encodeBase64(dest, src) {
+		var input = Uint8Array.from([(src[0] >> 2) & 63, ((src[0] << 4) | (src[1] >> 4)) & 63, ((src[1] << 2) | (src[2] >> 6)) & 63, src[2] & 63]);
+		for (var i = 0; i < 4; ++i)
+			dest[i] = input[i] + 65 +
+			(((25 - input[i]) >> 8) & 6) -
+			(((51 - input[i]) >> 8) & 75) -
+			(((61 - input[i]) >> 8) & 15) +
+			(((62 - input[i]) >> 8) & 3);
+	}
+
+	static keyToBase64(key) {
+		var i, base64 = new Uint8Array(44);
+		for (i = 0; i < 32 / 3; ++i)
+            this.encodeBase64(base64.subarray(i * 4), key.subarray(i * 3));
+        this.encodeBase64(base64.subarray(i * 4), Uint8Array.from([key[i * 3 + 0], key[i * 3 + 1], 0]));
+		base64[43] = 61;
+		return String.fromCharCode.apply(null, base64);
+	}
+
+    static keyFromBase64(encoded) {
+        const binaryStr = atob(encoded);
+        const bytes = new Uint8Array(binaryStr.length);
+        for (let i = 0; i < binaryStr.length; i++) {
+            bytes[i] = binaryStr.charCodeAt(i);
+        }
+        return bytes;
+    }
+
+	static generateKeypair(secretKey='') {
+        var privateKey = secretKey.length>0 ? this.keyFromBase64(secretKey) : this.generatePrivateKey();
+        var publicKey = this.generatePublicKey(privateKey);
+        return {
+            publicKey: this.keyToBase64(publicKey),
+            privateKey: secretKey.length>0 ? secretKey : this.keyToBase64(privateKey)
+        };
+    }
+}

+ 5 - 0
web/html/xui/form/inbound.html

@@ -97,6 +97,11 @@
     {{template "form/http"}}
     {{template "form/http"}}
 </template>
 </template>
 
 
+<!-- wireguard -->
+<template v-if="inbound.protocol === Protocols.WIREGUARD">
+    {{template "form/wireguard"}}
+</template>
+
 <!-- stream settings -->
 <!-- stream settings -->
 <template v-if="inbound.canEnableStream()">
 <template v-if="inbound.canEnableStream()">
     {{template "form/streamSettings"}}
     {{template "form/streamSettings"}}

+ 56 - 0
web/html/xui/form/protocol/wireguard.html

@@ -0,0 +1,56 @@
+{{define "form/wireguard"}}
+<a-form :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
+    <a-form-item>
+        <template slot="label">
+            <a-tooltip>
+                <template slot="title">
+                    <span>{{ i18n "reset" }}</span>
+                </template>
+                {{ i18n "pages.xray.wireguard.secretKey" }}
+                <a-icon type="sync"
+                    @click="[inbound.settings.pubKey, inbound.settings.secretKey] = Object.values(Wireguard.generateKeypair())">
+                </a-icon>
+            </a-tooltip>
+        </template>
+        <a-input v-model.trim="inbound.settings.secretKey"></a-input>
+    </a-form-item>
+    <a-form-item label='{{ i18n "pages.xray.wireguard.publicKey" }}'>
+        <a-input disabled v-model="inbound.settings.pubKey"></a-input>
+    </a-form-item>
+    <a-form-item label='MTU'>
+        <a-input-number v-model.number="inbound.settings.mtu"></a-input-number>
+    </a-form-item>
+    <a-form-item label='Kernel Mode'>
+        <a-switch v-model="inbound.settings.kernelMode"></a-switch>
+    </a-form-item>
+    <a-form-item label="Peers">
+        <a-button type="primary" size="small" @click="inbound.settings.addPeer()">+</a-button>
+    </a-form-item>
+    <a-form v-for="(peer, index) in inbound.settings.peers" :colon="false" :label-col="{ md: {span:6} }" :wrapper-col="{ md: {span:14} }">
+        <a-divider style="margin:0;">
+            Peer [[ index + 1 ]]
+            <a-icon v-if="inbound.settings.peers.length>1" type="delete" @click="() => inbound.settings.delPeer(index)"
+                    style="color: rgb(255, 77, 79);cursor: pointer;"/>
+        </a-divider>
+        <a-form-item label='{{ i18n "pages.xray.wireguard.publicKey" }}'>
+            <a-input v-model.trim="peer.publicKey"></a-input>
+        </a-form-item>
+        <a-form-item label='{{ i18n "pages.xray.wireguard.psk" }}'>
+            <a-input v-model.trim="peer.psk"></a-input>
+        </a-form-item>
+        <a-form-item>
+            <template slot="label">
+                {{ i18n "pages.xray.wireguard.allowedIPs" }} <a-button type="primary" size="small" @click="peer.allowedIPs.push('')">+</a-button>
+            </template>
+            <template v-for="(aip, index) in peer.allowedIPs" style="margin-bottom: 10px;">
+                <a-input v-model.trim="peer.allowedIPs[index]">
+                    <a-button v-if="peer.allowedIPs.length>1" slot="addonAfter" size="small" @click="peer.allowedIPs.splice(index, 1)">-</a-button>
+                </a-input>
+            </template>
+        </a-form-item>
+        <a-form-item label='Keep Alive'>
+            <a-input-number v-model.number="peer.keepAlive" :min="0"></a-input>
+        </a-form-item>    
+    </a-form>
+</a-form>
+{{end}}

+ 8 - 0
web/translation/translate.en_US.toml

@@ -420,6 +420,14 @@
 "portal" = "Portal"
 "portal" = "Portal"
 "intercon" = "Interconnection"
 "intercon" = "Interconnection"
 
 
+[pages.xray.wireguard]
+"secretKey" = "Secret Key"
+"publicKey" = "Public Key"
+"allowedIPs" = "Allowed IPs"
+"endpoint" = "End Point"
+"psk" = "PreShared Key"
+"domainStrategy" = "Domain Strategy"
+
 [pages.settings.security]
 [pages.settings.security]
 "admin" = "Admin"
 "admin" = "Admin"
 "secret" = "Secret Token"
 "secret" = "Secret Token"

+ 8 - 0
web/translation/translate.es_ES.toml

@@ -420,6 +420,14 @@
 "portal" = "portal"
 "portal" = "portal"
 "intercon" = "Interconexión"
 "intercon" = "Interconexión"
 
 
+[pages.xray.wireguard]
+"secretKey" = "Llave secreta"
+"publicKey" = "Llave pública"
+"allowedIPs" = "IP permitidas"
+"endpoint" = "Punto final"
+"psk" = "Clave precompartida"
+"domainStrategy" = "Estrategia de dominio"
+
 [pages.settings.security]
 [pages.settings.security]
 "admin" = "Administrador"
 "admin" = "Administrador"
 "secret" = "Token Secreto"
 "secret" = "Token Secreto"

+ 8 - 0
web/translation/translate.fa_IR.toml

@@ -420,6 +420,14 @@
 "portal" = "پرتال"
 "portal" = "پرتال"
 "intercon" = "اتصال میانی"
 "intercon" = "اتصال میانی"
 
 
+[pages.xray.wireguard]
+"secretKey" = "کلید شخصی"
+"publicKey" = "کلید عمومی"
+"allowedIPs" = "آی‌پی‌های مجاز"
+"endpoint" = "نقطه پایانی"
+"psk" = "کلید مشترک"
+"domainStrategy" = "استراتژی حل دامنه"
+
 [pages.settings.security]
 [pages.settings.security]
 "admin" = "مدیر"
 "admin" = "مدیر"
 "secret" = "توکن امنیتی"
 "secret" = "توکن امنیتی"

+ 8 - 0
web/translation/translate.ru_RU.toml

@@ -420,6 +420,14 @@
 "portal" = "Портал"
 "portal" = "Портал"
 "intercon" = "Соединение"
 "intercon" = "Соединение"
 
 
+[pages.xray.wireguard]
+"secretKey" = "Секретный ключ"
+"publicKey" = "Открытый ключ"
+"allowedIPs" = "Разрешенные IP-адреса"
+"endpoint" = "Конечная точка"
+"psk" = "Общий ключ"
+"domainStrategy" = "Стратегия домена"
+
 [pages.settings.security]
 [pages.settings.security]
 "admin" = "Админ"
 "admin" = "Админ"
 "secret" = "Секретный токен"
 "secret" = "Секретный токен"

+ 8 - 0
web/translation/translate.vi_VN.toml

@@ -420,6 +420,14 @@
 "portal" = "Cổng thông tin"
 "portal" = "Cổng thông tin"
 "intercon" = "Kết nối"
 "intercon" = "Kết nối"
 
 
+[pages.xray.wireguard]
+"secretKey" = "Chìa khoá bí mật"
+"publicKey" = "Khóa công khai"
+"allowedIPs" = "IP được phép"
+"endpoint" = "Điểm cuối"
+"psk" = "Khóa chia sẻ"
+"domainStrategy" = "Chiến lược tên miền"
+
 [pages.settings.security]
 [pages.settings.security]
 "admin" = "Quản trị viên"
 "admin" = "Quản trị viên"
 "secret" = "Mã thông báo bí mật"
 "secret" = "Mã thông báo bí mật"

+ 8 - 0
web/translation/translate.zh_Hans.toml

@@ -420,6 +420,14 @@
 "portal" = "门户"
 "portal" = "门户"
 "intercon" = "互连"
 "intercon" = "互连"
 
 
+[pages.xray.wireguard]
+"secretKey" = "密钥"
+"publicKey" = "公钥"
+"allowedIPs" = "允许的 IP"
+"endpoint" = "终点"
+"psk" = "共享密钥"
+"domainStrategy" = "域策略"
+
 [pages.settings.security]
 [pages.settings.security]
 "admin" = "管理员"
 "admin" = "管理员"
 "secret" = "密钥"
 "secret" = "密钥"