Browse Source

Feat: Cidr Support (#1347)

* cidr support

* add cidr

* fix some errors

fix server config

missing cidr block in server config
pull/1618/head
Bernd Storath 8 months ago
committed by Bernd Storath
parent
commit
080852dd15
  1. 2
      src/package.json
  2. 18
      src/pnpm-lock.yaml
  3. 33
      src/server/utils/WireGuard.ts
  4. 12
      src/server/utils/ip.ts
  5. 14
      src/services/database/migrations/1.ts

2
src/package.json

@ -29,6 +29,7 @@
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"crc-32": "^1.2.2", "crc-32": "^1.2.2",
"debug": "^4.3.7", "debug": "^4.3.7",
"ip": "^2.0.1",
"js-sha256": "^0.11.0", "js-sha256": "^0.11.0",
"lowdb": "^7.0.1", "lowdb": "^7.0.1",
"nuxt": "^3.13.0", "nuxt": "^3.13.0",
@ -44,6 +45,7 @@
"@nuxt/eslint-config": "^0.5.5", "@nuxt/eslint-config": "^0.5.5",
"@types/bcryptjs": "^2.4.6", "@types/bcryptjs": "^2.4.6",
"@types/debug": "^4.1.12", "@types/debug": "^4.1.12",
"@types/ip": "^1.1.3",
"@types/qrcode": "^1.5.5", "@types/qrcode": "^1.5.5",
"eslint": "^9.9.1", "eslint": "^9.9.1",
"eslint-config-prettier": "^9.1.0", "eslint-config-prettier": "^9.1.0",

18
src/pnpm-lock.yaml

@ -38,6 +38,9 @@ importers:
debug: debug:
specifier: ^4.3.7 specifier: ^4.3.7
version: 4.3.7 version: 4.3.7
ip:
specifier: ^2.0.1
version: 2.0.1
js-sha256: js-sha256:
specifier: ^0.11.0 specifier: ^0.11.0
version: 0.11.0 version: 0.11.0
@ -78,6 +81,9 @@ importers:
'@types/debug': '@types/debug':
specifier: ^4.1.12 specifier: ^4.1.12
version: 4.1.12 version: 4.1.12
'@types/ip':
specifier: ^1.1.3
version: 1.1.3
'@types/qrcode': '@types/qrcode':
specifier: ^1.5.5 specifier: ^1.5.5
version: 1.5.5 version: 1.5.5
@ -1217,6 +1223,9 @@ packages:
'@types/[email protected]': '@types/[email protected]':
resolution: {integrity: sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==} resolution: {integrity: sha512-25g5atgiVNTIv0LBDTg1H74Hvayx0ajtJPLLcYE3whFv75J0pWNtOBzaXJQgDTmrX1bx5U9YC2w/n65BN1HwRQ==}
'@types/[email protected]':
resolution: {integrity: sha512-64waoJgkXFTYnCYDUWgSATJ/dXEBanVkaP5d4Sbk7P6U7cTTMhxVyROTckc6JKdwCrgnAjZMn0k3177aQxtDEA==}
'@types/[email protected]': '@types/[email protected]':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
@ -2546,6 +2555,9 @@ packages:
resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==} resolution: {integrity: sha512-2YZsvl7jopIa1gaePkeMtd9rAcSjOOjPtpcLlOeusyO+XH2SK5ZcT+UCrElPP+WVIInh2TzeI4XW9ENaSLVVHA==}
engines: {node: '>=12.22.0'} engines: {node: '>=12.22.0'}
[email protected]:
resolution: {integrity: sha512-lJUL9imLTNi1ZfXT+DU6rBBdbiKGBuay9B6xGSPVjUeQwaH1RIGqef8RZkUtHioLmSNpPR5M4HVKJGm1j8FWVQ==}
[email protected]: [email protected]:
resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==} resolution: {integrity: sha512-feOM6FaSr6rEABp/eDfVseKyTMDt+KGpeB35SkVn9Tyn0CqvVsY3EwI0v5i8nMHyJnzCIQf7nsy3p41TPkJZhg==}
@ -5619,6 +5631,10 @@ snapshots:
dependencies: dependencies:
'@types/node': 22.5.2 '@types/node': 22.5.2
'@types/[email protected]':
dependencies:
'@types/node': 22.5.2
'@types/[email protected]': {} '@types/[email protected]': {}
'@types/[email protected]': {} '@types/[email protected]': {}
@ -7171,6 +7187,8 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
[email protected]: {}
[email protected]: {} [email protected]: {}
[email protected]: {} [email protected]: {}

33
src/server/utils/WireGuard.ts

@ -6,6 +6,7 @@ import QRCode from 'qrcode';
import CRC32 from 'crc-32'; import CRC32 from 'crc-32';
import type { NewClient } from '~~/services/database/repositories/client'; import type { NewClient } from '~~/services/database/repositories/client';
import ip from 'ip';
const DEBUG = debug('WireGuard'); const DEBUG = debug('WireGuard');
@ -18,6 +19,9 @@ 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 cidrBlock = ip.cidrSubnet(
system.userConfig.addressRange
).subnetMaskLength;
let result = ` let result = `
# Note: Do not edit this file directly. # Note: Do not edit this file directly.
# Your changes will be overwritten! # Your changes will be overwritten!
@ -25,7 +29,7 @@ class WireGuard {
# Server # Server
[Interface] [Interface]
PrivateKey = ${system.interface.privateKey} PrivateKey = ${system.interface.privateKey}
Address = ${system.interface.address}/24 Address = ${system.interface.address}/${cidrBlock}
ListenPort = ${system.wgPort} ListenPort = ${system.wgPort}
PreUp = ${system.iptables.PreUp} PreUp = ${system.iptables.PreUp}
PostUp = ${system.iptables.PostUp} PostUp = ${system.iptables.PostUp}
@ -41,9 +45,8 @@ PostDown = ${system.iptables.PostDown}
# Client: ${client.name} (${clientId}) # Client: ${client.name} (${clientId})
[Peer] [Peer]
PublicKey = ${client.publicKey} PublicKey = ${client.publicKey}
${ PresharedKey = ${client.preSharedKey}
client.preSharedKey ? `PresharedKey = ${client.preSharedKey}\n` : '' AllowedIPs = ${client.address}/32`;
}AllowedIPs = ${client.address}/32`;
} }
DEBUG('Config saving...'); DEBUG('Config saving...');
@ -134,8 +137,8 @@ ${
return ` return `
[Interface] [Interface]
PrivateKey = ${client.privateKey ? `${client.privateKey}` : 'REPLACE_ME'} PrivateKey = ${client.privateKey}
Address = ${client.address}/24 Address = ${client.address}
DNS = ${system.userConfig.defaultDns.join(',')} DNS = ${system.userConfig.defaultDns.join(',')}
MTU = ${system.userConfig.mtu} MTU = ${system.userConfig.mtu}
@ -175,19 +178,21 @@ Endpoint = ${system.wgHost}:${system.wgConfigPort}`;
}); });
const preSharedKey = await exec('wg genpsk'); const preSharedKey = await exec('wg genpsk');
// TODO: cidr
// Calculate next IP // Calculate next IP
const cidr = ip.cidrSubnet(system.userConfig.addressRange);
let address; 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) => { const client = Object.values(clients).find((client) => {
return ( return client.address === currentIp;
client.address ===
system.userConfig.addressRange.replace('x', i.toString())
);
}); });
if (!client) { if (!client) {
address = system.userConfig.addressRange.replace('x', i.toString()); address = currentIp;
break; break;
} }
} }
@ -281,7 +286,7 @@ Endpoint = ${system.wgHost}:${system.wgConfigPort}`;
clientId: string; clientId: string;
address: string; address: string;
}) { }) {
if (!isValidIPv4(address)) { if (!ip.isV4Format(address)) {
throw createError({ throw createError({
statusCode: 400, statusCode: 400,
statusMessage: `Invalid Address: ${address}`, statusMessage: `Invalid Address: ${address}`,

12
src/server/utils/ip.ts

@ -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;
}

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

@ -2,13 +2,15 @@ import type { Low } from 'lowdb';
import type { Database } from '../repositories/database'; import type { Database } from '../repositories/database';
import packageJson from '@@/package.json'; import packageJson from '@@/package.json';
import { ChartType } from '../repositories/system'; import { ChartType } from '../repositories/system';
import ip from 'ip';
export async function run1(db: Low<Database>) { export async function run1(db: Low<Database>) {
const privateKey = await exec('wg genkey'); const privateKey = await exec('wg genkey');
const publicKey = await exec(`echo ${privateKey} | wg pubkey`, { const publicKey = await exec(`echo ${privateKey} | wg pubkey`, {
log: 'echo ***hidden*** | 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 = { const database: Database = {
migrations: [], migrations: [],
system: { system: {
@ -17,20 +19,18 @@ export async function run1(db: Low<Database>) {
interface: { interface: {
privateKey: privateKey, privateKey: privateKey,
publicKey: publicKey, publicKey: publicKey,
address: addressRange.replace('x', '1'), address: cidr.firstAddress,
}, },
sessionTimeout: 3600, // 1 hour sessionTimeout: 3600, // 1 hour
lang: 'en', lang: 'en',
userConfig: { userConfig: {
mtu: 1420, mtu: 1420,
persistentKeepalive: 0, persistentKeepalive: 0,
// TODO: handle CIDR to compute next ip in WireGuard
//addressRange: '10.8.0.0/24',
addressRange: addressRange, addressRange: addressRange,
defaultDns: ['1.1.1.1'], defaultDns: ['1.1.1.1'],
allowedIps: ['0.0.0.0/0', '::/0'], allowedIps: ['0.0.0.0/0', '::/0'],
}, },
wgDevice: 'wg0', wgDevice: 'eth0',
// TODO: wgHost has to be configured when onboarding // TODO: wgHost has to be configured when onboarding
wgHost: '', wgHost: '',
wgPort: 51820, wgPort: 51820,
@ -71,7 +71,7 @@ export async function run1(db: Low<Database>) {
// TODO: use variables inside up/down script // TODO: use variables inside up/down script
database.system.iptables.PostUp = ` 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 INPUT -p udp -m udp --dport ${database.system.wgPort} -j ACCEPT;
iptables -A FORWARD -i wg0 -j ACCEPT; iptables -A FORWARD -i wg0 -j ACCEPT;
iptables -A FORWARD -o wg0 -j ACCEPT; iptables -A FORWARD -o wg0 -j ACCEPT;
@ -79,7 +79,7 @@ iptables -A FORWARD -o wg0 -j ACCEPT;
.split('\n') .split('\n')
.join(' '); .join(' ');
database.system.iptables.PostDown = ` 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 INPUT -p udp -m udp --dport ${database.system.wgPort} -j ACCEPT;
iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT;
iptables -D FORWARD -o wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT;

Loading…
Cancel
Save