From 37fa81eda4c0e01d647b279c96683758e4f13411 Mon Sep 17 00:00:00 2001 From: wgapi Cloud9 Date: Thu, 21 Oct 2021 22:21:59 -0600 Subject: [PATCH] Code cleanup --- admin/add.js | 15 ++- admin/del.js | 55 +++++------ app/add.js | 6 +- app/del.js | 225 +++++++++++++++++++++----------------------- app/list.js | 28 +++--- includes/helpers.js | 35 ++++++- 6 files changed, 194 insertions(+), 170 deletions(-) diff --git a/admin/add.js b/admin/add.js index e88b339..a5c86f4 100644 --- a/admin/add.js +++ b/admin/add.js @@ -12,16 +12,23 @@ const local_secret = env.SERVERS.filter( )[0].secret module.exports = async (req, res) => { + + // Check secret auth if (req.query['secret']!==local_secret) { console.log(`Peer sent from ${req.requester} without correct secret querystring!`) return res.sendStatus(403) - } else try { + } + + // Add request body to wg config + try { await fs.appendFile(env.WG_CONFIG_FILE, req.body) } catch (err) { console.error(`Failed to save new peer config from ${req.requester} to ${env.WG_CONFIG_FILE}`,err) return res.sendStatus(500) - } finally { - console.log(`Saved new peer from ${req.requester}`) - return res.sendStatus(200) } + console.log(`Saved new peer from ${req.requester}`) + + // Respond + return res.sendStatus(200) + } \ No newline at end of file diff --git a/admin/del.js b/admin/del.js index 883fe03..39a9cfa 100644 --- a/admin/del.js +++ b/admin/del.js @@ -19,41 +19,38 @@ module.exports = async (req, res) => { if (req.query['secret']!==local_secret) { console.log(`Peer sent from ${req.requester} without correct secret querystring!`) return res.sendStatus(403) + } // Read config file - } else try { + else try { const config_file = await fs.readFile(env.WG_CONFIG_FILE) config = config_file.toString() - } catch (err) { + } + catch (err) { console.error(`Failed to load config from ${env.WG_CONFIG_FILE}:\n`,err) res.sendStatus(500) - } finally { - - // Find peer in config - const peer = config.split('\n\n') - .filter( (paragraph) => { - return paragraph.includes('[Peer]') - }).filter( (peer) => { - return peer.includes(`PublicKey = ${req.body}`) - }) - - // Delete peer - try { - await fs.writeFile(env.WG_CONFIG_FILE, - config.replace(`\n\n${peer}`,'') - .replace('\n\n\n','\n\n') - ) - } catch (err) { - console.error(`Failed to delete peer config from ${env.WG_CONFIG_FILE}:\n`,err) - return res.sendStatus(500) - } finally { - - // Success - console.log(`Saved new peer from ${req.requester}`) - return res.sendStatus(200) - - } - } + + // Find peer in config + const peer = config.split('\n\n') + .filter( (paragraph) => { + return paragraph.includes('[Peer]') + }).filter( (peer) => { + return peer.includes(`PublicKey = ${req.body}`) + }) + + // Delete peer + try { await fs.writeFile(env.WG_CONFIG_FILE, + config.replace(`\n\n${peer}`,'') + .replace('\n\n\n','\n\n') + ) } + catch (err) { + console.error(`Failed to delete peer config from ${env.WG_CONFIG_FILE}:\n`,err) + return res.sendStatus(500) + } + + // Success + console.log(`Deleted peer by requested from ${req.requester}`) + return res.sendStatus(200) } \ No newline at end of file diff --git a/app/add.js b/app/add.js index 7dbb864..e7c8c5f 100644 --- a/app/add.js +++ b/app/add.js @@ -1,5 +1,5 @@ 'use strict' -/* add.js +/* app/add.js * route for a client to add a peer */ @@ -34,7 +34,7 @@ module.exports = async (req, res) => { return res.sendStatus(err) } console.log(`${req.requester} must be ${user.name}`) - // Check user token + // Check token if (req.query['token']!==helper.getToken(req.requester)) { console.log(`Invalid token from ${req.requester}: ${req.query['token']}`) return res.sendStatus(403) @@ -129,7 +129,7 @@ update add *.${domain} ${env.DNS_TTL} CNAME ${domain}`) console.error(`Failed to add ns record:\n${err}`) return res.sendStatus(500) } - finally { console.log(`Updated nameserver to add ${domain}.`) } + console.log(`Updated nameserver to add ${domain}.`) // Generate user config const listen_port = Math.floor(50000 + Math.random() * 10000) diff --git a/app/del.js b/app/del.js index 0cfc93a..e1a61f1 100644 --- a/app/del.js +++ b/app/del.js @@ -1,5 +1,5 @@ 'use strict' -/* del.js +/* app/del.js * route to let clients delete a peer */ @@ -28,127 +28,120 @@ module.exports = async (req, res) => { catch (err) { console.error(`Failed to get user from ${req.requester}`) return res.sendStatus(err) - } finally { - console.log(`${req.requester} must be ${user.name}`) - - // Check user token - if (req.query['token']!==helper.getToken(req.requester)) { - console.log(`Invalid token from ${req.requester}: ${req.query['token']}`) - return res.sendStatus(403) - } + } + console.log(`${req.requester} must be ${user.name}`) - // Load wg.conf - let config_file - try { config_file = await fs.readFile(env.WG_CONFIG_FILE) } - catch (err) { console.error(err); return res.sendStatus(500) } - finally { - - // Search for peer - let peer_pubkey - const config = config_file.toString() - const peer = config.split('\n\n') - .filter( (paragraph) => { - return paragraph.includes('[Peer]') - }).filter( (peer) => { - 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']) { - // .filter() doesn't support async so use then/catch in this block - 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) - return res.sendStatus(500) - }) - } else { - console.error(`${req.requester} sent delete request without specifying a peer`) - return res.sendStatus(400) - } - })[0] - if (peer===undefined) { - console.log(`No peer found for delete request from ${req.requester}`) - return res.sendStatus(404) + // Check token + if (req.query['token']!==helper.getToken(req.requester)) { + console.log(`Invalid token from ${req.requester}: ${req.query['token']}`) + return res.sendStatus(403) + } + + // Load wg.conf + let config_file + try { config_file = await fs.readFile(env.WG_CONFIG_FILE) } + catch (err) { console.error(err); return res.sendStatus(500) } + + // Search for peer + let peer_pubkey + const config = config_file.toString() + const peer = config.split('\n\n') + .filter( (paragraph) => { + return paragraph.includes('[Peer]') + }).filter( (peer) => { + 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']) { + // .filter() doesn't support async so use then/catch in this block + 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) + return res.sendStatus(500) + }) + } else { + console.error(`${req.requester} sent delete request without specifying a peer`) + return res.sendStatus(400) } - - // Parse peer - const peer_lines = peer.split('\n') - const peer_name = peer_lines - .filter( (line) => line.includes('[Peer] # ') )[0] - .split(' # ')[1] - const peer_ips = peer_lines - .filter( (line) => line.includes('AllowedIPs = '))[0] - .split(' = ')[1] - if (peer_pubkey===undefined) { - peer_pubkey = peer_lines - .filter( (line) => line.includes('PublicKey = ') )[0] - .split(' = ')[1] - } - - // Make sure requester can't delete self - if (peer_ips.includes(req.requester)) { - console.log(`Refused to let ${req.requester} delete self: ${peer_name}`) - return res.sendStatus(409) // "Conflict" - } - - // Delete from local wg config - console.log(`Deleting ${peer_name}`); try { - await fs.writeFile(env.WG_CONFIG_FILE, - config.replace(`\n\n${peer}`,'') - .replace('\n\n\n','\n\n') - ) + })[0] + if (peer===undefined) { + console.log(`No peer found for delete request from ${req.requester}`) + return res.sendStatus(404) + } + + // Parse peer + const peer_lines = peer.split('\n') + const peer_name = peer_lines + .filter( (line) => line.includes('[Peer] # ') )[0] + .split(' # ')[1] + const peer_ips = peer_lines + .filter( (line) => line.includes('AllowedIPs = '))[0] + .split(' = ')[1] + if (peer_pubkey===undefined) { + peer_pubkey = peer_lines + .filter( (line) => line.includes('PublicKey = ') )[0] + .split(' = ')[1] + } + + // Make sure requester can't delete self + if (peer_ips.includes(req.requester)) { + console.log(`Refused to let ${req.requester} delete self: ${peer_name}`) + return res.sendStatus(409) // "Conflict" + } + + // Delete from local wg config + console.log(`Deleting ${peer_name}`); try { + await fs.writeFile(env.WG_CONFIG_FILE, + config.replace(`\n\n${peer}`,'') + .replace('\n\n\n','\n\n') + ) + } catch (err) { + console.error(`Failed to delete ${peer_name}:\n`,err) + return res.sendStatus(500) + } + + // 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?secret=${server.secret}`, peer_pubkey, { + headers: {'Content-Type': 'text/plain'}, + }) } catch (err) { - console.error(`Failed to delete ${peer_name}:\n`,err) + console.error(`Failed to inform ${server.host} to delete ${peer_name}:\n\n`,err) return res.sendStatus(500) - } finally { - - // 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?secret=${server.secret}`, peer_pubkey, { - headers: {'Content-Type': 'text/plain'}, - }) - } catch (err) { - console.error(`Failed to inform ${server.host} to delete ${peer_name}:\n\n`,err) - return res.sendStatus(500) - } - } - } - - // Delete domains from nameserver - try { - await helper.nsUpdate(dns_key, env.DNS_MASTER, + } + } + } + + // Delete domains from nameserver + try { + await helper.nsUpdate(dns_key, env.DNS_MASTER, `update delete ${peer_name}. A update delete ${peer_name}. AAAA update delete *.${peer_name}. CNAME`) - } - catch (err) { - console.error(`Failed to delete ns record:\n${err}`) - return res.sendStatus(500) - } - finally { console.log(`Updated nameserver to delete ${peer_name}.`) } - - // Inform user that delete was successful - res.sendStatus(200) - - } - - } - } - + catch (err) { + console.error(`Failed to delete ns record`) + if (err) console.error(err) + return res.sendStatus(500) + } + console.log(`Updated nameserver to delete ${peer_name}.`) + + return res.sendStatus(200) + } \ No newline at end of file diff --git a/app/list.js b/app/list.js index f0ce2f4..19b2449 100644 --- a/app/list.js +++ b/app/list.js @@ -15,23 +15,17 @@ module.exports = async (req, res) => { console.error(`Failed to get user from ${req.requester}`) return res.sendStatus(err) } - finally { - console.log(`${req.requester} must be ${user.name}`) - - // Get or generate token - try { user.token = helper.getToken(req.requester) || await helper.setToken(req.requester) } - catch (err) { - console.error(`Failed to set token for ${req.requester}`) - return res.sendStatus(err) - } - finally { - - // Send user - res.setHeader('content-type', 'text/json') - res.send(user) - - } - + console.log(`${req.requester} must be ${user.name}`) + + // Get or generate token + try { user.token = helper.getToken(req.requester) || await helper.setToken(req.requester) } + catch (err) { + console.error(`Failed to set token for ${req.requester}`) + return res.sendStatus(err) } + + // Send user + res.setHeader('content-type', 'text/json') + res.send(user) } \ No newline at end of file diff --git a/includes/helpers.js b/includes/helpers.js index 27079ca..44d7e21 100644 --- a/includes/helpers.js +++ b/includes/helpers.js @@ -25,7 +25,7 @@ module.exports = { }).filter( (peer) => { return peer.includes(`${env.IPV4_NET}.${subnet}`) || peer.includes(`${env.IPV6_NET}:${subnet}`) }) - } catch (err) { console.log(err) } + } catch (err) { console.log(err); reject(500) } let found_usernames = [] let found_hosts = [] for (const userpeer of userpeers) { @@ -73,6 +73,39 @@ module.exports = { } }), + getAllUsers: () => + new Promise( async (resolve, reject) => { + let users = {} + let peers; try { + peers = (await fs.readFile(env.WG_CONFIG_FILE)).toString() + .split('\n\n').filter( (paragraph) => { + return paragraph.includes('[Peer]') + }) + } catch (err) { console.log(err) } + for (const peer of peers) { + let peer_obj = {} + let peer_username + for (const line of peer.split('\n')) { + if (line.includes('[Peer] # ')) { + const domain = line.split(' # ')[1].split('.') + peer_obj.name = domain[0] + peer_username = domain[1] + } + else if (line.includes('AllowedIPs = ')) { + const ips = line.split('=')[1].split(', ') + peer_obj.ipv4 = ips.filter( (ip) => ip.includes(env.IPV4_NET) )[0].trim() + peer_obj.ipv6 = ips.filter( (ip) => ip.includes(env.IPV6_NET) )[0].trim() + } + } + if (!users[peer_username]) users[peer_username] = { + name: peer_username, + peers: [], + } + users[peer_username].peers.push(peer_obj) + } + resolve(users) + }), + getToken: (ip) => tokens[ip], setToken: (ip) => new Promise ( async (resolve, reject) => {