Browse Source

Feat: Migration (#1363)

* start migration

* improve migration
pull/1648/head
Bernd Storath 7 months ago
committed by Bernd Storath
parent
commit
dccd2ec3cc
  1. 47
      src/server/utils/WireGuard.ts
  2. 72
      src/server/utils/config.ts
  3. 47
      src/server/utils/ip.ts

47
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();

72
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<Database['system']>,
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,
};
}
}

47
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;
}
Loading…
Cancel
Save