diff options
| author | MHSanaei <ho3ein.sanaei@gmail.com> | 2024-01-10 15:42:54 +0300 |
|---|---|---|
| committer | MHSanaei <ho3ein.sanaei@gmail.com> | 2024-01-10 15:42:54 +0300 |
| commit | 722f5e716fb9801a1329dd754268d1199ad97957 (patch) | |
| tree | 9e009cbe3cbad77a9a4688ca21904c961fb40366 | |
| parent | fdf31d80e7b378bc20e4958e2893b72caf088602 (diff) | |
[feature] wireguard inbound
Co-Authored-By: Alireza Ahmadi <alireza7@gmail.com>
| -rw-r--r-- | web/assets/js/model/xray.js | 125 | ||||
| -rw-r--r-- | web/assets/js/util/utils.js | 187 | ||||
| -rw-r--r-- | web/html/xui/form/inbound.html | 5 | ||||
| -rw-r--r-- | web/html/xui/form/protocol/wireguard.html | 56 | ||||
| -rw-r--r-- | web/translation/translate.en_US.toml | 8 | ||||
| -rw-r--r-- | web/translation/translate.es_ES.toml | 8 | ||||
| -rw-r--r-- | web/translation/translate.fa_IR.toml | 8 | ||||
| -rw-r--r-- | web/translation/translate.ru_RU.toml | 8 | ||||
| -rw-r--r-- | web/translation/translate.vi_VN.toml | 8 | ||||
| -rw-r--r-- | web/translation/translate.zh_Hans.toml | 8 |
10 files changed, 377 insertions, 44 deletions
diff --git a/web/assets/js/model/xray.js b/web/assets/js/model/xray.js index de0b31f1..3492db6f 100644 --- a/web/assets/js/model/xray.js +++ b/web/assets/js/model/xray.js @@ -6,6 +6,7 @@ const Protocols = { DOKODEMO: 'dokodemo-door', SOCKS: 'socks', HTTP: 'http', + WIREGUARD: 'wireguard', }; const SSMethods = { @@ -765,16 +766,18 @@ class RealityStreamSettings 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(); this.publicKey = publicKey; this.fingerprint = fingerprint; + this.serverName = serverName; this.spiderX = spiderX; } static fromJson(json = {}) { return new RealityStreamSettings.Settings( json.publicKey, json.fingerprint, + json.serverName, json.spiderX, ); } @@ -782,6 +785,7 @@ RealityStreamSettings.Settings = class extends XrayCommonClass { return { publicKey: this.publicKey, fingerprint: this.fingerprint, + serverName: this.serverName, spiderX: this.spiderX, }; } @@ -795,6 +799,7 @@ class SockoptStreamSettings extends XrayCommonClass { this.mark = mark; this.tproxy = tproxy; } + static fromJson(json = {}) { if (Object.keys(json).length === 0) return undefined; 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() { return this.stream.network; } @@ -1143,11 +1112,6 @@ class Inbound extends XrayCommonClass { 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 canEnableTlsFlow() { if (((this.stream.security === 'tls') || (this.stream.security === 'reality')) && (this.network === "tcp")) { @@ -1156,6 +1120,11 @@ class Inbound extends XrayCommonClass { return false; } + canEnableReality() { + if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false; + return ["tcp", "http", "grpc"].includes(this.network); + } + canEnableXtls() { if(![Protocols.VLESS, Protocols.TROJAN].includes(this.protocol)) return false; return this.network === "tcp"; @@ -1608,7 +1577,7 @@ class Inbound extends XrayCommonClass { }); return links.join('\r\n'); } 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 ''; } } @@ -1658,7 +1627,8 @@ Inbound.Settings = class extends XrayCommonClass { case Protocols.SHADOWSOCKS: return new Inbound.ShadowsocksSettings(protocol); case Protocols.DOKODEMO: return new Inbound.DokodemoSettings(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; } } @@ -1672,6 +1642,7 @@ Inbound.Settings = class extends XrayCommonClass { case Protocols.DOKODEMO: return Inbound.DokodemoSettings.fromJson(json); case Protocols.SOCKS: return Inbound.SocksSettings.fromJson(json); case Protocols.HTTP: return Inbound.HttpSettings.fromJson(json); + case Protocols.WIREGUARD: return Inbound.WireguardSettings.fromJson(json); default: return null; } } @@ -2274,3 +2245,69 @@ Inbound.HttpSettings.HttpAccount = class extends XrayCommonClass { 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, + }; + } +};
\ No newline at end of file diff --git a/web/assets/js/util/utils.js b/web/assets/js/util/utils.js index 8bab58ec..61b322bd 100644 --- a/web/assets/js/util/utils.js +++ b/web/assets/js/util/utils.js @@ -295,3 +295,190 @@ class ObjectUtil { 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) + }; + } +}
\ No newline at end of file diff --git a/web/html/xui/form/inbound.html b/web/html/xui/form/inbound.html index 5da8ec56..b19be9ac 100644 --- a/web/html/xui/form/inbound.html +++ b/web/html/xui/form/inbound.html @@ -97,6 +97,11 @@ {{template "form/http"}} </template> +<!-- wireguard --> +<template v-if="inbound.protocol === Protocols.WIREGUARD"> + {{template "form/wireguard"}} +</template> + <!-- stream settings --> <template v-if="inbound.canEnableStream()"> {{template "form/streamSettings"}} diff --git a/web/html/xui/form/protocol/wireguard.html b/web/html/xui/form/protocol/wireguard.html new file mode 100644 index 00000000..553f5d42 --- /dev/null +++ b/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}}
\ No newline at end of file diff --git a/web/translation/translate.en_US.toml b/web/translation/translate.en_US.toml index 0812ca6e..90355e39 100644 --- a/web/translation/translate.en_US.toml +++ b/web/translation/translate.en_US.toml @@ -420,6 +420,14 @@ "portal" = "Portal" "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] "admin" = "Admin" "secret" = "Secret Token" diff --git a/web/translation/translate.es_ES.toml b/web/translation/translate.es_ES.toml index 9a1f7caf..722e2dd8 100644 --- a/web/translation/translate.es_ES.toml +++ b/web/translation/translate.es_ES.toml @@ -420,6 +420,14 @@ "portal" = "portal" "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] "admin" = "Administrador" "secret" = "Token Secreto" diff --git a/web/translation/translate.fa_IR.toml b/web/translation/translate.fa_IR.toml index 09220cdd..1a378dfb 100644 --- a/web/translation/translate.fa_IR.toml +++ b/web/translation/translate.fa_IR.toml @@ -420,6 +420,14 @@ "portal" = "پرتال" "intercon" = "اتصال میانی" +[pages.xray.wireguard] +"secretKey" = "کلید شخصی" +"publicKey" = "کلید عمومی" +"allowedIPs" = "آیپیهای مجاز" +"endpoint" = "نقطه پایانی" +"psk" = "کلید مشترک" +"domainStrategy" = "استراتژی حل دامنه" + [pages.settings.security] "admin" = "مدیر" "secret" = "توکن امنیتی" diff --git a/web/translation/translate.ru_RU.toml b/web/translation/translate.ru_RU.toml index 00faa053..2d03ee35 100644 --- a/web/translation/translate.ru_RU.toml +++ b/web/translation/translate.ru_RU.toml @@ -420,6 +420,14 @@ "portal" = "Портал" "intercon" = "Соединение" +[pages.xray.wireguard] +"secretKey" = "Секретный ключ" +"publicKey" = "Открытый ключ" +"allowedIPs" = "Разрешенные IP-адреса" +"endpoint" = "Конечная точка" +"psk" = "Общий ключ" +"domainStrategy" = "Стратегия домена" + [pages.settings.security] "admin" = "Админ" "secret" = "Секретный токен" diff --git a/web/translation/translate.vi_VN.toml b/web/translation/translate.vi_VN.toml index f7e4de7a..20027dfa 100644 --- a/web/translation/translate.vi_VN.toml +++ b/web/translation/translate.vi_VN.toml @@ -420,6 +420,14 @@ "portal" = "Cổng thông tin"
"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]
"admin" = "Quản trị viên"
"secret" = "Mã thông báo bí mật"
diff --git a/web/translation/translate.zh_Hans.toml b/web/translation/translate.zh_Hans.toml index c790da6d..db1217a8 100644 --- a/web/translation/translate.zh_Hans.toml +++ b/web/translation/translate.zh_Hans.toml @@ -420,6 +420,14 @@ "portal" = "门户" "intercon" = "互连" +[pages.xray.wireguard] +"secretKey" = "密钥" +"publicKey" = "公钥" +"allowedIPs" = "允许的 IP" +"endpoint" = "终点" +"psk" = "共享密钥" +"domainStrategy" = "域策略" + [pages.settings.security] "admin" = "管理员" "secret" = "密钥" |
