'use strict'; const fs = require('fs').promises; const path = require('path'); const QRCode = require('qrcode'); const Util = require('./Util'); const ServerError = require('./ServerError'); const { WG_PATH, WG_HOST, WG_PORT, WG_DEFAULT_DNS, WG_DEFAULT_ADDRESS, } = require('../config'); module.exports = class WireGuard { async getConfig() { if (!this.__configPromise) { this.__configPromise = Promise.resolve().then(async () => { let config; try { config = await fs.readFile(path.join(WG_PATH, 'wg0.json'), 'utf8'); config = JSON.parse(config); } catch (err) { config = { server: { // TODO: Generate new config address: WG_DEFAULT_ADDRESS, dns: WG_DEFAULT_DNS, }, clients: {}, }; } return config; }); } return this.__configPromise; } async saveConfig() { const config = await this.getConfig(); let result = ` # Note: Do not edit this file directly. # Your changes will be overwritten! # Server [Interface] PrivateKey = ${config.server.privateKey} Address = ${config.server.address} ListenPort = ${config.server.port} DNS = ${config.server.dns}`; for (const [clientId, client] of Object.entries(config.clients)) { if (!client.enabled) continue; result += ` # Client: ${client.name} (${clientId}) [Peer] PublicKey = ${client.publicKey} PresharedKey = ${client.preSharedKey} AllowedIPs = ${client.allowedIPs}`; } await fs.writeFile(path.join(WG_PATH, 'wg0.json'), JSON.stringify(config, false, 2)); await fs.writeFile(path.join(WG_PATH, 'wg0.conf'), result); } async getClients() { const config = await this.getConfig(); const clients = Object.entries(config.clients).map(([clientId, client]) => ({ id: clientId, name: client.name, enabled: client.enabled, publicKey: client.publicKey, createdAt: new Date(client.createdAt), updatedAt: new Date(client.updatedAt), allowedIPs: client.allowedIPs, persistentKeepalive: null, latestHandshakeAt: null, transferRx: null, transferTx: null, })); // Loop WireGuard status const clientsDump = await Util.exec('wg show wg0 dump'); // const clientsDump = `iOQJS7OUUGPYATsX6nqlL+sOODoiWiN5IOE8Msfw/0o= BkdntwYazhYZzEEHhcYayq6TGw9/YUDQ251s+5bTgC0= 51820 off // i8xWKqicnDkNL14I4B+I1zlB8od/booA1joIosWn7X4= MzplKtOQ44/IaAKri2VKqCoIlg4XiVH7TCp5bcYRTQU= 172.17.0.1:60475 10.8.0.2/32 1621679257 7920 7440 off`; clientsDump .trim() .split('\n') .slice(1) .forEach(line => { const [ publicKey, preSharedKey, // eslint-disable-line no-unused-vars endpoint, allowedIps, // eslint-disable-line no-unused-vars latestHandshakeAt, transferRx, transferTx, persistentKeepalive, ] = line.split('\t'); const client = clients.find(client => client.publicKey === publicKey); if (!client) return; client.endpoint = endpoint === '(none)' ? null : endpoint; client.latestHandshakeAt = latestHandshakeAt === '0' ? null : new Date(Number(`${latestHandshakeAt}000`)); client.transferRx = Number(transferRx); client.transferTx = Number(transferTx); client.persistentKeepalive = persistentKeepalive; }); return clients; } async getClient({ clientId }) { const config = await this.getConfig(); const client = config.clients[clientId]; if (!client) { throw new ServerError(`Client Not Found: ${clientId}`, 404); } return client; } async getClientConfiguration({ clientId }) { const config = await this.getConfig(); const client = await this.getClient({ clientId }); return ` [Interface] PrivateKey = ${client.privateKey} Address = ${client.address} DNS = ${config.server.dns} [Peer] PublicKey = ${client.publicKey} PresharedKey = ${client.preSharedKey} AllowedIPs = ${client.allowedIPs} Endpoint = ${WG_HOST}:${WG_PORT}`; } async getClientQRCodeSVG({ clientId }) { const config = await this.getClientConfiguration({ clientId }); return QRCode.toString(config, { type: 'svg', width: 512, }); } async createClient({ name }) { if (!name) { throw new Error('Missing: Name'); } // try { // await this.getClient({ name }); // throw new Error(`Duplicate Client: ${name}`); // } catch( err ) { // if( err.message.startsWith('Duplicate Client') ) { // throw err; // } // } // // TODO: This is unsafe // await this.ssh.exec(`pivpn add -n ${name}`); // return this.getClient({ name }); await this.saveConfig(); } async deleteClient({ clientId }) { const config = await this.getConfig(); if (config.clients[clientId]) { delete config.clients[clientId]; await this.saveConfig(); } } async enableClient({ clientId }) { const client = await this.getClient({ clientId }); client.enabled = true; await this.saveConfig(); } async disableClient({ clientId }) { const client = await this.getClient({ clientId }); client.enabled = false; await this.saveConfig(); } };