This commit is contained in:
Emile Nijssen 2021-05-22 23:29:01 +02:00
parent 650128cacb
commit 18b6ce7c78
8 changed files with 77 additions and 37 deletions

View File

@ -7,10 +7,15 @@
PrivateKey = iOQJS7OUUGPYATsX6nqlL+sOODoiWiN5IOE8Msfw/0o= PrivateKey = iOQJS7OUUGPYATsX6nqlL+sOODoiWiN5IOE8Msfw/0o=
Address = 10.8.0.1/24 Address = 10.8.0.1/24
ListenPort = 51820 ListenPort = 51820
DNS = 1.1.1.1
# Client: Emile (af3111a4-7343-4380-a293-ed498d9aa3b8) # Client: Emile (af3111a4-7343-4380-a293-ed498d9aa3b8)
[Peer] [Peer]
PublicKey = i8xWKqicnDkNL14I4B+I1zlB8od/booA1joIosWn7X4= PublicKey = i8xWKqicnDkNL14I4B+I1zlB8od/booA1joIosWn7X4=
PresharedKey = MzplKtOQ44/IaAKri2VKqCoIlg4XiVH7TCp5bcYRTQU= PresharedKey = MzplKtOQ44/IaAKri2VKqCoIlg4XiVH7TCp5bcYRTQU=
AllowedIPs = 10.8.0.2/32 AllowedIPs = 10.8.0.2/32
# Client: Test 2 (c3ff2018-b2a8-4276-a16e-788e9a7e1aa6)
[Peer]
PublicKey =
PresharedKey =
AllowedIPs = 10.8.0.4/32

View File

@ -2,7 +2,7 @@
"server": { "server": {
"privateKey": "iOQJS7OUUGPYATsX6nqlL+sOODoiWiN5IOE8Msfw/0o=", "privateKey": "iOQJS7OUUGPYATsX6nqlL+sOODoiWiN5IOE8Msfw/0o=",
"publicKey": "BkdntwYazhYZzEEHhcYayq6TGw9/YUDQ251s+5bTgC0=", "publicKey": "BkdntwYazhYZzEEHhcYayq6TGw9/YUDQ251s+5bTgC0=",
"address": "10.8.0.1/24", "address": "10.8.0.1",
"port": "51820", "port": "51820",
"dns": "1.1.1.1" "dns": "1.1.1.1"
}, },
@ -14,8 +14,17 @@
"privateKey": "sHUUDbaZBQshfOvvF8HeebhhXq3rDKWlW1Vm+6XMklU=", "privateKey": "sHUUDbaZBQshfOvvF8HeebhhXq3rDKWlW1Vm+6XMklU=",
"publicKey": "i8xWKqicnDkNL14I4B+I1zlB8od/booA1joIosWn7X4=", "publicKey": "i8xWKqicnDkNL14I4B+I1zlB8od/booA1joIosWn7X4=",
"preSharedKey": "MzplKtOQ44/IaAKri2VKqCoIlg4XiVH7TCp5bcYRTQU=", "preSharedKey": "MzplKtOQ44/IaAKri2VKqCoIlg4XiVH7TCp5bcYRTQU=",
"address": "10.8.0.2/32", "address": "10.8.0.2",
"allowedIPs": "10.8.0.2/32", "enabled": true
},
"c3ff2018-b2a8-4276-a16e-788e9a7e1aa6": {
"name": "Test 2",
"address": "10.8.0.4",
"privateKey": "",
"publicKey": "",
"preSharedKey": "",
"createdAt": "2021-05-22T21:26:28.552Z",
"updatedAt": "2021-05-22T21:26:28.552Z",
"enabled": true "enabled": true
} }
} }

View File

@ -5,5 +5,5 @@ module.exports.PASSWORD = process.env.PASSWORD || 'wireguard';
module.exports.WG_PATH = process.env.WG_PATH || '/etc/wireguard/'; module.exports.WG_PATH = process.env.WG_PATH || '/etc/wireguard/';
module.exports.WG_HOST = process.env.WG_HOST || '127.0.0.1'; module.exports.WG_HOST = process.env.WG_HOST || '127.0.0.1';
module.exports.WG_PORT = process.env.WG_PORT || 51820; module.exports.WG_PORT = process.env.WG_PORT || 51820;
module.exports.WG_DEFAULT_ADDRESS = process.env.WG_DEFAULT_ADDRESS || '10.6.0.1/32'; module.exports.WG_DEFAULT_ADDRESS = process.env.WG_DEFAULT_ADDRESS || '10.8.0.x';
module.exports.WG_DEFAULT_DNS = process.env.WG_DEFAULT_DNS || '1.1.1.1'; module.exports.WG_DEFAULT_DNS = process.env.WG_DEFAULT_DNS || '1.1.1.1';

View File

@ -1,8 +1,6 @@
'use strict'; 'use strict';
const { rejects } = require('assert');
const childProcess = require('child_process'); const childProcess = require('child_process');
const { resolve } = require('path');
module.exports = class Util { module.exports = class Util {

View File

@ -3,6 +3,7 @@
const fs = require('fs').promises; const fs = require('fs').promises;
const path = require('path'); const path = require('path');
const uuid = require('uuid');
const QRCode = require('qrcode'); const QRCode = require('qrcode');
const Util = require('./Util'); const Util = require('./Util');
@ -28,9 +29,8 @@ module.exports = class WireGuard {
} catch (err) { } catch (err) {
config = { config = {
server: { server: {
// TODO: Generate new config privateKey: await Util.exec('wg genkey'),
address: WG_DEFAULT_ADDRESS, address: `${WG_DEFAULT_ADDRESS.replace('x', '1')}/24`,
dns: WG_DEFAULT_DNS,
}, },
clients: {}, clients: {},
}; };
@ -52,9 +52,8 @@ module.exports = class WireGuard {
# Server # Server
[Interface] [Interface]
PrivateKey = ${config.server.privateKey} PrivateKey = ${config.server.privateKey}
Address = ${config.server.address} Address = ${config.server.address}/24
ListenPort = ${config.server.port} ListenPort = 51820`;
DNS = ${config.server.dns}`;
for (const [clientId, client] of Object.entries(config.clients)) { for (const [clientId, client] of Object.entries(config.clients)) {
if (!client.enabled) continue; if (!client.enabled) continue;
@ -65,7 +64,7 @@ DNS = ${config.server.dns}`;
[Peer] [Peer]
PublicKey = ${client.publicKey} PublicKey = ${client.publicKey}
PresharedKey = ${client.preSharedKey} PresharedKey = ${client.preSharedKey}
AllowedIPs = ${client.allowedIPs}`; AllowedIPs = ${client.address}/32`;
} }
await fs.writeFile(path.join(WG_PATH, 'wg0.json'), JSON.stringify(config, false, 2)); await fs.writeFile(path.join(WG_PATH, 'wg0.json'), JSON.stringify(config, false, 2));
@ -78,6 +77,7 @@ AllowedIPs = ${client.allowedIPs}`;
id: clientId, id: clientId,
name: client.name, name: client.name,
enabled: client.enabled, enabled: client.enabled,
address: client.address,
publicKey: client.publicKey, publicKey: client.publicKey,
createdAt: new Date(client.createdAt), createdAt: new Date(client.createdAt),
updatedAt: new Date(client.updatedAt), updatedAt: new Date(client.updatedAt),
@ -90,10 +90,8 @@ AllowedIPs = ${client.allowedIPs}`;
})); }));
// Loop WireGuard status // Loop WireGuard status
const clientsDump = await Util.exec('wg show wg0 dump'); const dump = await Util.exec('wg show wg0 dump');
// const clientsDump = `iOQJS7OUUGPYATsX6nqlL+sOODoiWiN5IOE8Msfw/0o= BkdntwYazhYZzEEHhcYayq6TGw9/YUDQ251s+5bTgC0= 51820 off dump
// i8xWKqicnDkNL14I4B+I1zlB8od/booA1joIosWn7X4= MzplKtOQ44/IaAKri2VKqCoIlg4XiVH7TCp5bcYRTQU= 172.17.0.1:60475 10.8.0.2/32 1621679257 7920 7440 off`;
clientsDump
.trim() .trim()
.split('\n') .split('\n')
.slice(1) .slice(1)
@ -137,19 +135,18 @@ AllowedIPs = ${client.allowedIPs}`;
} }
async getClientConfiguration({ clientId }) { async getClientConfiguration({ clientId }) {
const config = await this.getConfig();
const client = await this.getClient({ clientId }); const client = await this.getClient({ clientId });
return ` return `
[Interface] [Interface]
PrivateKey = ${client.privateKey} PrivateKey = ${client.privateKey}
Address = ${client.address} Address = ${client.address}/24
DNS = ${config.server.dns} DNS = ${WG_DEFAULT_DNS}
[Peer] [Peer]
PublicKey = ${client.publicKey} PublicKey = ${client.publicKey}
PresharedKey = ${client.preSharedKey} PresharedKey = ${client.preSharedKey}
AllowedIPs = ${client.allowedIPs} AllowedIPs = 0.0.0.0/0, ::/0
Endpoint = ${WG_HOST}:${WG_PORT}`; Endpoint = ${WG_HOST}:${WG_PORT}`;
} }
@ -166,19 +163,45 @@ Endpoint = ${WG_HOST}:${WG_PORT}`;
throw new Error('Missing: Name'); throw new Error('Missing: Name');
} }
// try { const config = await this.getConfig();
// await this.getClient({ name });
// throw new Error(`Duplicate Client: ${name}`);
// } catch( err ) {
// if( err.message.startsWith('Duplicate Client') ) {
// throw err;
// }
// }
// // TODO: This is unsafe const privateKey = await Util.exec('wg genkey');
// await this.ssh.exec(`pivpn add -n ${name}`); const publicKey = await Util.exec(`echo ${privateKey} | wg pubkey`);
const preSharedKey = await Util.exec('wg genpsk');
// return this.getClient({ name }); // Calculate next IP
let address;
for (let i = 2; i < 255; i++) {
const client = Object.values(config.clients).find(client => {
return client.address === WG_DEFAULT_ADDRESS.replace('x', i);
});
if (!client) {
address = WG_DEFAULT_ADDRESS.replace('x', i);
break;
}
}
if (!address) {
throw new Error('Maximum number of clients reached.');
}
// Create Client
const clientId = uuid.v4();
const client = {
name,
address,
privateKey,
publicKey,
preSharedKey,
createdAt: new Date(),
updatedAt: new Date(),
enabled: true,
};
config.clients[clientId] = client;
await this.saveConfig(); await this.saveConfig();
} }
@ -194,14 +217,18 @@ Endpoint = ${WG_HOST}:${WG_PORT}`;
async enableClient({ clientId }) { async enableClient({ clientId }) {
const client = await this.getClient({ clientId }); const client = await this.getClient({ clientId });
client.enabled = true; client.enabled = true;
client.updatedAt = new Date();
await this.saveConfig(); await this.saveConfig();
} }
async disableClient({ clientId }) { async disableClient({ clientId }) {
const client = await this.getClient({ clientId }); const client = await this.getClient({ clientId });
client.enabled = false; client.enabled = false;
client.updatedAt = new Date();
await this.saveConfig(); await this.saveConfig();
} }

1
src/www/css/vendor/tailwind.min.css vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -3,7 +3,7 @@
<head> <head>
<title>WireGuard</title> <title>WireGuard</title>
<link href="https://unpkg.com/tailwindcss@^2/dist/tailwind.min.css" rel="stylesheet"> <link href="css/vendor/tailwind.min.css" rel="stylesheet">
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
<link rel="icon" type="image/png" href="img/favicon.png"> <link rel="icon" type="image/png" href="img/favicon.png">
<link rel="apple-touch-icon" href="img/apple-touch-icon.png"> <link rel="apple-touch-icon" href="img/apple-touch-icon.png">
@ -61,7 +61,7 @@
<div class="flex-grow"> <div class="flex-grow">
<div class="text-gray-700" :title="'Created at ' + dateTime(new Date(client.createdAt))">{{client.name}} <div class="text-gray-700" :title="'Created at ' + dateTime(new Date(client.createdAt))">{{client.name}}
</div> </div>
<div v-if="client.allowedIPs" class="text-gray-300 text-xs">{{client.allowedIPs.split('/')[0]}} <div class="text-gray-300 text-xs">{{client.address}}
<span v-if="client.transferTx" title="Download"> <span v-if="client.transferTx" title="Download">
· ·
<svg class="align-middle h-3 inline" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" <svg class="align-middle h-3 inline" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"

View File

@ -84,7 +84,7 @@ new Vue({
.catch(err => alert(err.message || err.toString())) .catch(err => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error)); .finally(() => this.refresh().catch(console.error));
}, },
deleteClient(clientId) { deleteClient(client) {
this.api.deleteClient({ clientId: client.id }) this.api.deleteClient({ clientId: client.id })
.catch(err => alert(err.message || err.toString())) .catch(err => alert(err.message || err.toString()))
.finally(() => this.refresh().catch(console.error)); .finally(() => this.refresh().catch(console.error));