diff options
| author | Serge Pavlyuk <flops@users.noreply.github.com> | 2024-01-01 16:03:43 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-01-01 16:03:43 +0300 |
| commit | b725ea7de5ed73a36a9cd2c23bb247ca6b587573 (patch) | |
| tree | 3abb80ee3bcf869a675bee1a1275bab8501fcbeb | |
| parent | 0a4c8ffcf53e85946088bd09e90140248cd7136b (diff) | |
Outboud wireguard protocol support (#1451)
* Wireguard outbound settings modal window
* wireguard optional fields saniteze fix
* wireguard save domainStrategy and reserved(not implemented in form but will work)
---------
Co-authored-by: Сергей Павлюк <spavlyuk@nic.ru>
| -rw-r--r-- | web/assets/js/model/outbound.js | 112 | ||||
| -rw-r--r-- | web/html/xui/form/outbound.html | 82 |
2 files changed, 194 insertions, 0 deletions
diff --git a/web/assets/js/model/outbound.js b/web/assets/js/model/outbound.js index eb5b8c3b..2685488f 100644 --- a/web/assets/js/model/outbound.js +++ b/web/assets/js/model/outbound.js @@ -8,6 +8,7 @@ const Protocols = { Shadowsocks: "shadowsocks", Socks: "socks", HTTP: "http", + Wireguard: "wireguard", }; const SSMethods = { @@ -53,11 +54,20 @@ const outboundDomainStrategies = [ "UseIPv6" ] +const WireguardDomainStrategy = [ + "ForceIP", + "ForceIPv4", + "ForceIPv4v6", + "ForceIPv6", + "ForceIPv6v4" +]; + Object.freeze(Protocols); Object.freeze(SSMethods); Object.freeze(TLS_FLOW_CONTROL); Object.freeze(ALPN_OPTION); Object.freeze(outboundDomainStrategies); +Object.freeze(WireguardDomainStrategy); class CommonClass { @@ -625,6 +635,7 @@ Outbound.Settings = class extends CommonClass { case Protocols.Shadowsocks: return new Outbound.ShadowsocksSettings(); case Protocols.Socks: return new Outbound.SocksSettings(); case Protocols.HTTP: return new Outbound.HttpSettings(); + case Protocols.Wireguard: return new Outbound.WireguardSettings(); default: return null; } } @@ -640,6 +651,7 @@ Outbound.Settings = class extends CommonClass { case Protocols.Shadowsocks: return Outbound.ShadowsocksSettings.fromJson(json); case Protocols.Socks: return Outbound.SocksSettings.fromJson(json); case Protocols.HTTP: return Outbound.HttpSettings.fromJson(json); + case Protocols.Wireguard: return Outbound.WireguardSettings.fromJson(json); default: return null; } } @@ -838,6 +850,106 @@ Outbound.ShadowsocksSettings = class extends CommonClass { }; } }; +Outbound.WireguardSettings = class extends CommonClass { + constructor(secretKey, address, peers, mtu, workers, domainStrategy, reserved) { + super(); + this.secretKey = secretKey || ''; + this.address = address ? [...address] : []; + this.peers = peers ? peers.map((p) => ({ + ...p, + allowedIPs: p.allowedIPs ? [...p.allowedIPs] : [], + })) : []; + this.mtu = mtu; + this.workers = workers; + this.domainStrategy = domainStrategy; + this.reserved = reserved; + } + + static fromJson(json={}) { + return new Outbound.WireguardSettings( + json.secretKey, + json.address, + json.peers, + json.mtu, + json.workers, + json.domainStrategy, + json.reserved, + ); + } + + addAddress() { + this.address.push(''); + } + + delAddress(index) { + this.address.splice(index, 1); + } + + addPeer() { + this.peers.push({ + endpoint: '', + publicKey: '', + allowedIPs: [], + }); + } + + delPeer(index) { + this.peers.splice(index, 1); + } + + addAllowedIP(index) { + if (!this.peers[index].allowedIPs) { + this.peers[index].allowedIPs = []; + } + + this.peers[index].allowedIPs.push(''); + } + + delAllowedIP(index, ipIndex) { + this.peers[index].allowedIPs.splice(ipIndex, 1); + } + + optionalFields = ['mtu', 'workers', 'domainStrategy', 'address', 'reserved']; + optionalPeerFields = ['allowedIPs', 'keepAlive', 'preSharedKey']; + + cleanUpOptionalFields(obj) { + const isEmpty = (v) => ObjectUtil.isEmpty(v) || ObjectUtil.isArrEmpty(v); + + return Object.entries(obj).reduce((memo, [key, value]) => { + if (key === 'peers') { + memo[key] = value.map((peer) => { + return Object.entries(peer).reduce((pMemo, [pKey, pValue]) => { + if (this.optionalPeerFields.includes(pKey) && isEmpty(pValue)) { + return pMemo; + } + + pMemo[pKey] = pValue; + return pMemo; + }, {}); + }); + } else if (this.optionalFields.includes(key) && isEmpty(value)) { + return memo; + } else { + memo[key] = value; + } + + return memo; + }, {}); + } + + toJson() { + return this.cleanUpOptionalFields({ + secretKey: this.secretKey, + address: this.address, + peers: this.peers, + mtu: this.mtu, + workers: this.workers, + domainStrategy: this.domainStrategy, + reserved: this.reserved, + }); + } +}; + Outbound.SocksSettings = class extends CommonClass { constructor(address, port, user, pass) { super(); diff --git a/web/html/xui/form/outbound.html b/web/html/xui/form/outbound.html index 590a1a93..df54395f 100644 --- a/web/html/xui/form/outbound.html +++ b/web/html/xui/form/outbound.html @@ -44,6 +44,88 @@ </template> </template> +<!-- wireguard settings --> +<template v-if="outbound.protocol === Protocols.Wireguard"> + <a-form-item label='Secret Key'> + <a-input v-model.trim="outbound.settings.secretKey"></a-input> + </a-form-item> + + <a-form-item label="Address"> + <a-row> + <a-button size="small" @click="outbound.settings.addAddress()"> + + + </a-button> + </a-row> + + <a-space direction="vertical"> + <a-input-group compact v-for="(address, index) in outbound.settings.address"> + <a-input v-model.trim="outbound.settings.address[index]" style="width: calc(100% - 40px)"></a-input> + <a-button type="delete" @click="outbound.settings.delAddress(index)">-</a-button> + </a-input-group> + </a-space> + </a-form-item> + + <a-form-item label='MTU'> + <a-input placeholder="1420" type="number" min="0" v-model.number="outbound.settings.mtu"></a-input> + </a-form-item> + + <a-form-item label='Workers'> + <a-input type="number" min="0" v-model.number="outbound.settings.workers"></a-input> + </a-form-item> + + <a-form-item label='Domain Strategy'> + <a-select + v-model="outbound.settings.domainStrategy" + :dropdown-class-name="themeSwitcher.currentTheme"> + <a-select-option v-for="s in ['', ...WireguardDomainStrategy]" :value="s">[[ s ]]</a-select-option> + </a-select> + </a-form-item> + + <a-form-item label="Peers"> + <a-row> + <a-button type="primary" size="small" + @click="outbound.settings.addPeer()"> + + + </a-button> + </a-row> + </a-form-item> + + <a-form v-for="(peer, index) in outbound.settings.peers" :colon="false" :label-col="{ md: {span:8} }" :wrapper-col="{ md: {span:14} }"> + <a-divider style="margin:0;"> + Peer [[ index + 1 ]] + <a-icon type="delete" @click="() => outbound.settings.delPeer(index)" + style="color: rgb(255, 77, 79);cursor: pointer;"/> + </a-divider> + + <a-form-item label='Endpoint'> + <a-input v-model.trim="peer.endpoint"></a-input> + </a-form-item> + <a-form-item label='Public Key'> + <a-input v-model.trim="peer.publicKey"></a-input> + </a-form-item> + <a-form-item label='PreShared Key'> + <a-input v-model.trim="peer.preSharedKey"></a-input> + </a-form-item> + <a-form-item label='keepAlive'> + <a-input type="number" min="0" v-model.number="peer.keepAlive"></a-input> + </a-form-item> + + <a-form-item label="Allowed IPs"> + <a-row> + <a-button size="small" @click="outbound.settings.addAllowedIP(index)"> + + + </a-button> + </a-row> + <a-space direction="vertical"> + <a-input-group compact v-for="(allowedIp, ipIndex) in peer.allowedIPs"> + <a-input v-model.trim="peer.allowedIPs[ipIndex]" style="width: calc(100% - 40px)"></a-input> + <a-button type="delete" @click="outbound.settings.delAllowedIP(index, ipIndex)">-</a-button> + </a-input-group> + </a-space> + </a-form-item> + </a-form> +</template> + <!-- blackhole settings --> <template v-if="outbound.protocol === Protocols.Blackhole"> <a-form-item label='Response Type'> |
