From 00f3fff15ee2a5e5b4d27a32b6b02e897eb0a697 Mon Sep 17 00:00:00 2001
From: crazyracer98 <8631139+crazyracer98@users.noreply.github.com>
Date: Wed, 26 Jan 2022 22:31:11 +0100
Subject: [PATCH] add IPv6 support
---
Dockerfile | 3 ++-
docker-compose.yml | 22 +++++++++++++++++++-
src/config.js | 9 ++++++++
src/lib/Server.js | 5 +++++
src/lib/Util.js | 7 +++++++
src/lib/WireGuard.js | 49 ++++++++++++++++++++++++++++++++++++++++----
src/www/index.html | 25 ++++++++++++++++++++++
src/www/js/api.js | 7 +++++++
src/www/js/app.js | 7 +++++++
9 files changed, 128 insertions(+), 6 deletions(-)
diff --git a/Dockerfile b/Dockerfile
index 2695361f..e3c1b71b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -36,7 +36,8 @@ RUN npm i -g nodemon
# Install Linux packages
RUN apk add -U --no-cache \
wireguard-tools \
- dumb-init
+ dumb-init \
+ ip6tables
# Expose Ports
EXPOSE 51820/udp
diff --git a/docker-compose.yml b/docker-compose.yml
index 0a13accc..a492de47 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,4 +1,5 @@
version: "3.8"
+
services:
wg-easy:
environment:
@@ -10,12 +11,18 @@ services:
# - PASSWORD=foobar123
# - WG_PORT=51820
# - WG_DEFAULT_ADDRESS=10.8.0.x
- # - WG_DEFAULT_DNS=1.1.1.1
+ # - WG_DEFAULT_ADDRESS6=fd42:beef::x
+ # - WG_DEFAULT_DNS=1.0.0.1
+ # - WG_DEFAULT_DNS6=2606:4700:4700::1001
# - WG_MTU=1420
# - WG_ALLOWED_IPS=192.168.15.0/24, 10.0.1.0/24
image: weejewel/wg-easy
container_name: wg-easy
+ networks:
+ wg:
+ ipv4_address: 10.42.42.42
+ ipv6_address: fd00:42::42
volumes:
- .:/etc/wireguard
ports:
@@ -28,3 +35,16 @@ services:
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.src_valid_mark=1
+ - net.ipv6.conf.all.disable_ipv6=0
+ - net.ipv6.conf.all.forwarding=1
+ - net.ipv6.conf.default.forwarding=1
+
+networks:
+ wg:
+ driver: bridge
+ enable_ipv6: true
+ ipam:
+ driver: default
+ config:
+ - subnet: 10.42.42.0/24
+ - subnet: fd00:42::/120
diff --git a/src/config.js b/src/config.js
index a08aab3b..72cb282c 100644
--- a/src/config.js
+++ b/src/config.js
@@ -11,9 +11,13 @@ module.exports.WG_PORT = process.env.WG_PORT || 51820;
module.exports.WG_MTU = process.env.WG_MTU || null;
module.exports.WG_PERSISTENT_KEEPALIVE = process.env.WG_PERSISTENT_KEEPALIVE || 0;
module.exports.WG_DEFAULT_ADDRESS = process.env.WG_DEFAULT_ADDRESS || '10.8.0.x';
+module.exports.WG_DEFAULT_ADDRESS6 = process.env.WG_DEFAULT_ADDRESS6 || 'fd80:cafe::x';
module.exports.WG_DEFAULT_DNS = typeof process.env.WG_DEFAULT_DNS === 'string'
? process.env.WG_DEFAULT_DNS
: '1.1.1.1';
+module.exports.WG_DEFAULT_DNS6 = typeof process.env.WG_DEFAULT_DNS6 === 'string'
+ ? process.env.WG_DEFAULT_DNS6
+ : '2606:4700:4700::1111';
module.exports.WG_ALLOWED_IPS = process.env.WG_ALLOWED_IPS || '0.0.0.0/0, ::/0';
module.exports.WG_POST_UP = process.env.WG_POST_UP || `
@@ -21,6 +25,11 @@ iptables -t nat -A POSTROUTING -s ${module.exports.WG_DEFAULT_ADDRESS.replace('x
iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT;
iptables -A FORWARD -i wg0 -j ACCEPT;
iptables -A FORWARD -o wg0 -j ACCEPT;
+ip6tables -t nat -A POSTROUTING -s ${module.exports.WG_DEFAULT_ADDRESS6.replace('x', '')}/120 -o eth0 -j MASQUERADE;
+ip6tables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT;
+ip6tables -A FORWARD -i wg0 -j ACCEPT;
+ip6tables -A FORWARD -o wg0 -j ACCEPT;
`.split('\n').join(' ');
+
module.exports.WG_POST_DOWN = process.env.WG_POST_DOWN || '';
diff --git a/src/lib/Server.js b/src/lib/Server.js
index e204fa5f..ee75f3a9 100644
--- a/src/lib/Server.js
+++ b/src/lib/Server.js
@@ -130,6 +130,11 @@ module.exports = class Server {
const { address } = req.body;
return WireGuard.updateClientAddress({ clientId, address });
}))
+ .put('/api/wireguard/client/:clientId/address6', Util.promisify(async req => {
+ const { clientId } = req.params;
+ const { address6 } = req.body;
+ return WireGuard.updateClientAddress6({ clientId, address6 });
+ }))
.listen(PORT, () => {
debug(`Listening on http://0.0.0.0:${PORT}`);
diff --git a/src/lib/Util.js b/src/lib/Util.js
index 2a47a20e..758f0751 100644
--- a/src/lib/Util.js
+++ b/src/lib/Util.js
@@ -17,6 +17,13 @@ module.exports = class Util {
return true;
}
+ static isValidIPv6(str) {
+ // Regex source : https://stackoverflow.com/a/17871737
+ const regex = new RegExp('(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))');
+ const matches = str.match(regex);
+ return !!matches;
+ }
+
static promisify(fn) {
// eslint-disable-next-line func-names
return function(req, res) {
diff --git a/src/lib/WireGuard.js b/src/lib/WireGuard.js
index 032854f1..5285f261 100644
--- a/src/lib/WireGuard.js
+++ b/src/lib/WireGuard.js
@@ -16,7 +16,9 @@ const {
WG_PORT,
WG_MTU,
WG_DEFAULT_DNS,
+ WG_DEFAULT_DNS6,
WG_DEFAULT_ADDRESS,
+ WG_DEFAULT_ADDRESS6,
WG_PERSISTENT_KEEPALIVE,
WG_ALLOWED_IPS,
WG_POST_UP,
@@ -44,12 +46,14 @@ module.exports = class WireGuard {
log: 'echo ***hidden*** | wg pubkey',
});
const address = WG_DEFAULT_ADDRESS.replace('x', '1');
+ const address6 = WG_DEFAULT_ADDRESS6.replace('x', '1');
config = {
server: {
privateKey,
publicKey,
address,
+ address6,
},
clients: {},
};
@@ -69,6 +73,10 @@ module.exports = class WireGuard {
// await Util.exec('iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT');
// await Util.exec('iptables -A FORWARD -i wg0 -j ACCEPT');
// await Util.exec('iptables -A FORWARD -o wg0 -j ACCEPT');
+ // await Util.exec(`ip6tables -t nat -A POSTROUTING -s ${WG_DEFAULT_ADDRESS6.replace('x', '')}/120 -o eth0 -j MASQUERADE`);
+ // await Util.exec('ip6tables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT');
+ // await Util.exec('ip6tables -A FORWARD -i wg0 -j ACCEPT');
+ // await Util.exec('ip6tables -A FORWARD -o wg0 -j ACCEPT');
await this.__syncConfig();
return config;
@@ -92,7 +100,7 @@ module.exports = class WireGuard {
# Server
[Interface]
PrivateKey = ${config.server.privateKey}
-Address = ${config.server.address}/24
+Address = ${config.server.address}/24, ${config.server.address6}/120
ListenPort = 51820
PostUp = ${WG_POST_UP}
PostDown = ${WG_POST_DOWN}
@@ -107,7 +115,7 @@ PostDown = ${WG_POST_DOWN}
[Peer]
PublicKey = ${client.publicKey}
PresharedKey = ${client.preSharedKey}
-AllowedIPs = ${client.address}/32`;
+AllowedIPs = ${client.address}/32, ${client.address6}/32`;
}
debug('Config saving...');
@@ -133,6 +141,7 @@ AllowedIPs = ${client.address}/32`;
name: client.name,
enabled: client.enabled,
address: client.address,
+ address6: client.address6,
publicKey: client.publicKey,
createdAt: new Date(client.createdAt),
updatedAt: new Date(client.updatedAt),
@@ -191,12 +200,14 @@ AllowedIPs = ${client.address}/32`;
async getClientConfiguration({ clientId }) {
const config = await this.getConfig();
const client = await this.getClient({ clientId });
+ const isDnsSet = WG_DEFAULT_DNS || WG_DEFAULT_DNS6;
+ const dnsServers = [WG_DEFAULT_DNS, WG_DEFAULT_DNS6].filter(item => !!item).join(', ')
return `
[Interface]
PrivateKey = ${client.privateKey}
-Address = ${client.address}/24
-${WG_DEFAULT_DNS ? `DNS = ${WG_DEFAULT_DNS}` : ''}
+Address = ${client.address}/24, ${client.address6}/120
+${isDnsSet ? `DNS = ${dnsServers}` : ''}
${WG_MTU ? `MTU = ${WG_MTU}` : ''}
[Peer]
@@ -243,11 +254,28 @@ Endpoint = ${WG_HOST}:${WG_PORT}`;
throw new Error('Maximum number of clients reached.');
}
+ let address6;
+ for (let i = 2; i < 255; i++) {
+ const client = Object.values(config.clients).find(client => {
+ return client.address6 === WG_DEFAULT_ADDRESS6.replace('x', i.toString(16));
+ });
+
+ if (!client) {
+ address6 = WG_DEFAULT_ADDRESS6.replace('x', i.toString(16));
+ break;
+ }
+ }
+
+ if (!address6) {
+ throw new Error('Maximum number of clients reached.');
+ }
+
// Create Client
const clientId = uuid.v4();
const client = {
name,
address,
+ address6,
privateKey,
publicKey,
preSharedKey,
@@ -311,5 +339,18 @@ Endpoint = ${WG_HOST}:${WG_PORT}`;
await this.saveConfig();
}
+
+ async updateClientAddress6({ clientId, address6 }) {
+ const client = await this.getClient({ clientId });
+
+ if (!Util.isValidIPv6(address6)) {
+ throw new ServerError(`Invalid Address6: ${address6}`, 400);
+ }
+
+ client.address6 = address6;
+ client.updatedAt = new Date();
+
+ await this.saveConfig();
+ }
};
diff --git a/src/www/index.html b/src/www/index.html
index 4080c51a..79813cda 100644
--- a/src/www/index.html
+++ b/src/www/index.html
@@ -153,6 +153,31 @@
+
+
+
+
+
+ {{client.address6}}
+
+
+ $refs['client-' + client.id + '-address6'][0].select(), 1);"
+ class="cursor-pointer opacity-0 group-hover:opacity-100 transition-opacity">
+
+
+
+
ยท
diff --git a/src/www/js/api.js b/src/www/js/api.js
index accbb579..4e41fd69 100644
--- a/src/www/js/api.js
+++ b/src/www/js/api.js
@@ -117,4 +117,11 @@ class API {
});
}
+ async updateClientAddress6({ clientId, address6 }) {
+ return this.call({
+ method: 'put',
+ path: `/wireguard/client/${clientId}/address6/`,
+ body: { address6 },
+ });
+ }
}
diff --git a/src/www/js/app.js b/src/www/js/app.js
index 137fb229..b4ff191c 100644
--- a/src/www/js/app.js
+++ b/src/www/js/app.js
@@ -43,6 +43,8 @@ new Vue({
clientEditNameId: null,
clientEditAddress: null,
clientEditAddressId: null,
+ clientEditAddress6: null,
+ clientEditAddress6Id: null,
qrcode: null,
currentRelease: null,
@@ -243,6 +245,11 @@ new Vue({
.catch(err => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error));
},
+ updateClientAddress6(client, address6) {
+ this.api.updateClientAddress6({ clientId: client.id, address6 })
+ .catch(err => alert(err.message || err.toString()))
+ .finally(() => this.refresh().catch(console.error));
+ },
},
filters: {
bytes,