Browse Source

add wireguard helpers

pull/1356/head
Bernd Storath 11 months ago
parent
commit
42771b7baa
  1. 104
      src/server/utils/WireGuard.ts
  2. 111
      src/server/utils/wgHelper.ts
  3. 8
      src/services/database/migrations/1.ts
  4. 1
      src/services/database/repositories/client.ts

104
src/server/utils/WireGuard.ts

@ -21,37 +21,18 @@ class WireGuard {
async #saveWireguardConfig() { async #saveWireguardConfig() {
const system = await Database.getSystem(); const system = await Database.getSystem();
const clients = await Database.getClients(); const clients = await Database.getClients();
const cidr4Block = parseCidr(system.userConfig.address4Range).prefix; const result = [];
const cidr6Block = parseCidr(system.userConfig.address6Range).prefix; result.push(wg.generateServerInterface(system));
let result = `
# Note: Do not edit this file directly. for (const client of Object.values(clients)) {
# Your changes will be overwritten! if (!client.enabled) {
continue;
# Server }
[Interface] result.push(wg.generateServerPeer(client));
PrivateKey = ${system.interface.privateKey}
Address = ${system.interface.address4}/${cidr4Block}, ${system.interface.address6}/${cidr6Block}
ListenPort = ${system.wgPort}
PreUp = ${system.iptables.PreUp}
PostUp = ${system.iptables.PostUp}
PreDown = ${system.iptables.PreDown}
PostDown = ${system.iptables.PostDown}
`;
for (const [clientId, client] of Object.entries(clients)) {
if (!client.enabled) continue;
result += `
# Client: ${client.name} (${clientId})
[Peer]
PublicKey = ${client.publicKey}
PresharedKey = ${client.preSharedKey}
AllowedIPs = ${client.address4}/32, ${client.address6}/128`;
} }
DEBUG('Config saving...'); DEBUG('Config saving...');
await fs.writeFile(path.join(WG_PATH, 'wg0.conf'), result, { await fs.writeFile(path.join(WG_PATH, 'wg0.conf'), result.join('\n'), {
mode: 0o600, mode: 0o600,
}); });
DEBUG('Config saved.'); DEBUG('Config saved.');
@ -59,7 +40,7 @@ AllowedIPs = ${client.address4}/32, ${client.address6}/128`;
async #syncWireguardConfig() { async #syncWireguardConfig() {
DEBUG('Config syncing...'); DEBUG('Config syncing...');
await exec('wg syncconf wg0 <(wg-quick strip wg0)'); await wg.sync();
DEBUG('Config synced.'); DEBUG('Config synced.');
} }
@ -86,25 +67,16 @@ AllowedIPs = ${client.address4}/32, ${client.address6}/128`;
})); }));
// Loop WireGuard status // Loop WireGuard status
const dump = await exec('wg show wg0 dump', { const dump = await wg.dump();
log: false, dump.forEach(
}); ({
dump publicKey,
.trim() latestHandshakeAt,
.split('\n') endpoint,
.slice(1) transferRx,
.forEach((line) => { transferTx,
const [ persistentKeepalive,
publicKey, }) => {
_preSharedKey,
endpoint,
_allowedIps,
latestHandshakeAt,
transferRx,
transferTx,
persistentKeepalive,
] = line.split('\t');
const client = clients.find((client) => client.publicKey === publicKey); const client = clients.find((client) => client.publicKey === publicKey);
if (!client) return; if (!client) return;
@ -116,7 +88,8 @@ AllowedIPs = ${client.address4}/32, ${client.address6}/128`;
client.transferRx = Number(transferRx); client.transferRx = Number(transferRx);
client.transferTx = Number(transferTx); client.transferTx = Number(transferTx);
client.persistentKeepalive = persistentKeepalive ?? null; client.persistentKeepalive = persistentKeepalive ?? null;
}); }
);
return clients; return clients;
} }
@ -136,22 +109,8 @@ AllowedIPs = ${client.address4}/32, ${client.address6}/128`;
async getClientConfiguration({ clientId }: { clientId: string }) { async getClientConfiguration({ clientId }: { clientId: string }) {
const system = await Database.getSystem(); const system = await Database.getSystem();
const client = await this.getClient({ clientId }); const client = await this.getClient({ clientId });
const cidr4Block = parseCidr(system.userConfig.address4Range).prefix;
const cidr6Block = parseCidr(system.userConfig.address6Range).prefix; return wg.generateClientConfig(system, client);
return `
[Interface]
PrivateKey = ${client.privateKey}
Address = ${client.address4}/${cidr4Block}, ${client.address6}/${cidr6Block}
DNS = ${system.userConfig.defaultDns.join(', ')}
MTU = ${system.userConfig.mtu}
[Peer]
PublicKey = ${system.interface.publicKey}
PresharedKey = ${client.preSharedKey}
AllowedIPs = ${client.allowedIPs.join(', ')}
PersistentKeepalive = ${client.persistentKeepalive}
Endpoint = ${system.wgHost}:${system.wgConfigPort}`;
} }
async getClientQRCodeSVG({ clientId }: { clientId: string }) { async getClientQRCodeSVG({ clientId }: { clientId: string }) {
@ -172,11 +131,9 @@ Endpoint = ${system.wgHost}:${system.wgConfigPort}`;
const system = await Database.getSystem(); const system = await Database.getSystem();
const clients = await Database.getClients(); const clients = await Database.getClients();
const privateKey = await exec('wg genkey'); const privateKey = await wg.generatePrivateKey();
const publicKey = await exec(`echo ${privateKey} | wg pubkey`, { const publicKey = await wg.getPublicKey(privateKey);
log: 'echo ***hidden*** | wg pubkey', const preSharedKey = await wg.generatePresharedKey();
});
const preSharedKey = await exec('wg genpsk');
// Calculate next IP // Calculate next IP
const cidr4 = parseCidr(system.userConfig.address4Range); const cidr4 = parseCidr(system.userConfig.address4Range);
@ -239,6 +196,7 @@ Endpoint = ${system.wgHost}:${system.wgConfigPort}`;
expiresAt: null, expiresAt: null,
enabled: true, enabled: true,
allowedIPs: system.userConfig.allowedIps, allowedIPs: system.userConfig.allowedIps,
serverAllowedIPs: null,
persistentKeepalive: system.userConfig.persistentKeepalive, persistentKeepalive: system.userConfig.persistentKeepalive,
}; };
@ -374,8 +332,8 @@ Endpoint = ${system.wgHost}:${system.wgConfigPort}`;
}); });
DEBUG('Starting Wireguard'); DEBUG('Starting Wireguard');
await this.#saveWireguardConfig(); await this.#saveWireguardConfig();
await exec('wg-quick down wg0').catch(() => {}); await wg.down().catch(() => {});
await exec('wg-quick up wg0').catch((err) => { await wg.up().catch((err) => {
if ( if (
err && err &&
err.message && err.message &&
@ -407,7 +365,7 @@ Endpoint = ${system.wgHost}:${system.wgConfigPort}`;
// Shutdown wireguard // Shutdown wireguard
async Shutdown() { async Shutdown() {
await exec('wg-quick down wg0').catch(() => {}); await wg.down().catch(() => {});
} }
async cronJob() { async cronJob() {

111
src/server/utils/wgHelper.ts

@ -0,0 +1,111 @@
import { parseCidr } from 'cidr-tools';
import type { Client } from '~~/services/database/repositories/client';
import type { System } from '~~/services/database/repositories/system';
export const wg = {
generateServerPeer: (client: Client) => {
return `# Client: ${client.name} (${client.id})
[Peer]
PublicKey = ${client.publicKey}
PresharedKey = ${client.preSharedKey}
AllowedIPs = ${client.address4}/32, ${client.address6}/128${client.serverAllowedIPs ? ` ${client.serverAllowedIPs.join(', ')}` : ''}`;
},
generateServerInterface: (system: System) => {
const cidr4Block = parseCidr(system.userConfig.address4Range).prefix;
const cidr6Block = parseCidr(system.userConfig.address6Range).prefix;
return `# Note: Do not edit this file directly.
# Your changes will be overwritten!
# Server
[Interface]
PrivateKey = ${system.interface.privateKey}
Address = ${system.interface.address4}/${cidr4Block}, ${system.interface.address6}/${cidr6Block}
ListenPort = ${system.wgPort}
PreUp = ${system.iptables.PreUp}
PostUp = ${system.iptables.PostUp}
PreDown = ${system.iptables.PreDown}
PostDown = ${system.iptables.PostDown}`;
},
generateClientConfig: (system: System, client: Client) => {
const cidr4Block = parseCidr(system.userConfig.address4Range).prefix;
const cidr6Block = parseCidr(system.userConfig.address6Range).prefix;
return `[Interface]
PrivateKey = ${client.privateKey}
Address = ${client.address4}/${cidr4Block}, ${client.address6}/${cidr6Block}
DNS = ${system.userConfig.defaultDns.join(', ')}
MTU = ${system.userConfig.mtu}
[Peer]
PublicKey = ${system.interface.publicKey}
PresharedKey = ${client.preSharedKey}
AllowedIPs = ${client.allowedIPs.join(', ')}
PersistentKeepalive = ${client.persistentKeepalive}
Endpoint = ${system.wgHost}:${system.wgConfigPort}`;
},
// TODO?: generate keys using plain javascript
generatePrivateKey: () => {
return exec('wg genkey');
},
getPublicKey: (privateKey: string) => {
return exec(`echo ${privateKey} | wg pubkey`, {
log: 'echo ***hidden*** | wg pubkey',
});
},
generatePresharedKey: () => {
return exec('wg genpsk');
},
up: () => {
return exec('wg-quick up wg0');
},
down: () => {
return exec('wg-quick down wg0');
},
sync: () => {
return exec('wg syncconf wg0 <(wg-quick strip wg0)');
},
// TODO: properly convert
dump: async () => {
const rawDump = await exec('wg show wg0 dump', {
log: false,
});
return rawDump
.trim()
.split('\n')
.slice(1)
.map((line) => {
const [
publicKey,
preSharedKey,
endpoint,
allowedIPs,
latestHandshakeAt,
transferRx,
transferTx,
persistentKeepalive,
] = line.split('\t');
return {
publicKey,
preSharedKey,
endpoint,
allowedIPs,
latestHandshakeAt,
transferRx,
transferTx,
persistentKeepalive,
};
});
},
};

8
src/services/database/migrations/1.ts

@ -5,14 +5,14 @@ import { parseCidr } from 'cidr-tools';
import { stringifyIp } from 'ip-bigint'; import { stringifyIp } from 'ip-bigint';
export async function run1(db: Low<Database>) { export async function run1(db: Low<Database>) {
const privateKey = await exec('wg genkey'); const privateKey = await wg.generatePrivateKey();
const publicKey = await exec(`echo ${privateKey} | wg pubkey`, { const publicKey = await wg.getPublicKey(privateKey);
log: 'echo ***hidden*** | wg pubkey',
});
const address4Range = '10.8.0.0/24'; const address4Range = '10.8.0.0/24';
const address6Range = 'fdcc:ad94:bacf:61a4::cafe:0/112'; const address6Range = 'fdcc:ad94:bacf:61a4::cafe:0/112';
const cidr4 = parseCidr(address4Range); const cidr4 = parseCidr(address4Range);
const cidr6 = parseCidr(address6Range); const cidr6 = parseCidr(address6Range);
const database: Database = { const database: Database = {
migrations: [], migrations: [],
system: { system: {

1
src/services/database/repositories/client.ts

@ -16,6 +16,7 @@ export type Client = {
expiresAt: string | null; expiresAt: string | null;
endpoint: string | null; endpoint: string | null;
allowedIPs: string[]; allowedIPs: string[];
serverAllowedIPs: string[] | null;
oneTimeLink: OneTimeLink | null; oneTimeLink: OneTimeLink | null;
/** ISO String */ /** ISO String */
createdAt: string; createdAt: string;

Loading…
Cancel
Save