2021-10-14 16:53:42 -06:00
|
|
|
'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}`)
|
2021-10-14 17:15:36 -06:00
|
|
|
res.sendStatus(500); return
|
2021-10-14 16:53:42 -06:00
|
|
|
}
|
|
|
|
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}`)
|
2021-10-14 17:15:36 -06:00
|
|
|
res.sendStatus(500); return
|
2021-10-14 16:53:42 -06:00
|
|
|
} 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}`)
|
2021-10-14 17:15:36 -06:00
|
|
|
res.sendStatus(500); return
|
2021-10-14 16:53:42 -06:00
|
|
|
// 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()}`)
|
2021-10-14 17:15:36 -06:00
|
|
|
res.sendStatus(500); return
|
2021-10-14 16:53:42 -06:00
|
|
|
|
|
|
|
// 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)
|