Refactoring and added /list (#11)
parent
b0f3764ead
commit
65b4a0d62c
|
@ -23,7 +23,10 @@ module.exports = async (req, res) => {
|
||||||
return peer.includes(`PublicKey = ${req.body}`)
|
return peer.includes(`PublicKey = ${req.body}`)
|
||||||
})
|
})
|
||||||
try {
|
try {
|
||||||
await fs.writeFile(env.WG_CONFIG_FILE, config.replace(`\n\n${peer}`,''))
|
await fs.writeFile(env.WG_CONFIG_FILE,
|
||||||
|
config.replace(`\n\n${peer}`,'')
|
||||||
|
.replace('\n\n\n','\n\n')
|
||||||
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Failed to delete peer config from ${env.WG_CONFIG_FILE}:\n`,err)
|
console.error(`Failed to delete peer config from ${env.WG_CONFIG_FILE}:\n`,err)
|
||||||
res.sendStatus(500)
|
res.sendStatus(500)
|
||||||
|
|
185
app/add.js
185
app/add.js
|
@ -4,6 +4,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const env = require(process.argv[2]||'../env/env.json')
|
const env = require(process.argv[2]||'../env/env.json')
|
||||||
|
const helper = require('../includes/helpers')
|
||||||
const fs = require('fs').promises
|
const fs = require('fs').promises
|
||||||
const wg = require('../includes/wireguard')
|
const wg = require('../includes/wireguard')
|
||||||
const https = require('https')
|
const https = require('https')
|
||||||
|
@ -19,140 +20,93 @@ let axios; (async()=>{
|
||||||
})()
|
})()
|
||||||
|
|
||||||
module.exports = async (req, res) => {
|
module.exports = async (req, res) => {
|
||||||
const hostname = req.query['name']
|
const new_hostname = req.query['name']
|
||||||
if (!hostname) {
|
if (!new_hostname) {
|
||||||
console.log(`New peer request from ${req.requester} didn't provide a hostname`)
|
console.log(`New peer request from ${req.requester} didn't provide a hostname`)
|
||||||
res.sendStatus(400); return
|
res.sendStatus(400); return
|
||||||
}
|
} else console.log(`New peer request from ${req.requester} for ${new_hostname}`)
|
||||||
console.log(`New peer request from ${req.requester} for ${hostname}`)
|
|
||||||
|
|
||||||
// Determine user subnet
|
// Get user from IP
|
||||||
let subnet
|
let user; try { user = await helper.getUserFromIp(req.requester) }
|
||||||
if (req.requester.includes(env.IPV4_NET)) subnet = req.requester.split('.').slice(-2,-1)[0]
|
catch (err) {
|
||||||
else if (req.requester.includes(env.IPV6_NET)) subnet = req.requester.split(':').slice(-2,-1)[0]
|
console.error(`Failed to get user from ${req.requester}`)
|
||||||
else console.log(`Received add request from ${req.requester}, which does not appear to be from the network.`)
|
res.sendStatus(err); return
|
||||||
|
} console.log(`${req.requester} must be ${user.name}`)
|
||||||
// Read wg.conf file for this user's other devices
|
|
||||||
let userpeers; try {
|
// Check if new peer already exists
|
||||||
userpeers = (await fs.readFile(env.WG_CONFIG_FILE)).toString()
|
if (user.peers.map((peer) => peer.name).includes(new_hostname)) {
|
||||||
.split('\n\n').filter( (paragraph) => {
|
console.log(`Host already exists for ${new_hostname}.${user.name}.${env.TLD}`)
|
||||||
return paragraph.includes('[Peer]')
|
res.sendStatus(409); return
|
||||||
}).filter( (peer) => {
|
|
||||||
return peer.includes(`${env.IPV4_NET}.${subnet}`) || peer.includes(`${env.IPV6_NET}:${subnet}`)
|
|
||||||
})
|
|
||||||
} catch (err) { console.log(err) }
|
|
||||||
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(409); 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(env.IPV4_NET) )[0].trim())
|
|
||||||
found_ipv6s.push(ips.filter( (ip) => ip.includes(env.IPV6_NET) )[0].trim())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Check that all IP addresses are in correct subnet or error out
|
|
||||||
if (!found_ipv4s.every((found_ipv4) =>
|
// Find next available host part
|
||||||
found_ipv4.toString().includes(`${env.IPV4_NET}.${subnet}.`))) {
|
const used_ipv4_hosts = user.peers
|
||||||
console.log(`Found unmatching IPv4 address subnets for ${req.requester}: ${found_ipv4s}`)
|
.map((host) => host.ipv4).map((found_ipv4) =>
|
||||||
res.sendStatus(500); return
|
|
||||||
} else if (!found_ipv6s.every((found_ipv6) =>
|
|
||||||
found_ipv6.toString().includes(`${env.IPV6_NET}:${subnet}:`))) {
|
|
||||||
console.log(`Found unmatching IPv6 address subnets for ${req.requester}: ${found_ipv6s}`)
|
|
||||||
res.sendStatus(500); return
|
|
||||||
|
|
||||||
// Check that the req.requester is "on the list"
|
|
||||||
// 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(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] )) {
|
|
||||||
console.log(`Found unmatching usernames for ${req.requester}: ${found_usernames.toString()}`)
|
|
||||||
res.sendStatus(500); return
|
|
||||||
|
|
||||||
// Everything looks good! Proceed
|
|
||||||
} else {
|
|
||||||
const username = found_usernames[0]
|
|
||||||
console.log(`${req.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])
|
found_ipv4.toString().split('.')[3].split('/')[0])
|
||||||
const used_ipv6_hosts = found_ipv6s.map((found_ipv6) =>
|
const used_ipv6_hosts = user.peers
|
||||||
|
.map(() => host.ipv6).map((found_ipv6) =>
|
||||||
found_ipv6.toString().split(':')[3].split('/')[0])
|
found_ipv6.toString().split(':')[3].split('/')[0])
|
||||||
let host = 1
|
let host = 1
|
||||||
while ([...used_ipv4_hosts,...used_ipv6_hosts].includes(host.toString())) host++
|
while ([...used_ipv4_hosts,...used_ipv6_hosts].includes(host.toString())) host++
|
||||||
|
|
||||||
// Create IP Addresses and keys
|
// Create IP Addresses and keys
|
||||||
const ipv4_addr = `${env.IPV4_NET}.${subnet}.${host}`
|
const ipv4_addr = `${env.IPV4_NET}.${user.subnet}.${host}`
|
||||||
const ipv6_addr = `${env.IPV6_NET}:${subnet}:${host}`
|
const ipv6_addr = `${env.IPV6_NET}:${user.subnet}:${host}`
|
||||||
let keypair; try {
|
let keypair; try {
|
||||||
keypair = await wg.generateKeypair()
|
keypair = await wg.generateKeypair()
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Peer with each server
|
||||||
|
let client_peers = []
|
||||||
|
for (const server of env.SERVERS) {
|
||||||
|
let psk; try {
|
||||||
|
psk = await wg.generatePSK()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
}
|
}
|
||||||
|
// Add server to client as [Peer]
|
||||||
// Peer with each server
|
const allowed_ipv4s = `${server.ipv4}/${(server.host===env.LOCAL_SERVER)?env.IPV4_CIDR:'32'}`
|
||||||
let client_peers = []
|
const allowed_ipv6s = `${server.ipv6}/${(server.host===env.LOCAL_SERVER)?env.IPV6_CIDR:'128'}`
|
||||||
for (const server of env.SERVERS) {
|
|
||||||
let psk; try {
|
client_peers.push(`
|
||||||
psk = await wg.generatePSK()
|
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
|
||||||
}
|
|
||||||
// Add server to client as [Peer]
|
|
||||||
const allowed_ipv4s = `${server.ipv4}/${(server.host===env.LOCAL_SERVER)?env.IPV4_CIDR:'32'}`
|
|
||||||
const allowed_ipv6s = `${server.ipv6}/${(server.host===env.LOCAL_SERVER)?env.IPV6_CIDR:'128'}`
|
|
||||||
|
|
||||||
client_peers.push(`
|
|
||||||
[Peer] # ${server.host}.${env.TLD}
|
[Peer] # ${server.host}.${env.TLD}
|
||||||
PublicKey = ${server.pubkey}
|
PublicKey = ${server.pubkey}
|
||||||
PresharedKey = ${psk}
|
PresharedKey = ${psk}
|
||||||
AllowedIPs = ${allowed_ipv4s}, ${allowed_ipv6s}
|
AllowedIPs = ${allowed_ipv4s}, ${allowed_ipv6s}
|
||||||
Endpoint = ${server.endpoint}
|
Endpoint = ${server.endpoint}
|
||||||
PersistentKeepAlive = 25`)
|
PersistentKeepAlive = 25`)
|
||||||
// Add client to server as [Peer]
|
// Add client to server as [Peer]
|
||||||
const server_config = `\n
|
const server_config = `\n
|
||||||
[Peer] # ${hostname}.${username}.${env.TLD}
|
[Peer] # ${new_hostname}.${user.name}.${env.TLD}
|
||||||
PublicKey = ${keypair[0]}
|
PublicKey = ${keypair[0]}
|
||||||
PresharedKey = ${psk}
|
PresharedKey = ${psk}
|
||||||
AllowedIPs = ${ipv4_addr}/32, ${ipv6_addr}/128`
|
AllowedIPs = ${ipv4_addr}/32, ${ipv6_addr}/128`
|
||||||
if (server.host===env.LOCAL_SERVER) {
|
if (server.host===env.LOCAL_SERVER) {
|
||||||
// Add server_config to wg0.conf
|
// Add server_config to wg0.conf
|
||||||
try { await fs.appendFile(env.WG_CONFIG_FILE, server_config) }
|
try { await fs.appendFile(env.WG_CONFIG_FILE, server_config) }
|
||||||
catch (err) { console.error(err); return}
|
catch (err) { console.error(err); return}
|
||||||
} else {
|
} else {
|
||||||
// Send config to other server
|
// Send config to other server
|
||||||
console.log(`Sending config to ${server.host}.gf4`)
|
console.log(`Sending config to ${server.host}.gf4`)
|
||||||
try {
|
try {
|
||||||
await axios.post(`${server.admin_endpoint}/add`, server_config, {
|
await axios.post(`${server.admin_endpoint}/add`, server_config, {
|
||||||
headers: {'Content-Type': 'text/plain'},
|
headers: {'Content-Type': 'text/plain'},
|
||||||
})
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err.message==='Request failed with status code 403') {
|
if (err.message==='Request failed with status code 403') {
|
||||||
console.error(`Received 403 from ${server.admin_endpoint}/add`)
|
console.error(`Received 403 from ${server.admin_endpoint}/add`)
|
||||||
} else console.error(err)
|
} else console.error(err)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//TODO: Nameserver config
|
//TODO: Nameserver config
|
||||||
|
|
||||||
// Generate config
|
// Generate config
|
||||||
const listen_port = Math.floor(50000 + Math.random() * 10000)
|
const listen_port = Math.floor(50000 + Math.random() * 10000)
|
||||||
const config = `[Interface]
|
const config = `[Interface]
|
||||||
PrivateKey = ${keypair[1]}
|
PrivateKey = ${keypair[1]}
|
||||||
Address = ${ipv4_addr}/${env.IPV4_CIDR}, ${ipv6_addr}/${env.IPV6_CIDR}
|
Address = ${ipv4_addr}/${env.IPV4_CIDR}, ${ipv6_addr}/${env.IPV6_CIDR}
|
||||||
DNS = ${res.locals.DNS_SERVERS_STRING}
|
DNS = ${res.locals.DNS_SERVERS_STRING}
|
||||||
|
@ -160,9 +114,8 @@ ListenPort = ${listen_port}
|
||||||
PostUp = resolvectl domain ${env.TLD} ${env.TLD}
|
PostUp = resolvectl domain ${env.TLD} ${env.TLD}
|
||||||
${client_peers.join('\n')}`
|
${client_peers.join('\n')}`
|
||||||
|
|
||||||
// Send config to user
|
// Send config to user
|
||||||
res.setHeader('content-type', 'text/plain')
|
res.setHeader('content-type', 'text/plain')
|
||||||
res.send(config)
|
res.send(config)
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -77,7 +77,10 @@ module.exports = async (req, res) => {
|
||||||
|
|
||||||
// Delete from config
|
// Delete from config
|
||||||
console.log(`Deleting ${peer_name}`); try {
|
console.log(`Deleting ${peer_name}`); try {
|
||||||
await fs.writeFile(env.WG_CONFIG_FILE, config.replace(`\n\n${peer}`,''))
|
await fs.writeFile(env.WG_CONFIG_FILE,
|
||||||
|
config.replace(`\n\n${peer}`,'')
|
||||||
|
.replace('\n\n\n','\n\n')
|
||||||
|
)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Failed to delete ${peer_name}:\n`,err)
|
console.error(`Failed to delete ${peer_name}:\n`,err)
|
||||||
res.sendStatus(500); return
|
res.sendStatus(500); return
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
'use strict'
|
||||||
|
/* app/list.js
|
||||||
|
* route for a client to add a peer
|
||||||
|
*/
|
||||||
|
|
||||||
|
const helper = require('../includes/helpers')
|
||||||
|
|
||||||
|
module.exports = async (req, res) => {
|
||||||
|
console.log(`${req.requester} loaded dashboard`)
|
||||||
|
|
||||||
|
// Get user from IP
|
||||||
|
let user; try { user = await helper.getUserFromIp(req.requester) }
|
||||||
|
catch (err) {
|
||||||
|
console.error(`Failed to get user from ${req.requester}`)
|
||||||
|
res.sendStatus(err); return
|
||||||
|
} console.log(`${req.requester} must be ${user.name}`)
|
||||||
|
|
||||||
|
// Send user
|
||||||
|
res.setHeader('content-type', 'text/json')
|
||||||
|
res.send(user)
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,71 @@
|
||||||
|
'use strict'
|
||||||
|
const env = require(process.argv[2]||'../env/env.json')
|
||||||
|
const fs = require('fs').promises
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
getUserFromIp: async (ip) => new Promise( async (resolve, reject) => {
|
||||||
|
|
||||||
|
// Get subnet (number)
|
||||||
|
let subnet
|
||||||
|
if (ip.includes(env.IPV4_NET)) subnet = ip.split('.').slice(-2,-1)[0]
|
||||||
|
else if (ip.includes(env.IPV6_NET)) subnet = ip.split(':').slice(-2,-1)[0]
|
||||||
|
else console.log(`Received request from ${ip}, which does not appear to be from the network.`)
|
||||||
|
|
||||||
|
// Read wg.conf file for this user's other devices
|
||||||
|
let userpeers; try {
|
||||||
|
userpeers = (await fs.readFile(env.WG_CONFIG_FILE)).toString()
|
||||||
|
.split('\n\n').filter( (paragraph) => {
|
||||||
|
return paragraph.includes('[Peer]')
|
||||||
|
}).filter( (peer) => {
|
||||||
|
return peer.includes(`${env.IPV4_NET}.${subnet}`) || peer.includes(`${env.IPV6_NET}:${subnet}`)
|
||||||
|
})
|
||||||
|
} catch (err) { console.log(err) }
|
||||||
|
let found_usernames = []
|
||||||
|
let found_hosts = []
|
||||||
|
for (const userpeer of userpeers) {
|
||||||
|
let userpeer_obj = {}
|
||||||
|
for (const line of userpeer.split('\n')) {
|
||||||
|
if (line.includes('[Peer] # ')) {
|
||||||
|
const domain = line.split(' # ')[1].split('.')
|
||||||
|
userpeer_obj.name = domain[0]
|
||||||
|
found_usernames.push(domain[1])
|
||||||
|
}
|
||||||
|
else if (line.includes('AllowedIPs = ')) {
|
||||||
|
const ips = line.split('=')[1].split(', ')
|
||||||
|
userpeer_obj.ipv4 = ips.filter( (ip) => ip.includes(env.IPV4_NET) )[0].trim()
|
||||||
|
userpeer_obj.ipv6 = ips.filter( (ip) => ip.includes(env.IPV6_NET) )[0].trim()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
found_hosts.push(userpeer_obj)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that all IP addresses are in correct subnet or error out
|
||||||
|
if (!found_hosts.every((host) =>
|
||||||
|
host.ipv4.includes(`${env.IPV4_NET}.${subnet}.`) &&
|
||||||
|
host.ipv6.includes(`${env.IPV6_NET}:${subnet}:`)
|
||||||
|
)) {
|
||||||
|
console.error(
|
||||||
|
`Found unmatching IP address subnets for ${ip}: \
|
||||||
|
${found_hosts.map( (host) => [host.ipv4,host.ipv6] )}`
|
||||||
|
); reject(500)
|
||||||
|
// Check that the ip is "on the list"
|
||||||
|
// Should never get here because this ip can't access this IP!
|
||||||
|
} else if (found_usernames.length ===0) {
|
||||||
|
console.error(`Received request from ${ip} not in wg.conf!`)
|
||||||
|
reject(403)
|
||||||
|
// Check that all usernames are the same correct or error out
|
||||||
|
// https://stackoverflow.com/a/35568895
|
||||||
|
} else if (!found_usernames.every( (v,i,r) => v === r[0] )) {
|
||||||
|
console.error(`Found unmatching usernames for ${ip}: ${found_usernames.toString()}`)
|
||||||
|
reject(500)
|
||||||
|
} else {
|
||||||
|
resolve({
|
||||||
|
name: found_usernames[0],
|
||||||
|
subnet: subnet,
|
||||||
|
peers: found_hosts,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
|
||||||
|
}
|
2
index.js
2
index.js
|
@ -11,6 +11,8 @@ const admin = express()
|
||||||
|
|
||||||
app
|
app
|
||||||
.use(mw.getRequester)
|
.use(mw.getRequester)
|
||||||
|
.get('/', (req, res) => res.redirect('/list'))
|
||||||
|
.get('/list', require('./app/list.js'))
|
||||||
.get('/add', mw.getDnsServers, require('./app/add.js'))
|
.get('/add', mw.getDnsServers, require('./app/add.js'))
|
||||||
.get('/del', require('./app/del.js'))
|
.get('/del', require('./app/del.js'))
|
||||||
.listen(env.PORT)
|
.listen(env.PORT)
|
||||||
|
|
Loading…
Reference in New Issue