'use strict' const fs = require('fs').promises const app = require('express')() const wg = require('./wireguard') // Variables const PORT = 80 const IPV4_CIDR = 16 const IPV4_NET = '10.4' const IPV6_CIDR = 80 const IPV6_NET = 'fd69:1337:0:420:f4:f4' const WG_CONFIG_FILE = '/etc/wireguard/wg.conf' const TLD = 'gf4' const SERVERS = [ { host: 'ksn', ipv4: '10.4.0.1', ipv6: 'fd69:1337:0:420:f4:f4::1', pubkey: '/LrbvvmXLk2ZmU94JZua+eliqySuJ4QMHApthjvhO3s=', endpoint: '172.93.54.60:52348', dns: true, local: true, }, { host: 'krow', ipv4: '10.4.0.3', ipv6: 'fd69:1337:0:420:f4:f4::3', pubkey: '6VA79LOmlUaJSD1AiLEMCtnjMRZ7rwRrdbtNSCDtO2k=', endpoint: '85.17.214.157:56333', dns: true, } ] //TODO: Extract this from SERVERS with dns=true const DNS_SERVERS = '10.4.0.1, fd69:1337:0:420:f4:f4::1, 10.4.0.3, fd69:1337:0:420:f4:f4::3' // API app.get('/', async (req,res) => { const privkey = req.query['del'] const hostname = req.query['add'] const requester = req.ip.replace('::ffff:','') // Delete a peer if (privkey) { console.log(`Deleting ${privkey} from ${req.ip}`) res.setHeader('content-type', 'text/plain') res.send(`Delete ${privkey}`) } // Add a peer else if (hostname) { console.log(`New peer request from ${requester}`) // Determine user subnet let subnet if (requester.includes(IPV4_NET)) subnet = requester.split('.').slice(-2,-1)[0] else if (requester.includes(IPV6_NET)) subnet = requester.split(':').slice(-2,-1)[0] else console.log(`Received add request from ${requester}, which does not appear to be from the network.`) // Read wg.conf file for this user's other devices const userpeers = (await fs.readFile(WG_CONFIG_FILE)).toString() .split('\n\n').filter( (paragraph) => { return paragraph.includes('[Peer]') }).filter( (peer) => { return peer.includes(`${IPV4_NET}.${subnet}`) || peer.includes(`${IPV6_NET}:${subnet}`) }) let found_usernames = [] let found_ipv6s = [] let found_ipv4s = [] for (const userpeer of userpeers) { for (const line of userpeer.split('\n')) { if (line.includes('[Peer]')) { // Check if host exists if (line.includes(`# ${hostname}.`)) { console.log(`Host already exists for ${hostname}`) res.sendStatus(500); return } found_usernames.push(line.split('.').slice(-2,-1)[0]) } else if (line.includes('AllowedIPs')) { const ips = line.split('=')[1].split(',') found_ipv4s.push(ips.filter( (ip) => ip.includes(IPV4_NET) )) found_ipv6s.push(ips.filter( (ip) => ip.includes(IPV6_NET) )) } } } // Check that all IP addresses are in correct subnet or error out if (!found_ipv4s.every((found_ipv4) => found_ipv4.toString().includes(`${IPV4_NET}.${subnet}.`))) { console.log(`Found unmatching IPv4 address subnets for ${requester}: ${found_ipv4s}`) res.sendStatus(500); return } else if (!found_ipv6s.every((found_ipv6) => found_ipv6.toString().includes(`${IPV6_NET}:${subnet}:`))) { console.log(`Found unmatching IPv6 address subnets for ${requester}: ${found_ipv6s}`) res.sendStatus(500); 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] )) { console.log(`Found unmatching usernames for ${requester}: ${found_usernames.toString()}`) res.sendStatus(500); return // Everything looks good! Proceed } else { const username = found_usernames[0] console.log(`${requester} must be ${username}`) // Find next available host part in config const used_ipv4_hosts = found_ipv4s.map((found_ipv4) => found_ipv4.toString().split('.')[3].split('/')[0]) const used_ipv6_hosts = found_ipv6s.map((found_ipv6) => found_ipv6.toString().split(':')[3].split('/')[0]) let host = 1 while ([...used_ipv4_hosts,...used_ipv6_hosts].includes(host.toString())) host++ // Create IP Addresses and keys const ipv4_addr = `${IPV4_NET}.${subnet}.${host}` const ipv6_addr = `${IPV6_NET}:${subnet}:${host}` const keypair = await wg.generateKeypair() const pubkey = keypair[0] const privkey = keypair[1] // Create [Peer] sections for each SERVERS let client_peers = [] let server_peers = {} for (const server of SERVERS) { const psk = await wg.generatePSK() client_peers.push(` [Peer] # ${server.host}.${TLD} PublicKey = ${server.pubkey} PresharedKey = ${psk} AllowedIPs = ${server.ipv4}/32, ${server.ipv6}/128 Endpoint = ${server.endpoint} PersistentKeepAlive = 25`) const server_config = `\n [Peer] # ${hostname}.${username}.${TLD} PublicKey = ${pubkey} PresharedKey = ${psk} AllowedIPs = ${ipv4_addr}/32, ${ipv6_addr}/128` if (server.local) { // Add server_config to wg0.conf fs.appendFile(WG_CONFIG_FILE,server_config) } else { //TODO: Send server_config to `${server.host}.gf4` console.log(`Sending config to ${server.host}.gf4`) } } //TODO: Nameserver config // Generate config const listen_port = Math.floor(50000 + Math.random() * 10000) const config = `[Interface] PrivateKey = ${privkey} Address = ${ipv4_addr}/${IPV4_CIDR}, ${ipv6_addr}/${IPV6_CIDR} DNS = ${DNS_SERVERS} ListenPort = ${listen_port} PostUp = resolvectl domain ${TLD} ${TLD} ${client_peers.join('\n')}` // Send config to user res.setHeader('content-type', 'text/plain') res.send(config) } } // Invalid querystring else { console.log(`Invalid querystring: ${req.query}`) } }).listen(PORT)