|
|
@ -31,70 +31,85 @@ class ServerError extends Error { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
type Server = { |
|
|
|
privateKey: string; |
|
|
|
publicKey: string; |
|
|
|
address: string; |
|
|
|
}; |
|
|
|
|
|
|
|
type Client = { |
|
|
|
enabled: boolean; |
|
|
|
name: string; |
|
|
|
publicKey: string; |
|
|
|
privateKey: string; |
|
|
|
preSharedKey: string; |
|
|
|
address: string; |
|
|
|
createdAt: number; |
|
|
|
updatedAt: Date; |
|
|
|
allowedIPs?: string[]; |
|
|
|
}; |
|
|
|
|
|
|
|
type Config = { |
|
|
|
server: Server; |
|
|
|
clients: Record<string, Client>; |
|
|
|
}; |
|
|
|
|
|
|
|
class WireGuard { |
|
|
|
async __buildConfig() { |
|
|
|
this.__configPromise = Promise.resolve().then(async () => { |
|
|
|
if (!WG_HOST) { |
|
|
|
throw new Error('WG_HOST Environment Variable Not Set!'); |
|
|
|
} |
|
|
|
if (!WG_HOST) { |
|
|
|
throw new Error('WG_HOST Environment Variable Not Set!'); |
|
|
|
} |
|
|
|
|
|
|
|
debug('Loading configuration...'); |
|
|
|
let config; |
|
|
|
try { |
|
|
|
config = await fs.readFile(path.join(WG_PATH, 'wg0.json'), 'utf8'); |
|
|
|
config = JSON.parse(config); |
|
|
|
debug('Configuration loaded.'); |
|
|
|
} catch { |
|
|
|
const privateKey = await exec('wg genkey'); |
|
|
|
const publicKey = await exec(`echo ${privateKey} | wg pubkey`, { |
|
|
|
log: 'echo ***hidden*** | wg pubkey', |
|
|
|
}); |
|
|
|
const address = WG_DEFAULT_ADDRESS.replace('x', '1'); |
|
|
|
|
|
|
|
config = { |
|
|
|
server: { |
|
|
|
privateKey, |
|
|
|
publicKey, |
|
|
|
address, |
|
|
|
}, |
|
|
|
clients: {}, |
|
|
|
}; |
|
|
|
debug('Configuration generated.'); |
|
|
|
} |
|
|
|
debug('Loading configuration...'); |
|
|
|
try { |
|
|
|
const config = await fs.readFile(path.join(WG_PATH, 'wg0.json'), 'utf8'); |
|
|
|
const parsedConfig = JSON.parse(config); |
|
|
|
debug('Configuration loaded.'); |
|
|
|
return parsedConfig as Config; |
|
|
|
} catch { |
|
|
|
const privateKey = await exec('wg genkey'); |
|
|
|
const publicKey = await exec(`echo ${privateKey} | wg pubkey`, { |
|
|
|
log: 'echo ***hidden*** | wg pubkey', |
|
|
|
}); |
|
|
|
const address = WG_DEFAULT_ADDRESS.replace('x', '1'); |
|
|
|
|
|
|
|
const config: Config = { |
|
|
|
server: { |
|
|
|
privateKey, |
|
|
|
publicKey, |
|
|
|
address, |
|
|
|
}, |
|
|
|
clients: {}, |
|
|
|
}; |
|
|
|
debug('Configuration generated.'); |
|
|
|
return config; |
|
|
|
}); |
|
|
|
|
|
|
|
return this.__configPromise; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
async getConfig() { |
|
|
|
if (!this.__configPromise) { |
|
|
|
const config = await this.__buildConfig(); |
|
|
|
|
|
|
|
await this.__saveConfig(config); |
|
|
|
await exec('wg-quick down wg0').catch(() => {}); |
|
|
|
await exec('wg-quick up wg0').catch((err) => { |
|
|
|
if ( |
|
|
|
err && |
|
|
|
err.message && |
|
|
|
err.message.includes('Cannot find device "wg0"') |
|
|
|
) { |
|
|
|
throw new Error( |
|
|
|
'WireGuard exited with the error: Cannot find device "wg0"\nThis usually means that your host\'s kernel does not support WireGuard!' |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
throw err; |
|
|
|
}); |
|
|
|
// await Util.exec(`iptables -t nat -A POSTROUTING -s ${WG_DEFAULT_ADDRESS.replace('x', '0')}/24 -o ' + WG_DEVICE + ' -j MASQUERADE`);
|
|
|
|
// await Util.exec('iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT');
|
|
|
|
// await Util.exec('iptables -A FORWARD -i wg0 -j ACCEPT');
|
|
|
|
// await Util.exec('iptables -A FORWARD -o wg0 -j ACCEPT');
|
|
|
|
await this.__syncConfig(); |
|
|
|
} |
|
|
|
async getConfig(): Promise<Config> { |
|
|
|
const config = await this.__buildConfig(); |
|
|
|
|
|
|
|
await this.__saveConfig(config); |
|
|
|
await exec('wg-quick down wg0').catch(() => {}); |
|
|
|
await exec('wg-quick up wg0').catch((err) => { |
|
|
|
if ( |
|
|
|
err && |
|
|
|
err.message && |
|
|
|
err.message.includes('Cannot find device "wg0"') |
|
|
|
) { |
|
|
|
throw new Error( |
|
|
|
'WireGuard exited with the error: Cannot find device "wg0"\nThis usually means that your host\'s kernel does not support WireGuard!' |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
return this.__configPromise; |
|
|
|
throw err; |
|
|
|
}); |
|
|
|
// await Util.exec(`iptables -t nat -A POSTROUTING -s ${WG_DEFAULT_ADDRESS.replace('x', '0')}/24 -o ' + WG_DEVICE + ' -j MASQUERADE`);
|
|
|
|
// await Util.exec('iptables -A INPUT -p udp -m udp --dport 51820 -j ACCEPT');
|
|
|
|
// await Util.exec('iptables -A FORWARD -i wg0 -j ACCEPT');
|
|
|
|
// await Util.exec('iptables -A FORWARD -o wg0 -j ACCEPT');
|
|
|
|
await this.__syncConfig(); |
|
|
|
return config; |
|
|
|
} |
|
|
|
|
|
|
|
async saveConfig() { |
|
|
@ -103,7 +118,7 @@ class WireGuard { |
|
|
|
await this.__syncConfig(); |
|
|
|
} |
|
|
|
|
|
|
|
async __saveConfig(config) { |
|
|
|
async __saveConfig(config: Config) { |
|
|
|
let result = ` |
|
|
|
# Note: Do not edit this file directly. |
|
|
|
# Your changes will be overwritten! |
|
|
@ -135,7 +150,7 @@ ${ |
|
|
|
debug('Config saving...'); |
|
|
|
await fs.writeFile( |
|
|
|
path.join(WG_PATH, 'wg0.json'), |
|
|
|
JSON.stringify(config, false, 2), |
|
|
|
JSON.stringify(config, undefined, 2), |
|
|
|
{ |
|
|
|
mode: 0o660, |
|
|
|
} |
|
|
@ -207,7 +222,7 @@ ${ |
|
|
|
return clients; |
|
|
|
} |
|
|
|
|
|
|
|
async getClient({ clientId }) { |
|
|
|
async getClient({ clientId }: { clientId: string }) { |
|
|
|
const config = await this.getConfig(); |
|
|
|
const client = config.clients[clientId]; |
|
|
|
if (!client) { |
|
|
@ -217,7 +232,7 @@ ${ |
|
|
|
return client; |
|
|
|
} |
|
|
|
|
|
|
|
async getClientConfiguration({ clientId }) { |
|
|
|
async getClientConfiguration({ clientId }: { clientId: string }) { |
|
|
|
const config = await this.getConfig(); |
|
|
|
const client = await this.getClient({ clientId }); |
|
|
|
|
|
|
@ -237,7 +252,7 @@ PersistentKeepalive = ${WG_PERSISTENT_KEEPALIVE} |
|
|
|
Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`;
|
|
|
|
} |
|
|
|
|
|
|
|
async getClientQRCodeSVG({ clientId }) { |
|
|
|
async getClientQRCodeSVG({ clientId }: { clientId: string }) { |
|
|
|
const config = await this.getClientConfiguration({ clientId }); |
|
|
|
return QRCode.toString(config, { |
|
|
|
type: 'svg', |
|
|
@ -245,7 +260,7 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
async createClient({ name }) { |
|
|
|
async createClient({ name }: { name: string }) { |
|
|
|
if (!name) { |
|
|
|
throw new Error('Missing: Name'); |
|
|
|
} |
|
|
@ -298,7 +313,7 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`; |
|
|
|
return client; |
|
|
|
} |
|
|
|
|
|
|
|
async deleteClient({ clientId }) { |
|
|
|
async deleteClient({ clientId }: { clientId: string }) { |
|
|
|
const config = await this.getConfig(); |
|
|
|
|
|
|
|
if (config.clients[clientId]) { |
|
|
@ -307,7 +322,7 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
async enableClient({ clientId }) { |
|
|
|
async enableClient({ clientId }: { clientId: string }) { |
|
|
|
const client = await this.getClient({ clientId }); |
|
|
|
|
|
|
|
client.enabled = true; |
|
|
@ -316,7 +331,7 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`; |
|
|
|
await this.saveConfig(); |
|
|
|
} |
|
|
|
|
|
|
|
async disableClient({ clientId }) { |
|
|
|
async disableClient({ clientId }: { clientId: string }) { |
|
|
|
const client = await this.getClient({ clientId }); |
|
|
|
|
|
|
|
client.enabled = false; |
|
|
@ -325,7 +340,13 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`; |
|
|
|
await this.saveConfig(); |
|
|
|
} |
|
|
|
|
|
|
|
async updateClientName({ clientId, name }) { |
|
|
|
async updateClientName({ |
|
|
|
clientId, |
|
|
|
name, |
|
|
|
}: { |
|
|
|
clientId: string; |
|
|
|
name: string; |
|
|
|
}) { |
|
|
|
const client = await this.getClient({ clientId }); |
|
|
|
|
|
|
|
client.name = name; |
|
|
@ -334,7 +355,13 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`; |
|
|
|
await this.saveConfig(); |
|
|
|
} |
|
|
|
|
|
|
|
async updateClientAddress({ clientId, address }) { |
|
|
|
async updateClientAddress({ |
|
|
|
clientId, |
|
|
|
address, |
|
|
|
}: { |
|
|
|
clientId: string; |
|
|
|
address: string; |
|
|
|
}) { |
|
|
|
const client = await this.getClient({ clientId }); |
|
|
|
|
|
|
|
if (!isValidIPv4(address)) { |
|
|
@ -352,7 +379,7 @@ Endpoint = ${WG_HOST}:${WG_CONFIG_PORT}`; |
|
|
|
await this.__syncConfig(); |
|
|
|
} |
|
|
|
|
|
|
|
async restoreConfiguration(config) { |
|
|
|
async restoreConfiguration(config: string) { |
|
|
|
debug('Starting configuration restore process.'); |
|
|
|
const _config = JSON.parse(config); |
|
|
|
await this.__saveConfig(_config); |
|
|
|