diff --git a/admin/add.js b/admin/add.js index 57ff9f9..5492dfd 100644 --- a/admin/add.js +++ b/admin/add.js @@ -1,5 +1,5 @@ 'use strict' -/* srv-add.js +/* admin/add.js * routes for intra-server peer sharing */ diff --git a/admin/del.js b/admin/del.js index 95458e9..169a6e2 100644 --- a/admin/del.js +++ b/admin/del.js @@ -1,12 +1,32 @@ 'use strict' -/* srv-del.js +/* admin/del.js * routes for intra-server peer sharing */ -const env = require('../env/env.json') +const env = require(process.argv[2]||'../env/env.json') const fs = require('fs').promises module.exports = async (req, res) => { - console.log(`Received delete from ${req.requester}`) - res.send(req.body) + console.log(`Received delete from ${req.requester} for ${req.body}`) + let config + try { + const config_file = await fs.readFile(env.WG_CONFIG_FILE) + config = config_file.toString() + } catch (err) { + console.error(`Failed to load config from ${env.WG_CONFIG_FILE}:\n`,err) + res.sendStatus(500) + } + const peer = config.split('\n\n') + .filter( (paragraph) => { + return paragraph.includes('[Peer]') + }).filter( (peer) => { + return peer.includes(`PublicKey = ${req.body}`) + }) + try { + await fs.writeFile(env.WG_CONFIG_FILE, config.replace(`\n\n${peer}`,'')) + } catch (err) { + console.error(`Failed to delete peer config from ${env.WG_CONFIG_FILE}:\n`,err) + res.sendStatus(500) + } + res.sendStatus(200) } \ No newline at end of file diff --git a/app/add.js b/app/add.js index 5ae3dc0..1badd4b 100644 --- a/app/add.js +++ b/app/add.js @@ -22,7 +22,7 @@ module.exports = async (req, res) => { const hostname = req.query['name'] if (!hostname) { console.log(`New peer request from ${req.requester} didn't provide a hostname`) - res.sendStatus(500); return + res.sendStatus(400); return } console.log(`New peer request from ${req.requester} for ${hostname}`) @@ -50,7 +50,7 @@ module.exports = async (req, res) => { // Check if host exists if (line.includes(`# ${hostname}.`)) { console.log(`Host already exists for ${hostname}`) - res.sendStatus(500); return + res.sendStatus(409); return } found_usernames.push(line.split('.').slice(-2,-1)[0]) } @@ -75,7 +75,7 @@ module.exports = async (req, res) => { // Should never get here because this req.requester can't access this IP! } else if (found_usernames.length ===0) { console.log(`Received request from ${req.requester} not in wg.conf!`) - res.sendStatus(500); return + res.sendStatus(403); return // Check that all usernames are correct or error out // https://stackoverflow.com/a/35568895 } else if (!found_usernames.every( (v,i,r) => v === r[0] )) { @@ -131,7 +131,8 @@ PresharedKey = ${psk} AllowedIPs = ${ipv4_addr}/32, ${ipv6_addr}/128` if (server.host===env.LOCAL_SERVER) { // Add server_config to wg0.conf - fs.appendFile(env.WG_CONFIG_FILE, server_config) + try { await fs.appendFile(env.WG_CONFIG_FILE, server_config) } + catch (err) { console.error(err); return} } else { // Send config to other server console.log(`Sending config to ${server.host}.gf4`) diff --git a/app/del.js b/app/del.js index c273d09..8640866 100644 --- a/app/del.js +++ b/app/del.js @@ -4,13 +4,101 @@ */ const fs = require('fs').promises -const env = require('../env/env.json') +const env = require(process.argv[2]||'../env/env.json') +const wg = require('../includes/wireguard') +const https = require('https') +// Construct axios in IIFE for async/await +let axios; (async()=>{ + try { + axios = require('axios').create({ + httpsAgent: new https.Agent({ + ca: await fs.readFile(env.CA_CERT_FILE), + }), + }) + } catch (err) { console.log(err) } +})() module.exports = async (req, res) => { - const privkey = req.query['del'] + console.log(`Received request from ${req.requester} to delete ${JSON.stringify(req.query)}`) + + // Load wg.conf and search for peers + let config_file + let peer_pubkey + try { config_file = await fs.readFile(env.WG_CONFIG_FILE) } + catch (err) { console.error(err); res.sendStatus(500); return } + const config = config_file.toString() + const peer = config.split('\n\n') + .filter( (paragraph) => { + return paragraph.includes('[Peer]') + }).filter( (peer) => { + // .filter() doesn't support async so use then/catch in this block + if (req.query['name']) { + return peer.includes(`[Peer] # ${req.query['name']}.`) + } else if (req.query['pubkey']) { + peer_pubkey = req.query['pubkey'] + return peer.includes(`PublicKey = ${req.query['pubkey']}`) + } else if (req.query['psk']) { + return peer.includes(`PresharedKey = ${req.query['psk']}`) + } else if (req.query['ip']) { + return peer.split('\n').some( (line) => ( + line.includes('AllowedIPs') && + line.includes(` ${req.query['ip']}/`) + ) ) + } else if (req.query['privkey']) { + wg.getPubkeyFromPrivkey(req.query['privkey']) + .then((pubkey) => { + peer_pubkey = pubkey + return peer.includes(`PublicKey = ${pubkey}`) + }) + .catch((err) => { + console.error(`Failed to generate public key from private key during delete request\n`,err) + res.sendStatus(500); return + }) + } else { + console.error(`${req.requester} sent delete request without specifying a peer`) + res.sendStatus(400); return + } + })[0] + if (peer===undefined) { + console.log(`No peer found for delete request from ${req.requester}`) + res.sendStatus(404); return + } - console.log(`Received request from ${req.requester} to delete ${privkey}`) - res.setHeader('content-type', 'text/plain') - res.send(`Delete ${privkey}`) + // Parse peer + const peer_lines = peer.split('\n') + const peer_name = peer_lines + .filter( (line) => line.includes('[Peer] # ') )[0] + .split(' # ')[1] + if (peer_pubkey===undefined) { + peer_pubkey = peer_lines + .filter( (line) => line.includes('PublicKey = ') )[0] + .split(' = ')[1] + } + + // Delete from config + console.log(`Deleting ${peer_name}`); try { + await fs.writeFile(env.WG_CONFIG_FILE, config.replace(`\n\n${peer}`,'')) + } catch (err) { + console.error(`Failed to delete ${peer_name}:\n`,err) + res.sendStatus(500); return + } + + // Inform other servers + for (const server of env.SERVERS) { + if (server.host!==env.LOCAL_SERVER) { + try { + console.log(`Informing ${server.host} to delete ${peer_name}`) + await axios.post(`${server.admin_endpoint}/del`, peer_pubkey, { + headers: {'Content-Type': 'text/plain'}, + }) + } catch (err) { + console.error(`Failed to inform ${server.host} to delete ${peer_name}:\n\n`,err) + res.sendStatus(500); return + } + } + } + + // Inform user that delete was successful + res.sendStatus(200) } \ No newline at end of file diff --git a/includes/wireguard.js b/includes/wireguard.js index be6e457..4e82858 100644 --- a/includes/wireguard.js +++ b/includes/wireguard.js @@ -162,23 +162,32 @@ const keyToBase64 = (key) => { } module.exports = { + generateKeypair: async () => { let privateKey, publicKey try { privateKey = await generatePrivateKey() - } catch (err) { console.log(err) } + } catch (err) { console.error(err) } try { publicKey = await generatePublicKey(privateKey) - } catch (err) { console.log(err) } + } catch (err) { console.error(err) } return [ keyToBase64(publicKey), keyToBase64(privateKey), ] }, + generatePSK: async () => { try { return keyToBase64(await generatePresharedKey()) - } catch (err) { console.log(err) } + } catch (err) { console.error(err); return } }, + + getPubkeyFromPrivkey: async (privKey) => { + try { + return await generatePublicKey(privKey) + } catch (err) { console.error(err); return } + }, + }