Browse Source

migrate to sqlite

pull/1619/head
Bernd Storath 3 months ago
parent
commit
139ef95a84
  1. 9
      src/server/api/client/index.post.ts
  2. 12
      src/server/database/repositories/client/schema.ts
  3. 106
      src/server/database/repositories/client/service.ts
  4. 19
      src/server/database/repositories/client/types.ts
  5. 39
      src/server/database/repositories/clients/service.ts
  6. 8
      src/server/database/repositories/hooks/schema.ts
  7. 4
      src/server/database/repositories/hooks/types.ts
  8. 12
      src/server/database/repositories/interface/schema.ts
  9. 4
      src/server/database/repositories/interface/types.ts
  10. 8
      src/server/database/repositories/metrics/schema.ts
  11. 14
      src/server/database/repositories/oneTimeLink/schema.ts
  12. 2
      src/server/database/repositories/user/schema.ts
  13. 8
      src/server/database/repositories/userConfig/schema.ts
  14. 4
      src/server/database/repositories/userConfig/types.ts
  15. 6
      src/server/database/schema.ts
  16. 4
      src/server/database/sqlite.ts
  17. 55
      src/server/utils/WireGuard.ts
  18. 36
      src/server/utils/ip.ts
  19. 17
      src/server/utils/template.ts
  20. 59
      src/server/utils/wgHelper.ts

9
src/server/api/client/index.post.ts

@ -1,8 +1,11 @@
import { ClientCreateSchema } from '#db/repositories/client/types';
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const { name, expireDate } = await readValidatedBody( const { name, expiresAt } = await readValidatedBody(
event, event,
validateZod(createType) validateZod(ClientCreateSchema)
); );
await WireGuard.createClient({ name, expireDate }); await Database.clients.create({ name, expiresAt });
await WireGuard.saveConfig();
return { success: true }; return { success: true };
}); });

12
src/server/database/repositories/clients/schema.ts → src/server/database/repositories/client/schema.ts

@ -1,9 +1,9 @@
import { sql, relations } from 'drizzle-orm'; import { sql, relations } from 'drizzle-orm';
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { oneTimeLinks } from '../../schema'; import { oneTimeLink } from '../../schema';
export const clients = sqliteTable('clients_table', { export const client = sqliteTable('clients_table', {
id: int().primaryKey({ autoIncrement: true }), id: int().primaryKey({ autoIncrement: true }),
name: text().notNull(), name: text().notNull(),
ipv4Address: text('ipv4_address').notNull().unique(), ipv4Address: text('ipv4_address').notNull().unique(),
@ -29,9 +29,9 @@ export const clients = sqliteTable('clients_table', {
.$onUpdate(() => sql`(CURRENT_TIMESTAMP)`), .$onUpdate(() => sql`(CURRENT_TIMESTAMP)`),
}); });
export const clientsRelations = relations(clients, ({ one }) => ({ export const clientsRelations = relations(client, ({ one }) => ({
oneTimeLink: one(oneTimeLinks, { oneTimeLink: one(oneTimeLink, {
fields: [clients.id], fields: [client.id],
references: [oneTimeLinks.clientId], references: [oneTimeLink.clientId],
}), }),
})); }));

106
src/server/database/repositories/client/service.ts

@ -0,0 +1,106 @@
import type { DBType } from '#db/sqlite';
import { eq, sql } from 'drizzle-orm';
import { client } from './schema';
import type { ClientCreateType } from './types';
import { wgInterface, userConfig } from '../../schema';
import { parseCidr } from 'cidr-tools';
function createPreparedStatement(db: DBType) {
return {
findAll: db.query.client
.findMany({
with: {
oneTimeLink: true,
},
})
.prepare(),
findById: db.query.client
.findFirst({ where: eq(client.id, sql.placeholder('id')) })
.prepare(),
};
}
export class ClientService {
#db: DBType;
#statements: ReturnType<typeof createPreparedStatement>;
constructor(db: DBType) {
this.#db = db;
this.#statements = createPreparedStatement(db);
}
async getAll() {
const result = await this.#statements.findAll.all();
return result.map((row) => ({
...row,
createdAt: new Date(row.createdAt),
updatedAt: new Date(row.updatedAt),
}));
}
async get(id: number) {
return this.#statements.findById.all({ id });
}
async create({ name, expiresAt }: ClientCreateType) {
const privateKey = await wg.generatePrivateKey();
const publicKey = await wg.getPublicKey(privateKey);
const preSharedKey = await wg.generatePreSharedKey();
let parsedExpiresAt = expiresAt;
if (parsedExpiresAt) {
const expiresAtDate = new Date(parsedExpiresAt);
expiresAtDate.setHours(23);
expiresAtDate.setMinutes(59);
expiresAtDate.setSeconds(59);
parsedExpiresAt = expiresAtDate.toISOString();
}
await this.#db.transaction(async (tx) => {
const clients = await tx.query.client.findMany().execute();
const clientInterface = await tx.query.wgInterface
.findFirst({
where: eq(wgInterface.name, 'wg0'),
})
.execute();
if (!clientInterface) {
throw new Error('WireGuard interface not found');
}
const clientConfig = await tx.query.userConfig
.findFirst({
where: eq(userConfig.id, clientInterface.name),
})
.execute();
if (!clientConfig) {
throw new Error('WireGuard interface configuration not found');
}
const ipv4Cidr = parseCidr(clientInterface.ipv4Cidr);
const ipv4Address = nextIP(4, ipv4Cidr, clients);
const ipv6Cidr = parseCidr(clientInterface.ipv6Cidr);
const ipv6Address = nextIP(6, ipv6Cidr, clients);
return await tx
.insert(client)
.values({
name,
expiresAt: parsedExpiresAt,
privateKey,
publicKey,
preSharedKey,
ipv4Address,
ipv6Address,
mtu: clientConfig.defaultMtu,
allowedIps: clientConfig.defaultAllowedIps,
dns: clientConfig.defaultDns,
persistentKeepalive: clientConfig.defaultPersistentKeepalive,
serverAllowedIps: [],
enabled: true,
})
.execute();
});
}
}

19
src/server/database/repositories/clients/types.ts → src/server/database/repositories/client/types.ts

@ -1,7 +1,7 @@
import type { InferSelectModel } from 'drizzle-orm'; import type { InferSelectModel } from 'drizzle-orm';
import { zod } from '#imports'; import { zod } from '#imports';
import type { clients } from './schema'; import type { client } from './schema';
const schemaForType = const schemaForType =
<T>() => <T>() =>
@ -10,10 +10,12 @@ const schemaForType =
return arg; return arg;
}; };
export type ClientsType = InferSelectModel<typeof clients>; export type ID = string;
export type ClientType = InferSelectModel<typeof client>;
export type CreateClientType = Omit< export type CreateClientType = Omit<
ClientsType, ClientType,
'createdAt' | 'updatedAt' | 'id' 'createdAt' | 'updatedAt' | 'id'
>; >;
@ -27,7 +29,7 @@ const name = zod
.min(1, 'zod.nameMin') .min(1, 'zod.nameMin')
.pipe(safeStringRefine); .pipe(safeStringRefine);
const expireDate = zod const expiresAt = zod
.string({ message: 'zod.expireDate' }) .string({ message: 'zod.expireDate' })
.min(1, 'zod.expireDateMin') .min(1, 'zod.expireDateMin')
.pipe(safeStringRefine) .pipe(safeStringRefine)
@ -70,11 +72,18 @@ const enabled = zod.boolean({ message: 'zod.enabled' });
const dns = zod.array(address, { message: 'zod.dns' }).min(1, 'zod.dnsMin'); const dns = zod.array(address, { message: 'zod.dns' }).min(1, 'zod.dnsMin');
export const ClientCreateSchema = zod.object({
name: name,
expiresAt: expiresAt,
});
export type ClientCreateType = zod.infer<typeof ClientCreateSchema>;
export const ClientUpdateSchema = schemaForType<UpdateClientType>()( export const ClientUpdateSchema = schemaForType<UpdateClientType>()(
zod.object({ zod.object({
name: name, name: name,
enabled: enabled, enabled: enabled,
expiresAt: expireDate, expiresAt: expiresAt,
ipv4Address: address4, ipv4Address: address4,
ipv6Address: address6, ipv6Address: address6,
allowedIps: allowedIps, allowedIps: allowedIps,

39
src/server/database/repositories/clients/service.ts

@ -1,39 +0,0 @@
import type { DBType } from '#db/sqlite';
import { eq, sql } from 'drizzle-orm';
import { clients } from './schema';
function createPreparedStatement(db: DBType) {
return {
findAll: db.query.clients
.findMany({
with: {
oneTimeLink: true,
},
})
.prepare(),
findById: db.query.clients
.findFirst({ where: eq(clients.id, sql.placeholder('id')) })
.prepare(),
};
}
export class ClientsService {
#statements: ReturnType<typeof createPreparedStatement>;
constructor(db: DBType) {
this.#statements = createPreparedStatement(db);
}
async findAll() {
const result = await this.#statements.findAll.all();
return result.map((row) => ({
...row,
createdAt: new Date(row.createdAt),
updatedAt: new Date(row.updatedAt),
}));
}
async findById(id: number) {
return this.#statements.findById.all({ id });
}
}

8
src/server/database/repositories/hooks/schema.ts

@ -1,12 +1,12 @@
import { sql } from 'drizzle-orm'; import { sql } from 'drizzle-orm';
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { wgInterface } from '../../schema'; import { wgInterface } from '../../schema';
export const hooks = sqliteTable('hooks_table', { export const hooks = sqliteTable('hooks_table', {
id: int() id: text()
.primaryKey({ autoIncrement: true }) .primaryKey()
.references(() => wgInterface.id, { .references(() => wgInterface.name, {
onDelete: 'cascade', onDelete: 'cascade',
onUpdate: 'cascade', onUpdate: 'cascade',
}), }),

4
src/server/database/repositories/hooks/types.ts

@ -0,0 +1,4 @@
import type { InferSelectModel } from 'drizzle-orm';
import type { hooks } from './schema';
export type HooksType = InferSelectModel<typeof hooks>;

12
src/server/database/repositories/interface/schema.ts

@ -4,9 +4,9 @@ import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { userConfig, hooks, prometheus } from '../../schema'; import { userConfig, hooks, prometheus } from '../../schema';
// maybe support multiple interfaces in the future // maybe support multiple interfaces in the future
export const wgInterface = sqliteTable('interface_table', { export const wgInterface = sqliteTable('interfaces_table', {
id: int().primaryKey({ autoIncrement: true }), name: text().primaryKey(),
device: text().notNull().unique(), device: text().notNull(),
port: int().notNull().unique(), port: int().notNull().unique(),
privateKey: text('private_key').notNull(), privateKey: text('private_key').notNull(),
publicKey: text('public_key').notNull(), publicKey: text('public_key').notNull(),
@ -25,15 +25,15 @@ export const wgInterface = sqliteTable('interface_table', {
export const wgInterfaceRelations = relations(wgInterface, ({ one }) => ({ export const wgInterfaceRelations = relations(wgInterface, ({ one }) => ({
hooks: one(hooks, { hooks: one(hooks, {
fields: [wgInterface.id], fields: [wgInterface.name],
references: [hooks.id], references: [hooks.id],
}), }),
prometheus: one(prometheus, { prometheus: one(prometheus, {
fields: [wgInterface.id], fields: [wgInterface.name],
references: [prometheus.id], references: [prometheus.id],
}), }),
userConfig: one(userConfig, { userConfig: one(userConfig, {
fields: [wgInterface.id], fields: [wgInterface.name],
references: [userConfig.id], references: [userConfig.id],
}), }),
})); }));

4
src/server/database/repositories/interface/types.ts

@ -0,0 +1,4 @@
import type { InferSelectModel } from 'drizzle-orm';
import type { wgInterface } from './schema';
export type InterfaceType = InferSelectModel<typeof wgInterface>;

8
src/server/database/repositories/metrics/schema.ts

@ -1,12 +1,12 @@
import { sql } from 'drizzle-orm'; import { sql } from 'drizzle-orm';
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { wgInterface } from '../../schema'; import { wgInterface } from '../../schema';
export const prometheus = sqliteTable('prometheus_table', { export const prometheus = sqliteTable('prometheus_table', {
id: int() id: text()
.primaryKey({ autoIncrement: true }) .primaryKey()
.references(() => wgInterface.id, { .references(() => wgInterface.name, {
onDelete: 'cascade', onDelete: 'cascade',
onUpdate: 'cascade', onUpdate: 'cascade',
}), }),

14
src/server/database/repositories/oneTimeLinks/schema.ts → src/server/database/repositories/oneTimeLink/schema.ts

@ -1,15 +1,15 @@
import { sql, relations } from 'drizzle-orm'; import { sql, relations } from 'drizzle-orm';
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { clients } from '../../schema'; import { client } from '../../schema';
export const oneTimeLinks = sqliteTable('one_time_links_table', { export const oneTimeLink = sqliteTable('one_time_links_table', {
id: int().primaryKey({ autoIncrement: true }), id: int().primaryKey({ autoIncrement: true }),
oneTimeLink: text('one_time_link').notNull(), oneTimeLink: text('one_time_link').notNull(),
expiresAt: text('expires_at').notNull(), expiresAt: text('expires_at').notNull(),
clientId: int() clientId: int()
.notNull() .notNull()
.references(() => clients.id, { onDelete: 'cascade', onUpdate: 'cascade' }), .references(() => client.id, { onDelete: 'cascade', onUpdate: 'cascade' }),
createdAt: text('created_at') createdAt: text('created_at')
.notNull() .notNull()
.default(sql`(CURRENT_TIMESTAMP)`), .default(sql`(CURRENT_TIMESTAMP)`),
@ -19,9 +19,9 @@ export const oneTimeLinks = sqliteTable('one_time_links_table', {
.$onUpdate(() => sql`(CURRENT_TIMESTAMP)`), .$onUpdate(() => sql`(CURRENT_TIMESTAMP)`),
}); });
export const oneTimeLinksRelations = relations(oneTimeLinks, ({ one }) => ({ export const oneTimeLinksRelations = relations(oneTimeLink, ({ one }) => ({
client: one(clients, { client: one(client, {
fields: [oneTimeLinks.clientId], fields: [oneTimeLink.clientId],
references: [clients.id], references: [client.id],
}), }),
})); }));

2
src/server/database/repositories/users/schema.ts → src/server/database/repositories/user/schema.ts

@ -1,7 +1,7 @@
import { sql } from 'drizzle-orm'; import { sql } from 'drizzle-orm';
import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core'; import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core';
export const users = sqliteTable('users_table', { export const user = sqliteTable('users_table', {
id: int().primaryKey({ autoIncrement: true }), id: int().primaryKey({ autoIncrement: true }),
username: text().notNull(), username: text().notNull(),
password: text().notNull(), password: text().notNull(),

8
src/server/database/repositories/userConfig/schema.ts

@ -4,10 +4,10 @@ import { int, sqliteTable, text } from 'drizzle-orm/sqlite-core';
import { wgInterface } from '../../schema'; import { wgInterface } from '../../schema';
// default* means clients store it themselves // default* means clients store it themselves
export const userConfig = sqliteTable('user_config_table', { export const userConfig = sqliteTable('user_configs_table', {
id: int() id: text()
.primaryKey({ autoIncrement: true }) .primaryKey()
.references(() => wgInterface.id, { .references(() => wgInterface.name, {
onDelete: 'cascade', onDelete: 'cascade',
onUpdate: 'cascade', onUpdate: 'cascade',
}), }),

4
src/server/database/repositories/userConfig/types.ts

@ -0,0 +1,4 @@
import type { InferSelectModel } from 'drizzle-orm';
import type { userConfig } from './schema';
export type UserConfigType = InferSelectModel<typeof userConfig>;

6
src/server/database/schema.ts

@ -1,9 +1,9 @@
// Make sure to not use any Path Aliases in these files // Make sure to not use any Path Aliases in these files
export * from './repositories/clients/schema'; export * from './repositories/client/schema';
export * from './repositories/general/schema'; export * from './repositories/general/schema';
export * from './repositories/hooks/schema'; export * from './repositories/hooks/schema';
export * from './repositories/interface/schema'; export * from './repositories/interface/schema';
export * from './repositories/metrics/schema'; export * from './repositories/metrics/schema';
export * from './repositories/oneTimeLinks/schema'; export * from './repositories/oneTimeLink/schema';
export * from './repositories/userConfig/schema'; export * from './repositories/userConfig/schema';
export * from './repositories/users/schema'; export * from './repositories/user/schema';

4
src/server/database/sqlite.ts

@ -3,7 +3,7 @@ import { migrate as drizzleMigrate } from 'drizzle-orm/libsql/migrator';
import { createClient } from '@libsql/client'; import { createClient } from '@libsql/client';
import * as schema from './schema'; import * as schema from './schema';
import { ClientsService } from './repositories/clients/service'; import { ClientService } from './repositories/client/service';
const client = createClient({ url: 'file:/etc/wireguard/wg0.db' }); const client = createClient({ url: 'file:/etc/wireguard/wg0.db' });
const db = drizzle({ client, schema }); const db = drizzle({ client, schema });
@ -14,7 +14,7 @@ export async function connect() {
} }
class DBService { class DBService {
clients = new ClientsService(db); clients = new ClientService(db);
constructor(private db: DBType) {} constructor(private db: DBType) {}
} }

55
src/server/utils/WireGuard.ts

@ -50,7 +50,7 @@ class WireGuard {
} }
async getClients() { async getClients() {
const dbClients = await Database.clients.findAll(); const dbClients = await Database.clients.getAll();
const clients = dbClients.map((client) => ({ const clients = dbClients.map((client) => ({
...client, ...client,
latestHandshakeAt: null as Date | null, latestHandshakeAt: null as Date | null,
@ -78,9 +78,9 @@ class WireGuard {
return clients; return clients;
} }
async getClientConfiguration({ clientId }: { clientId: string }) { async getClientConfiguration({ clientId }: { clientId: number }) {
const system = await Database.system.get(); const system = await Database.system.get();
const client = await this.getClient({ clientId }); const client = await Database.clients.get(clientId);
return wg.generateClientConfig(system, client); return wg.generateClientConfig(system, client);
} }
@ -93,55 +93,6 @@ class WireGuard {
}); });
} }
async createClient({
name,
expireDate,
}: {
name: string;
expireDate: string | null;
}) {
const system = await Database.system.get();
const clients = await Database.client.findAll();
const privateKey = await wg.generatePrivateKey();
const publicKey = await wg.getPublicKey(privateKey);
const preSharedKey = await wg.generatePresharedKey();
const address4 = nextIPv4(system, clients);
const address6 = nextIPv6(system, clients);
const client: CreateClient = {
name,
address4,
address6,
privateKey,
publicKey,
preSharedKey,
oneTimeLink: null,
expiresAt: null,
enabled: true,
allowedIps: [...system.userConfig.allowedIps],
serverAllowedIPs: [],
persistentKeepalive: system.userConfig.persistentKeepalive,
mtu: system.userConfig.mtu,
};
if (expireDate) {
const date = new Date(expireDate);
date.setHours(23);
date.setMinutes(59);
date.setSeconds(59);
client.expiresAt = date.toISOString();
}
await Database.client.create(client);
await this.saveConfig();
return client;
}
async deleteClient({ clientId }: { clientId: string }) { async deleteClient({ clientId }: { clientId: string }) {
await Database.client.delete(clientId); await Database.client.delete(clientId);
await this.saveConfig(); await this.saveConfig();

36
src/server/utils/ip.ts

@ -1,36 +1,20 @@
import { parseCidr } from 'cidr-tools'; import type { parseCidr } from 'cidr-tools';
import { stringifyIp } from 'ip-bigint'; import { stringifyIp } from 'ip-bigint';
import type { DeepReadonly } from 'vue';
import type { Database } from '~~/services/database/repositories/database';
export function nextIPv4( import type { ClientType } from '#db/repositories/client/types';
system: DeepReadonly<Database['system']>,
clients: DeepReadonly<Database['clients']>
) {
return nextIP(4, system, clients);
}
export function nextIPv6(
system: DeepReadonly<Database['system']>,
clients: DeepReadonly<Database['clients']>
) {
return nextIP(6, system, clients);
}
// TODO: above functions should probably have a lock type ParsedCidr = ReturnType<typeof parseCidr>;
// TODO(general): what happens if multiple users create client at the same time?
function nextIP( export function nextIP(
version: 4 | 6, version: 4 | 6,
system: DeepReadonly<Database['system']>, cidr: ParsedCidr,
clients: DeepReadonly<Database['clients']> clients: ClientType[]
) { ) {
const cidr = parseCidr(system.userConfig[`address${version}Range`]);
let address; let address;
for (let i = cidr.start + 2n; i <= cidr.end - 1n; i++) { for (let i = cidr.start + 2n; i <= cidr.end - 1n; i++) {
const currentIp = stringifyIp({ number: i, version: version }); const currentIp = stringifyIp({ number: i, version: version });
const client = Object.values(clients).find((client) => { const client = Object.values(clients).find((client) => {
return client[`address${version}`] === currentIp; return client[`ipv${version}Address`] === currentIp;
}); });
if (!client) { if (!client) {
@ -40,10 +24,8 @@ function nextIP(
} }
if (!address) { if (!address) {
throw createError({ throw new Error('Maximum number of clients reached', {
statusCode: 409, cause: `IPv${version} Address Pool exhausted`,
statusMessage: 'Maximum number of clients reached.',
data: { cause: `IPv${version} Address Pool exhausted` },
}); });
} }

17
src/server/utils/template.ts

@ -1,5 +1,4 @@
import type { DeepReadonly } from 'vue'; import type { InterfaceType } from '#db/repositories/interface/types';
import type { System } from '~~/services/database/repositories/system';
/** /**
* Replace all {{key}} in the template with the values[key] * Replace all {{key}} in the template with the values[key]
@ -12,16 +11,16 @@ export function template(templ: string, values: Record<string, string>) {
/** /**
* Available keys: * Available keys:
* - address4: IPv4 address range * - ipv4Cidr: IPv4 CIDR
* - address6: IPv6 address range * - ipv6Cidr: IPv6 CIDR
* - device: Network device * - device: Network device
* - port: Port number * - port: Port number
*/ */
export function iptablesTemplate(templ: string, system: DeepReadonly<System>) { export function iptablesTemplate(templ: string, wgInterface: InterfaceType) {
return template(templ, { return template(templ, {
address4: system.userConfig.address4Range, ipv4Cidr: wgInterface.ipv4Cidr,
address6: system.userConfig.address6Range, ipv6Cidr: wgInterface.ipv6Cidr,
device: system.interface.device, device: wgInterface.device,
port: system.interface.port.toString(), port: wgInterface.port.toString(),
}); });
} }

59
src/server/utils/wgHelper.ts

@ -1,16 +1,18 @@
import { parseCidr } from 'cidr-tools'; import { parseCidr } from 'cidr-tools';
import type { DeepReadonly } from 'vue'; import type { ClientType } from '#db/repositories/client/types';
import type { Client } from '~~/services/database/repositories/client'; import type { InterfaceType } from '#db/repositories/interface/types';
import type { System } from '~~/services/database/repositories/system'; import { stringifyIp } from 'ip-bigint';
import type { UserConfigType } from '#db/repositories/userConfig/types';
import type { HooksType } from '#db/repositories/hooks/types';
// TODO: replace wg0 with parameter (to allow multi interface design) // TODO: replace wg0 with parameter (to allow multi interface design)
export const wg = { export const wg = {
generateServerPeer: (client: DeepReadonly<Client>) => { generateServerPeer: (client: ClientType) => {
const allowedIps = [ const allowedIps = [
`${client.address4}/32`, `${client.ipv4Address}/32`,
`${client.address6}/128`, `${client.ipv6Address}/128`,
...(client.serverAllowedIPs ?? []), ...(client.serverAllowedIps ?? []),
]; ];
return `# Client: ${client.name} (${client.id}) return `# Client: ${client.name} (${client.id})
@ -20,44 +22,47 @@ PresharedKey = ${client.preSharedKey}
AllowedIPs = ${allowedIps.join(', ')}`; AllowedIPs = ${allowedIps.join(', ')}`;
}, },
generateServerInterface: (system: DeepReadonly<System>) => { generateServerInterface: (wgInterface: InterfaceType, hooks: HooksType) => {
const cidr4Block = parseCidr(system.userConfig.address4Range).prefix; const cidr4 = parseCidr(wgInterface.ipv4Cidr);
const cidr6Block = parseCidr(system.userConfig.address6Range).prefix; const cidr6 = parseCidr(wgInterface.ipv6Cidr);
const ipv4Addr = stringifyIp({ number: cidr4.start + 1n, version: 4 });
const ipv6Addr = stringifyIp({ number: cidr6.start + 1n, version: 6 });
return `# Note: Do not edit this file directly. return `# Note: Do not edit this file directly.
# Your changes will be overwritten! # Your changes will be overwritten!
# Server # Server
[Interface] [Interface]
PrivateKey = ${system.interface.privateKey} PrivateKey = ${wgInterface.privateKey}
Address = ${system.interface.address4}/${cidr4Block}, ${system.interface.address6}/${cidr6Block} Address = ${ipv4Addr}/${cidr4.prefix}, ${ipv6Addr}/${cidr6.prefix}
ListenPort = ${system.interface.port} ListenPort = ${wgInterface.port}
MTU = ${system.interface.mtu} MTU = ${wgInterface.mtu}
PreUp = ${iptablesTemplate(system.hooks.PreUp, system)} PreUp = ${iptablesTemplate(hooks.preUp, wgInterface)}
PostUp = ${iptablesTemplate(system.hooks.PostUp, system)} PostUp = ${iptablesTemplate(hooks.postUp, wgInterface)}
PreDown = ${iptablesTemplate(system.hooks.PreDown, system)} PreDown = ${iptablesTemplate(hooks.preDown, wgInterface)}
PostDown = ${iptablesTemplate(system.hooks.PostDown, system)}`; PostDown = ${iptablesTemplate(hooks.postDown, wgInterface)}`;
}, },
generateClientConfig: ( generateClientConfig: (
system: DeepReadonly<System>, wgInterface: InterfaceType,
client: DeepReadonly<Client> userConfig: UserConfigType,
client: ClientType
) => { ) => {
const cidr4Block = parseCidr(system.userConfig.address4Range).prefix; const cidr4Block = parseCidr(wgInterface.ipv4Cidr).prefix;
const cidr6Block = parseCidr(system.userConfig.address6Range).prefix; const cidr6Block = parseCidr(wgInterface.ipv6Cidr).prefix;
return `[Interface] return `[Interface]
PrivateKey = ${client.privateKey} PrivateKey = ${client.privateKey}
Address = ${client.address4}/${cidr4Block}, ${client.address6}/${cidr6Block} Address = ${client.ipv4Address}/${cidr4Block}, ${client.ipv6Address}/${cidr6Block}
DNS = ${system.userConfig.defaultDns.join(', ')} DNS = ${client.dns.join(', ')}
MTU = ${client.mtu} MTU = ${client.mtu}
[Peer] [Peer]
PublicKey = ${system.interface.publicKey} PublicKey = ${wgInterface.publicKey}
PresharedKey = ${client.preSharedKey} PresharedKey = ${client.preSharedKey}
AllowedIPs = ${client.allowedIps.join(', ')} AllowedIPs = ${client.allowedIps.join(', ')}
PersistentKeepalive = ${client.persistentKeepalive} PersistentKeepalive = ${client.persistentKeepalive}
Endpoint = ${system.userConfig.host}:${system.userConfig.port}`; Endpoint = ${userConfig.host}:${userConfig.port}`;
}, },
generatePrivateKey: () => { generatePrivateKey: () => {
@ -70,7 +75,7 @@ Endpoint = ${system.userConfig.host}:${system.userConfig.port}`;
}); });
}, },
generatePresharedKey: () => { generatePreSharedKey: () => {
return exec('wg genpsk'); return exec('wg genpsk');
}, },

Loading…
Cancel
Save