From 080852dd15b1b45c89bce8060b509697787111b6 Mon Sep 17 00:00:00 2001 From: Bernd Storath <32197462+kaaax0815@users.noreply.github.com> Date: Tue, 3 Sep 2024 15:56:02 +0200 Subject: [PATCH] Feat: Cidr Support (#1347) * cidr support * add cidr * fix some errors fix server config missing cidr block in server config --- src/package.json | 2 ++ src/pnpm-lock.yaml | 18 +++++++++++++++ src/server/utils/WireGuard.ts | 33 +++++++++++++++------------ src/server/utils/ip.ts | 12 ---------- src/services/database/migrations/1.ts | 14 ++++++------ 5 files changed, 46 insertions(+), 33 deletions(-) delete mode 100644 src/server/utils/ip.ts diff --git a/src/package.json b/src/package.json index f1f7e461..49aa057c 100644 --- a/src/package.json +++ b/src/package.json @@ -29,6 +29,7 @@ "bcryptjs": "^2.4.3", "crc-32": "^1.2.2", "debug": "^4.3.7", + "ip": "^2.0.1", "js-sha256": "^0.11.0", "lowdb": "^7.0.1", "nuxt": "^3.13.0", @@ -44,6 +45,7 @@ "@nuxt/eslint-config": "^0.5.5", "@types/bcryptjs": "^2.4.6", "@types/debug": "^4.1.12", + "@types/ip": "^1.1.3", "@types/qrcode": "^1.5.5", "eslint": "^9.9.1", "eslint-config-prettier": "^9.1.0", diff --git a/src/pnpm-lock.yaml b/src/pnpm-lock.yaml index 64fc4531..ad598b2a 100644 --- a/src/pnpm-lock.yaml +++ b/src/pnpm-lock.yaml @@ -38,6 +38,9 @@ importers: debug: specifier: ^4.3.7 version: 4.3.7 + ip: + specifier: ^2.0.1 + version: 2.0.1 js-sha256: specifier: ^0.11.0 version: 0.11.0 @@ -78,6 +81,9 @@ importers: '@types/debug': specifier: ^4.1.12 version: 4.1.12 + '@types/ip': + specifier: ^1.1.3 + version: 1.1.3 '@types/qrcode': specifier: ^1.5.5 version: 1.5.5 @@ -1217,6 +1223,9 @@ packages: '@types/http-proxy@1.17.15': resolution: {integrity: sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==} + '@types/ip@1.1.3': + resolution: {integrity: sha512-64waoJgkXFTYnCYDUWgSATJ/dXEBanVkaP5d4Sbk7P6U7cTTMhxVyROTckc6JKdwCrgnAjZMn0k3177aQxtDEA==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -2546,6 +2555,9 @@ packages: resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==} engines: {node: '>=12.22.0'} + ip@2.0.1: + resolution: {integrity: sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==} + iron-webcrypto@1.2.1: resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} @@ -5619,6 +5631,10 @@ snapshots: dependencies: '@types/node': 22.5.2 + '@types/ip@1.1.3': + dependencies: + '@types/node': 22.5.2 + '@types/json-schema@7.0.15': {} '@types/ms@0.7.34': {} @@ -7171,6 +7187,8 @@ snapshots: transitivePeerDependencies: - supports-color + ip@2.0.1: {} + iron-webcrypto@1.2.1: {} is-arrayish@0.2.1: {} diff --git a/src/server/utils/WireGuard.ts b/src/server/utils/WireGuard.ts index a07cf5a9..7d1be6a1 100644 --- a/src/server/utils/WireGuard.ts +++ b/src/server/utils/WireGuard.ts @@ -6,6 +6,7 @@ import QRCode from 'qrcode'; import CRC32 from 'crc-32'; import type { NewClient } from '~~/services/database/repositories/client'; +import ip from 'ip'; const DEBUG = debug('WireGuard'); @@ -18,6 +19,9 @@ class WireGuard { async #saveWireguardConfig() { const system = await Database.getSystem(); const clients = await Database.getClients(); + const cidrBlock = ip.cidrSubnet( + system.userConfig.addressRange + ).subnetMaskLength; let result = ` # Note: Do not edit this file directly. # Your changes will be overwritten! @@ -25,7 +29,7 @@ class WireGuard { # Server [Interface] PrivateKey = ${system.interface.privateKey} -Address = ${system.interface.address}/24 +Address = ${system.interface.address}/${cidrBlock} ListenPort = ${system.wgPort} PreUp = ${system.iptables.PreUp} PostUp = ${system.iptables.PostUp} @@ -41,9 +45,8 @@ PostDown = ${system.iptables.PostDown} # Client: ${client.name} (${clientId}) [Peer] PublicKey = ${client.publicKey} -${ - client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : '' -}AllowedIPs = ${client.address}/32`; +PresharedKey = ${client.preSharedKey} +AllowedIPs = ${client.address}/32`; } DEBUG('Config saving...'); @@ -134,8 +137,8 @@ ${ return ` [Interface] -PrivateKey = ${client.privateKey ? `${client.privateKey}` : 'REPLACE_ME'} -Address = ${client.address}/24 +PrivateKey = ${client.privateKey} +Address = ${client.address} DNS = ${system.userConfig.defaultDns.join(',')} MTU = ${system.userConfig.mtu} @@ -175,19 +178,21 @@ Endpoint = ${system.wgHost}:${system.wgConfigPort}`; }); const preSharedKey = await exec('wg genpsk'); - // TODO: cidr // Calculate next IP + const cidr = ip.cidrSubnet(system.userConfig.addressRange); let address; - for (let i = 2; i < 255; i++) { + for ( + let i = ip.toLong(cidr.firstAddress) + 1; + i <= ip.toLong(cidr.lastAddress) - 1; + i++ + ) { + const currentIp = ip.fromLong(i); const client = Object.values(clients).find((client) => { - return ( - client.address === - system.userConfig.addressRange.replace('x', i.toString()) - ); + return client.address === currentIp; }); if (!client) { - address = system.userConfig.addressRange.replace('x', i.toString()); + address = currentIp; break; } } @@ -281,7 +286,7 @@ Endpoint = ${system.wgHost}:${system.wgConfigPort}`; clientId: string; address: string; }) { - if (!isValidIPv4(address)) { + if (!ip.isV4Format(address)) { throw createError({ statusCode: 400, statusMessage: `Invalid Address: ${address}`, diff --git a/src/server/utils/ip.ts b/src/server/utils/ip.ts deleted file mode 100644 index de2b6ba0..00000000 --- a/src/server/utils/ip.ts +++ /dev/null @@ -1,12 +0,0 @@ -export function isValidIPv4(str: string) { - const blocks = str.split('.'); - if (blocks.length !== 4) return false; - - for (const value of blocks) { - const num = parseInt(value, 10); - if (Number.isNaN(value)) return false; - if (num < 0 || num > 255) return false; - } - - return true; -} diff --git a/src/services/database/migrations/1.ts b/src/services/database/migrations/1.ts index e36300cc..7f4f5aef 100644 --- a/src/services/database/migrations/1.ts +++ b/src/services/database/migrations/1.ts @@ -2,13 +2,15 @@ import type { Low } from 'lowdb'; import type { Database } from '../repositories/database'; import packageJson from '@@/package.json'; import { ChartType } from '../repositories/system'; +import ip from 'ip'; export async function run1(db: Low) { const privateKey = await exec('wg genkey'); const publicKey = await exec(`echo ${privateKey} | wg pubkey`, { log: 'echo ***hidden*** | wg pubkey', }); - const addressRange = '10.8.0.x'; + const addressRange = '10.8.0.0/24'; + const cidr = ip.cidrSubnet(addressRange); const database: Database = { migrations: [], system: { @@ -17,20 +19,18 @@ export async function run1(db: Low) { interface: { privateKey: privateKey, publicKey: publicKey, - address: addressRange.replace('x', '1'), + address: cidr.firstAddress, }, sessionTimeout: 3600, // 1 hour lang: 'en', userConfig: { mtu: 1420, persistentKeepalive: 0, - // TODO: handle CIDR to compute next ip in WireGuard - //addressRange: '10.8.0.0/24', addressRange: addressRange, defaultDns: ['1.1.1.1'], allowedIps: ['0.0.0.0/0', '::/0'], }, - wgDevice: 'wg0', + wgDevice: 'eth0', // TODO: wgHost has to be configured when onboarding wgHost: '', wgPort: 51820, @@ -71,7 +71,7 @@ export async function run1(db: Low) { // TODO: use variables inside up/down script database.system.iptables.PostUp = ` -iptables -t nat -A POSTROUTING -s ${database.system.userConfig.addressRange.replace('x', '0')}/24 -o ${database.system.wgDevice} -j MASQUERADE; +iptables -t nat -A POSTROUTING -s ${database.system.userConfig.addressRange} -o ${database.system.wgDevice} -j MASQUERADE; iptables -A INPUT -p udp -m udp --dport ${database.system.wgPort} -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT; @@ -79,7 +79,7 @@ iptables -A FORWARD -o wg0 -j ACCEPT; .split('\n') .join(' '); database.system.iptables.PostDown = ` -iptables -t nat -D POSTROUTING -s ${database.system.userConfig.addressRange.replace('x', '0')}/24 -o ${database.system.wgDevice} -j MASQUERADE; +iptables -t nat -D POSTROUTING -s ${database.system.userConfig.addressRange} -o ${database.system.wgDevice} -j MASQUERADE; iptables -D INPUT -p udp -m udp --dport ${database.system.wgPort} -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT;