From 1aa2cb1e1ecb1c76381b8f619ae17108217ab9c7 Mon Sep 17 00:00:00 2001 From: Bernd Storath <32197462+kaaax0815@users.noreply.github.com> Date: Mon, 9 Sep 2024 08:54:08 +0200 Subject: [PATCH] Feat: Migration (#1363) * start migration * improve migration --- src/server/utils/WireGuard.ts | 47 +---------------------- src/server/utils/config.ts | 72 +++++++++++++++++++++++++++++++++++ src/server/utils/ip.ts | 47 +++++++++++++++++++++++ 3 files changed, 121 insertions(+), 45 deletions(-) create mode 100644 src/server/utils/ip.ts diff --git a/src/server/utils/WireGuard.ts b/src/server/utils/WireGuard.ts index 55e56b0e..bbf77ddf 100644 --- a/src/server/utils/WireGuard.ts +++ b/src/server/utils/WireGuard.ts @@ -6,8 +6,6 @@ import QRCode from 'qrcode'; import CRC32 from 'crc-32'; import type { NewClient } from '~~/services/database/repositories/client'; -import { parseCidr } from 'cidr-tools'; -import { stringifyIp } from 'ip-bigint'; import { isIPv4 } from 'is-ip'; const DEBUG = debug('WireGuard'); @@ -134,50 +132,9 @@ class WireGuard { const publicKey = await wg.getPublicKey(privateKey); const preSharedKey = await wg.generatePresharedKey(); - // Calculate next IP - const cidr4 = parseCidr(system.userConfig.address4Range); - let address4; - for (let i = cidr4.start + 2n; i <= cidr4.end - 1n; i++) { - const currentIp4 = stringifyIp({ number: i, version: 4 }); - const client = Object.values(clients).find((client) => { - return client.address4 === currentIp4; - }); - - if (!client) { - address4 = currentIp4; - break; - } - } - - if (!address4) { - throw createError({ - statusCode: 409, - statusMessage: 'Maximum number of clients reached.', - data: { cause: 'IPv4 Address Pool exhausted' }, - }); - } + const address4 = nextIPv4(system, clients); - const cidr6 = parseCidr(system.userConfig.address6Range); - let address6; - for (let i = cidr6.start + 2n; i <= cidr6.end - 1n; i++) { - const currentIp6 = stringifyIp({ number: i, version: 6 }); - const client = Object.values(clients).find((client) => { - return client.address6 === currentIp6; - }); - - if (!client) { - address6 = currentIp6; - break; - } - } - - if (!address6) { - throw createError({ - statusCode: 409, - statusMessage: 'Maximum number of clients reached.', - data: { cause: 'IPv6 Address Pool exhausted' }, - }); - } + const address6 = nextIPv6(system, clients); // Create Client const id = crypto.randomUUID(); diff --git a/src/server/utils/config.ts b/src/server/utils/config.ts index 31498076..87f8363d 100644 --- a/src/server/utils/config.ts +++ b/src/server/utils/config.ts @@ -1,8 +1,80 @@ import debug from 'debug'; import packageJson from '@@/package.json'; +import { z } from 'zod'; +import type { Database } from '~~/services/database/repositories/database'; +import { parseCidr } from 'cidr-tools'; +import { stringifyIp } from 'ip-bigint'; export const WG_PATH = process.env.WG_PATH || '/etc/wireguard/'; export const RELEASE = packageJson.release.version; export const SERVER_DEBUG = debug('Server'); + +export async function migrateConfig(input: unknown) { + const schema = z.object({ + server: z.object({ + privateKey: z.string(), + publicKey: z.string(), + address: z.string(), + }), + clients: z.record( + z.string(), + z.object({ + name: z.string(), + address: z.string(), + privateKey: z.string(), + publicKey: z.string(), + preSharedKey: z.string(), + createdAt: z.string(), + updatedAt: z.string(), + enabled: z.boolean(), + }) + ), + }); + const res = await schema.safeParseAsync(input); + if (!res.success) { + throw new Error('Invalid Config'); + } + const system = await Database.getSystem(); + const oldConfig = res.data; + const oldCidr = parseCidr(oldConfig.server.address + '/24'); + const db = { + system: { + ...system, + interface: { + ...system.interface, + address4: oldConfig.server.address, + privateKey: oldConfig.server.privateKey, + publicKey: oldConfig.server.publicKey, + }, + userConfig: { + ...system.userConfig, + address4Range: + stringifyIp({ number: oldCidr.start, version: 4 }) + '/24', + }, + } satisfies Partial, + clients: {} as Database['clients'], + }; + for (const [oldId, oldClient] of Object.entries(oldConfig.clients)) { + const address6 = nextIPv6(db.system, db.clients); + db.clients[oldId] = { + id: oldId, + address4: oldClient.address, + createdAt: oldClient.createdAt, + enabled: oldClient.enabled, + name: oldClient.name, + preSharedKey: oldClient.preSharedKey, + privateKey: oldClient.privateKey, + publicKey: oldClient.publicKey, + updatedAt: oldClient.updatedAt, + endpoint: null, + expiresAt: null, + oneTimeLink: null, + allowedIPs: db.system.userConfig.allowedIps, + serverAllowedIPs: [], + persistentKeepalive: 0, + address6: address6, + }; + } +} diff --git a/src/server/utils/ip.ts b/src/server/utils/ip.ts new file mode 100644 index 00000000..3f2fb5e3 --- /dev/null +++ b/src/server/utils/ip.ts @@ -0,0 +1,47 @@ +import { parseCidr } from 'cidr-tools'; +import { stringifyIp } from 'ip-bigint'; +import type { Database } from '~~/services/database/repositories/database'; + +export function nextIPv4( + system: Database['system'], + clients: Database['clients'] +) { + return nextIP(4, system, clients); +} + +export function nextIPv6( + system: Database['system'], + clients: Database['clients'] +) { + return nextIP(6, system, clients); +} + +function nextIP( + version: 4 | 6, + system: Database['system'], + clients: Database['clients'] +) { + const cidr = parseCidr(system.userConfig[`address${version}Range`]); + let address; + for (let i = cidr.start + 2n; i <= cidr.end - 1n; i++) { + const currentIp = stringifyIp({ number: i, version: version }); + const client = Object.values(clients).find((client) => { + return client[`address${version}`] === currentIp; + }); + + if (!client) { + address = currentIp; + break; + } + } + + if (!address) { + throw createError({ + statusCode: 409, + statusMessage: 'Maximum number of clients reached.', + data: { cause: `IPv${version} Address Pool exhausted` }, + }); + } + + return address; +}