Browse Source

streamline references to wg0

database wg0 name makes no sense anymore
wg0 only in database, could be easily replaced, or support for custom name added
pull/1657/head
Bernd Storath 6 months ago
parent
commit
9ec6c4af9c
  1. 4
      .gitignore
  2. 2
      src/.gitignore
  3. 2
      src/drizzle.config.ts
  4. 5
      src/server/api/admin/hooks.get.ts
  5. 2
      src/server/api/admin/hooks.post.ts
  6. 2
      src/server/api/admin/interface/cidr.post.ts
  7. 6
      src/server/api/admin/interface/index.get.ts
  8. 2
      src/server/api/admin/interface/index.post.ts
  9. 5
      src/server/api/admin/userconfig.get.ts
  10. 2
      src/server/api/admin/userconfig.post.ts
  11. 2
      src/server/api/setup/5.post.ts
  12. 12
      src/server/database/repositories/hooks/service.ts
  13. 1
      src/server/database/repositories/interface/schema.ts
  14. 25
      src/server/database/repositories/interface/service.ts
  15. 18
      src/server/database/repositories/userConfig/service.ts
  16. 2
      src/server/database/sqlite.ts
  17. 11
      src/server/routes/metrics/prometheus.get.ts
  18. 62
      src/server/utils/WireGuard.ts

4
.gitignore

@ -1,6 +1,2 @@
/config
/wg0.conf
/wg0.json
/src/node_modules
.DS_Store .DS_Store
*.swp *.swp

2
src/.gitignore

@ -23,4 +23,4 @@ logs
.env.* .env.*
!.env.example !.env.example
wg0.db wg-easy.db

2
src/drizzle.config.ts

@ -5,6 +5,6 @@ export default defineConfig({
schema: './server/database/schema.ts', schema: './server/database/schema.ts',
dialect: 'sqlite', dialect: 'sqlite',
dbCredentials: { dbCredentials: {
url: 'file:./wg0.db', url: 'file:./wg-easy.db',
}, },
}); });

5
src/server/api/admin/hooks.get.ts

@ -1,7 +1,4 @@
export default definePermissionEventHandler(actions.ADMIN, async () => { export default definePermissionEventHandler(actions.ADMIN, async () => {
const hooks = await Database.hooks.get('wg0'); const hooks = await Database.hooks.get();
if (!hooks) {
throw new Error('Hooks not found');
}
return hooks; return hooks;
}); });

2
src/server/api/admin/hooks.post.ts

@ -7,7 +7,7 @@ export default definePermissionEventHandler(
event, event,
validateZod(HooksUpdateSchema, event) validateZod(HooksUpdateSchema, event)
); );
await Database.hooks.update('wg0', data); await Database.hooks.update(data);
await WireGuard.saveConfig(); await WireGuard.saveConfig();
return { success: true }; return { success: true };
} }

2
src/server/api/admin/interface/cidr.post.ts

@ -8,7 +8,7 @@ export default definePermissionEventHandler(
validateZod(InterfaceCidrUpdateSchema, event) validateZod(InterfaceCidrUpdateSchema, event)
); );
await Database.interfaces.updateCidr('wg0', data); await Database.interfaces.updateCidr(data);
await WireGuard.saveConfig(); await WireGuard.saveConfig();
return { success: true }; return { success: true };
} }

6
src/server/api/admin/interface/index.get.ts

@ -1,9 +1,5 @@
export default definePermissionEventHandler(actions.ADMIN, async () => { export default definePermissionEventHandler(actions.ADMIN, async () => {
const wgInterface = await Database.interfaces.get('wg0'); const wgInterface = await Database.interfaces.get();
if (!wgInterface) {
throw new Error('Interface not found');
}
return { return {
...wgInterface, ...wgInterface,

2
src/server/api/admin/interface/index.post.ts

@ -7,7 +7,7 @@ export default definePermissionEventHandler(
event, event,
validateZod(InterfaceUpdateSchema, event) validateZod(InterfaceUpdateSchema, event)
); );
await Database.interfaces.update('wg0', data); await Database.interfaces.update(data);
await WireGuard.saveConfig(); await WireGuard.saveConfig();
return { success: true }; return { success: true };
} }

5
src/server/api/admin/userconfig.get.ts

@ -1,7 +1,4 @@
export default definePermissionEventHandler(actions.ADMIN, async () => { export default definePermissionEventHandler(actions.ADMIN, async () => {
const userConfig = await Database.userConfigs.get('wg0'); const userConfig = await Database.userConfigs.get();
if (!userConfig) {
throw new Error('User config not found');
}
return userConfig; return userConfig;
}); });

2
src/server/api/admin/userconfig.post.ts

@ -7,7 +7,7 @@ export default definePermissionEventHandler(
event, event,
validateZod(UserConfigUpdateSchema, event) validateZod(UserConfigUpdateSchema, event)
); );
await Database.userConfigs.update('wg0', data); await Database.userConfigs.update(data);
await WireGuard.saveConfig(); await WireGuard.saveConfig();
return { success: true }; return { success: true };
} }

2
src/server/api/setup/5.post.ts

@ -5,7 +5,7 @@ export default defineSetupEventHandler(async ({ event }) => {
event, event,
validateZod(UserConfigSetupSchema, event) validateZod(UserConfigSetupSchema, event)
); );
await Database.userConfigs.updateHostPort('wg0', host, port); await Database.userConfigs.updateHostPort(host, port);
await Database.general.setSetupStep(0); await Database.general.setSetupStep(0);
return { success: true }; return { success: true };
}); });

12
src/server/database/repositories/hooks/service.ts

@ -20,15 +20,19 @@ export class HooksService {
this.#statements = createPreparedStatement(db); this.#statements = createPreparedStatement(db);
} }
get(infName: string) { async get() {
return this.#statements.get.execute({ interface: infName }); const hooks = await this.#statements.get.execute({ interface: 'wg0' });
if (!hooks) {
throw new Error('Hooks not found');
}
return hooks;
} }
update(infName: string, data: HooksUpdateType) { update(data: HooksUpdateType) {
return this.#db return this.#db
.update(hooks) .update(hooks)
.set(data) .set(data)
.where(eq(hooks.id, infName)) .where(eq(hooks.id, 'wg0'))
.execute(); .execute();
} }
} }

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

@ -13,6 +13,7 @@ export const wgInterface = sqliteTable('interfaces_table', {
ipv4Cidr: text('ipv4_cidr').notNull(), ipv4Cidr: text('ipv4_cidr').notNull(),
ipv6Cidr: text('ipv6_cidr').notNull(), ipv6Cidr: text('ipv6_cidr').notNull(),
mtu: int().notNull(), mtu: int().notNull(),
// does nothing yet
enabled: int({ mode: 'boolean' }).notNull(), enabled: int({ mode: 'boolean' }).notNull(),
createdAt: text('created_at') createdAt: text('created_at')
.notNull() .notNull()

25
src/server/database/repositories/interface/service.ts

@ -11,7 +11,6 @@ function createPreparedStatement(db: DBType) {
get: db.query.wgInterface get: db.query.wgInterface
.findFirst({ where: eq(wgInterface.name, sql.placeholder('interface')) }) .findFirst({ where: eq(wgInterface.name, sql.placeholder('interface')) })
.prepare(), .prepare(),
getAll: db.query.wgInterface.findMany().prepare(),
updateKeyPair: db updateKeyPair: db
.update(wgInterface) .update(wgInterface)
.set({ .set({
@ -32,31 +31,33 @@ export class InterfaceService {
this.#statements = createPreparedStatement(db); this.#statements = createPreparedStatement(db);
} }
get(infName: string) { async get() {
return this.#statements.get.execute({ interface: infName }); const wgInterface = await this.#statements.get.execute({
interface: 'wg0',
});
if (!wgInterface) {
throw new Error('Interface not found');
} }
return wgInterface;
getAll() {
return this.#statements.getAll.execute();
} }
updateKeyPair(infName: string, privateKey: string, publicKey: string) { updateKeyPair(privateKey: string, publicKey: string) {
return this.#statements.updateKeyPair.execute({ return this.#statements.updateKeyPair.execute({
interface: infName, interface: 'wg0',
privateKey, privateKey,
publicKey, publicKey,
}); });
} }
update(infName: string, data: InterfaceUpdateType) { update(data: InterfaceUpdateType) {
return this.#db return this.#db
.update(wgInterface) .update(wgInterface)
.set(data) .set(data)
.where(eq(wgInterface.name, infName)) .where(eq(wgInterface.name, 'wg0'))
.execute(); .execute();
} }
updateCidr(infName: string, data: InterfaceCidrUpdateType) { updateCidr(data: InterfaceCidrUpdateType) {
if (!isCidr(data.ipv4Cidr) || !isCidr(data.ipv6Cidr)) { if (!isCidr(data.ipv4Cidr) || !isCidr(data.ipv6Cidr)) {
throw new Error('Invalid CIDR'); throw new Error('Invalid CIDR');
} }
@ -64,7 +65,7 @@ export class InterfaceService {
await tx await tx
.update(wgInterface) .update(wgInterface)
.set(data) .set(data)
.where(eq(wgInterface.name, infName)) .where(eq(wgInterface.name, 'wg0'))
.execute(); .execute();
const clients = await tx.query.client.findMany().execute(); const clients = await tx.query.client.findMany().execute();

18
src/server/database/repositories/userConfig/service.ts

@ -28,23 +28,29 @@ export class UserConfigService {
this.#statements = createPreparedStatement(db); this.#statements = createPreparedStatement(db);
} }
get(infName: string) { async get() {
return this.#statements.get.execute({ interface: infName }); const userConfig = await this.#statements.get.execute({ interface: 'wg0' });
if (!userConfig) {
throw new Error('User config not found');
}
return userConfig;
} }
updateHostPort(infName: string, host: string, port: number) { updateHostPort(host: string, port: number) {
return this.#statements.updateHostPort.execute({ return this.#statements.updateHostPort.execute({
interface: infName, interface: 'wg0',
host, host,
port, port,
}); });
} }
update(infName: string, data: UserConfigUpdateType) { update(data: UserConfigUpdateType) {
return this.#db return this.#db
.update(userConfig) .update(userConfig)
.set(data) .set(data)
.where(eq(userConfig.id, infName)) .where(eq(userConfig.id, 'wg0'))
.execute(); .execute();
} }
} }

2
src/server/database/sqlite.ts

@ -14,7 +14,7 @@ import { OneTimeLinkService } from './repositories/oneTimeLink/service';
const DB_DEBUG = debug('Database'); const DB_DEBUG = debug('Database');
const client = createClient({ url: 'file:/etc/wireguard/wg0.db' }); const client = createClient({ url: 'file:/etc/wireguard/wg-easy.db' });
const db = drizzle({ client, schema }); const db = drizzle({ client, schema });
export async function connect() { export async function connect() {

11
src/server/routes/metrics/prometheus.get.ts

@ -4,6 +4,7 @@ export default defineMetricsHandler('prometheus', async ({ event }) => {
}); });
async function getPrometheusResponse() { async function getPrometheusResponse() {
const wgInterface = await Database.interfaces.get();
const clients = await WireGuard.getClients(); const clients = await WireGuard.getClients();
let wireguardPeerCount = 0; let wireguardPeerCount = 0;
let wireguardEnabledPeersCount = 0; let wireguardEnabledPeersCount = 0;
@ -21,7 +22,7 @@ async function getPrometheusResponse() {
wireguardConnectedPeersCount++; wireguardConnectedPeersCount++;
} }
const id = `interface="wg0",enabled="${client.enabled}",ipv4Address="${client.ipv4Address}",ipv6Address="${client.ipv6Address}",name="${client.name}"`; const id = `interface="${wgInterface.name}",enabled="${client.enabled}",ipv4Address="${client.ipv4Address}",ipv6Address="${client.ipv6Address}",name="${client.name}"`;
wireguardSentBytes.push( wireguardSentBytes.push(
`wireguard_sent_bytes{${id}} ${client.transferTx ?? 0}` `wireguard_sent_bytes{${id}} ${client.transferTx ?? 0}`
@ -35,20 +36,22 @@ async function getPrometheusResponse() {
); );
} }
const id = `interface="${wgInterface.name}"`;
const returnText = [ const returnText = [
'# HELP wg-easy and wireguard metrics', '# HELP wg-easy and wireguard metrics',
'', '',
'# HELP wireguard_configured_peers', '# HELP wireguard_configured_peers',
'# TYPE wireguard_configured_peers gauge', '# TYPE wireguard_configured_peers gauge',
`wireguard_configured_peers{interface="wg0"} ${wireguardPeerCount}`, `wireguard_configured_peers{${id}} ${wireguardPeerCount}`,
'', '',
'# HELP wireguard_enabled_peers', '# HELP wireguard_enabled_peers',
'# TYPE wireguard_enabled_peers gauge', '# TYPE wireguard_enabled_peers gauge',
`wireguard_enabled_peers{interface="wg0"} ${wireguardEnabledPeersCount}`, `wireguard_enabled_peers{${id}} ${wireguardEnabledPeersCount}`,
'', '',
'# HELP wireguard_connected_peers', '# HELP wireguard_connected_peers',
'# TYPE wireguard_connected_peers gauge', '# TYPE wireguard_connected_peers gauge',
`wireguard_connected_peers{interface="wg0"} ${wireguardConnectedPeersCount}`, `wireguard_connected_peers{${id}} ${wireguardConnectedPeersCount}`,
'', '',
'# HELP wireguard_sent_bytes Bytes sent to the peer', '# HELP wireguard_sent_bytes Bytes sent to the peer',
'# TYPE wireguard_sent_bytes counter', '# TYPE wireguard_sent_bytes counter',

62
src/server/utils/WireGuard.ts

@ -2,6 +2,7 @@ import fs from 'node:fs/promises';
import debug from 'debug'; import debug from 'debug';
import QRCode from 'qrcode'; import QRCode from 'qrcode';
import type { ID } from '#db/schema'; import type { ID } from '#db/schema';
import type { InterfaceType } from '#db/repositories/interface/types';
const WG_DEBUG = debug('WireGuard'); const WG_DEBUG = debug('WireGuard');
@ -10,21 +11,17 @@ class WireGuard {
* Save and sync config * Save and sync config
*/ */
async saveConfig() { async saveConfig() {
await this.#saveWireguardConfig('wg0'); const wgInterface = await Database.interfaces.get();
await this.#syncWireguardConfig('wg0'); await this.#saveWireguardConfig(wgInterface);
await this.#syncWireguardConfig(wgInterface);
} }
/** /**
* Generates and saves WireGuard config from database as wg0 * Generates and saves WireGuard config from database
*/ */
async #saveWireguardConfig(infName: string) { async #saveWireguardConfig(wgInterface: InterfaceType) {
const wgInterface = await Database.interfaces.get(infName);
const clients = await Database.clients.getAll(); const clients = await Database.clients.getAll();
const hooks = await Database.hooks.get(infName); const hooks = await Database.hooks.get();
if (!wgInterface || !hooks) {
throw new Error('Interface or Hooks not found');
}
const result = []; const result = [];
result.push(wg.generateServerInterface(wgInterface, hooks)); result.push(wg.generateServerInterface(wgInterface, hooks));
@ -37,19 +34,24 @@ class WireGuard {
} }
WG_DEBUG('Saving Config...'); WG_DEBUG('Saving Config...');
await fs.writeFile(`/etc/wireguard/${infName}.conf`, result.join('\n\n'), { await fs.writeFile(
`/etc/wireguard/${wgInterface.name}.conf`,
result.join('\n\n'),
{
mode: 0o600, mode: 0o600,
}); }
);
WG_DEBUG('Config saved successfully.'); WG_DEBUG('Config saved successfully.');
} }
async #syncWireguardConfig(infName: string) { async #syncWireguardConfig(wgInterface: InterfaceType) {
WG_DEBUG('Syncing Config...'); WG_DEBUG('Syncing Config...');
await wg.sync(infName); await wg.sync(wgInterface.name);
WG_DEBUG('Config synced successfully.'); WG_DEBUG('Config synced successfully.');
} }
async getClients() { async getClients() {
const wgInterface = await Database.interfaces.get();
const dbClients = await Database.clients.getAll(); const dbClients = await Database.clients.getAll();
const clients = dbClients.map((client) => ({ const clients = dbClients.map((client) => ({
...client, ...client,
@ -60,7 +62,7 @@ class WireGuard {
})); }));
// Loop WireGuard status // Loop WireGuard status
const dump = await wg.dump('wg0'); const dump = await wg.dump(wgInterface.name);
dump.forEach( dump.forEach(
({ publicKey, latestHandshakeAt, endpoint, transferRx, transferTx }) => { ({ publicKey, latestHandshakeAt, endpoint, transferRx, transferTx }) => {
const client = clients.find((client) => client.publicKey === publicKey); const client = clients.find((client) => client.publicKey === publicKey);
@ -79,12 +81,8 @@ class WireGuard {
} }
async getClientConfiguration({ clientId }: { clientId: ID }) { async getClientConfiguration({ clientId }: { clientId: ID }) {
const wgInterface = await Database.interfaces.get('wg0'); const wgInterface = await Database.interfaces.get();
const userConfig = await Database.userConfigs.get('wg0'); const userConfig = await Database.userConfigs.get();
if (!wgInterface || !userConfig) {
throw new Error('Interface or UserConfig not found');
}
const client = await Database.clients.get(clientId); const client = await Database.clients.get(clientId);
@ -124,11 +122,8 @@ class WireGuard {
async Startup() { async Startup() {
WG_DEBUG('Starting WireGuard...'); WG_DEBUG('Starting WireGuard...');
const wgInterfaces = await Database.interfaces.getAll(); const wgInterface = await Database.interfaces.get();
for (const wgInterface of wgInterfaces) {
if (wgInterface.enabled !== true) {
continue;
}
// default interface has no keys // default interface has no keys
if ( if (
wgInterface.privateKey === '---default---' && wgInterface.privateKey === '---default---' &&
@ -138,15 +133,11 @@ class WireGuard {
const privateKey = await wg.generatePrivateKey(); const privateKey = await wg.generatePrivateKey();
const publicKey = await wg.getPublicKey(privateKey); const publicKey = await wg.getPublicKey(privateKey);
await Database.interfaces.updateKeyPair( await Database.interfaces.updateKeyPair(privateKey, publicKey);
wgInterface.name,
privateKey,
publicKey
);
WG_DEBUG('New Wireguard Keys generated successfully.'); WG_DEBUG('New Wireguard Keys generated successfully.');
} }
WG_DEBUG(`Starting Wireguard Interface ${wgInterface.name}...`); WG_DEBUG(`Starting Wireguard Interface ${wgInterface.name}...`);
await this.#saveWireguardConfig(wgInterface.name); await this.#saveWireguardConfig(wgInterface);
await wg.down(wgInterface.name).catch(() => {}); await wg.down(wgInterface.name).catch(() => {});
await wg.up(wgInterface.name).catch((err) => { await wg.up(wgInterface.name).catch((err) => {
if ( if (
@ -162,9 +153,8 @@ class WireGuard {
throw err; throw err;
}); });
await this.#syncWireguardConfig(wgInterface.name); await this.#syncWireguardConfig(wgInterface);
WG_DEBUG(`Wireguard Interface ${wgInterface.name} started successfully.`); WG_DEBUG(`Wireguard Interface ${wgInterface.name} started successfully.`);
}
WG_DEBUG('Starting Cron Job...'); WG_DEBUG('Starting Cron Job...');
await this.startCronJob(); await this.startCronJob();
@ -184,11 +174,9 @@ class WireGuard {
// Shutdown wireguard // Shutdown wireguard
async Shutdown() { async Shutdown() {
const wgInterfaces = await Database.interfaces.getAll(); const wgInterface = await Database.interfaces.get();
for (const wgInterface of wgInterfaces) {
await wg.down(wgInterface.name).catch(() => {}); await wg.down(wgInterface.name).catch(() => {});
} }
}
async cronJob() { async cronJob() {
const clients = await Database.clients.getAll(); const clients = await Database.clients.getAll();

Loading…
Cancel
Save