opnsense: filter queried connections

This commit is contained in:
2024-12-22 00:55:33 -08:00
parent e03bf11fa5
commit bdea663178
12 changed files with 155 additions and 54 deletions

View File

@@ -59,3 +59,11 @@ export interface OpnsenseWgPeers {
* };
* ```
*/
export interface OpnsenseWgServers {
status: "ok" | string | number;
rows: {
name: string;
uuid: string;
}[];
}

View File

@@ -23,7 +23,7 @@ export async function createSession(userId: string): Promise<table.Session> {
userId,
expiresAt: new Date(Date.now() + DAY_IN_MS * 30)
};
await db.insert(table.session).values(session);
await db.insert(table.sessions).values(session);
return session;
}
@@ -38,7 +38,7 @@ export function setSessionTokenCookie(event: RequestEvent, sessionId: string, ex
}
export async function invalidateSession(sessionId: string): Promise<void> {
await db.delete(table.session).where(eq(table.session.id, sessionId));
await db.delete(table.sessions).where(eq(table.sessions.id, sessionId));
}
export function deleteSessionTokenCookie(event: RequestEvent) {
@@ -49,12 +49,12 @@ export async function validateSession(sessionId: string) {
const [result] = await db
.select({
// Adjust user table here to tweak returned data
user: { id: table.user.id, username: table.user.username, name: table.user.name },
session: table.session
user: { id: table.users.id, username: table.users.username, name: table.users.name },
session: table.sessions
})
.from(table.session)
.innerJoin(table.user, eq(table.session.userId, table.user.id))
.where(eq(table.session.id, sessionId));
.from(table.sessions)
.innerJoin(table.users, eq(table.sessions.userId, table.users.id))
.where(eq(table.sessions.id, sessionId));
if (!result) {
return { session: null, user: null };
@@ -63,7 +63,7 @@ export async function validateSession(sessionId: string) {
const sessionExpired = Date.now() >= session.expiresAt.getTime();
if (sessionExpired) {
await db.delete(table.session).where(eq(table.session.id, session.id));
await db.delete(table.sessions).where(eq(table.sessions.id, session.id));
return { session: null, user: null };
}
@@ -71,9 +71,9 @@ export async function validateSession(sessionId: string) {
if (renewSession) {
session.expiresAt = new Date(Date.now() + DAY_IN_MS * 30);
await db
.update(table.session)
.update(table.sessions)
.set({ expiresAt: session.expiresAt })
.where(eq(table.session.id, session.id));
.where(eq(table.sessions.id, session.id));
}
return { session, user };

View File

@@ -1,8 +1,7 @@
import { drizzle } from 'drizzle-orm/better-sqlite3';
import Database from 'better-sqlite3';
import { env } from '$env/dynamic/private';
import { drizzle } from 'drizzle-orm/libsql';
import assert from 'node:assert';
import * as schema from './schema';
import { DATABASE_URL } from '$env/static/private';
assert(env.DATABASE_URL, 'DATABASE_URL is not set');
const client = new Database(env.DATABASE_URL);
export const db = drizzle(client);
assert(DATABASE_URL, 'DATABASE_URL is not set');
export const db= drizzle(DATABASE_URL, { schema });

View File

@@ -1,19 +1,59 @@
import { sqliteTable, text, integer } from 'drizzle-orm/sqlite-core';
import { relations } from 'drizzle-orm';
export const user = sqliteTable('user', {
export const users = sqliteTable('users', {
id: text('id').primaryKey(),
username: text('username').notNull(),
name: text('name').notNull(),
});
export const session = sqliteTable('session', {
export const usersRelations = relations(users, ({ many }) => ({
wgClients: many(wgClients),
}));
export const sessions = sqliteTable('sessions', {
id: text('id').primaryKey(),
userId: text('user_id')
.notNull()
.references(() => user.id),
expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull()
.references(() => users.id),
expiresAt: integer('expires_at', { mode: 'timestamp' }).notNull(),
});
export type Session = typeof session.$inferSelect;
export const ipAllocations = sqliteTable('ip_allocations', {
// for now, id will be the same as the ipIndex
id: integer('id').primaryKey({ autoIncrement: true }),
// clientId is nullable because allocations can remain after the client is deleted
// unique for now, only allowing one allocation per client
clientId: integer('client_id')
.unique()
.references(() => wgClients.id),
});
export type User = typeof user.$inferSelect;
export const wgClients = sqliteTable('wg_clients', {
id: integer().primaryKey({ autoIncrement: true }),
userId: text('user_id')
.notNull()
.references(() => users.id),
name: text('name').notNull(),
// questioning whether this should be nullable
opnsenseId: text('opnsense_id'),
publicKey: text('public_key').notNull().unique(),
// nullable for the possibility of a client supplying their own private key
privateKey: text('private_key'),
// nullable for the possibility of no psk
preSharedKey: text('pre_shared_key'),
// discarded ideas:
// (mostly because they make finding the next available ipIndex difficult)
// ipIndex: integer('ip_index').notNull().unique(),
// allowedIps: text('allowed_ips').notNull(),
});
export const wgClientsRelations = relations(wgClients, ({ one }) => ({
ipAllocation: one(ipAllocations),
}));
export type WgClient = typeof wgClients.$inferSelect;
export type Session = typeof sessions.$inferSelect;
export type User = typeof users.$inferSelect;

29
src/lib/server/db/seed.ts Normal file
View File

@@ -0,0 +1,29 @@
import { users, wgClients } from './schema';
import { eq } from 'drizzle-orm';
import assert from 'node:assert';
import { drizzle } from 'drizzle-orm/libsql';
import * as schema from '$lib/server/db/schema';
assert(process.env.DATABASE_URL, 'DATABASE_URL is not set');
const db = drizzle(process.env.DATABASE_URL, { schema });
export async function seed() {
const user = await db.query.users.findFirst({ where: eq(users.username, 'CaZzzer') });
assert(user, 'User not found');
const clients = [
{
userId: user.id,
name: 'Client1',
publicKey: 'BJ5faPVJsDP4CCxNYilmKnwlQXOtXEOJjqIwb4U/CgM=',
privateKey: 'KKqsHDu30WCSrVsyzMkOKbE3saQ+wlx0sBwGs61UGXk=',
preSharedKey: '0LWopbrISXBNHUxr+WOhCSAg+0hD8j3TLmpyzHkBHCQ=',
ipIndex: 1,
// allowedIps: '10.18.11.101/32,fd00::1/112',
}
]
await db.insert(wgClients).values(clients);
}
seed();

View File

@@ -1,16 +0,0 @@
import { env } from '$env/dynamic/private';
import assert from 'node:assert';
import { encodeBasicCredentials } from 'arctic/dist/request';
import { dev } from '$app/environment';
assert(env.OPNSENSE_API_URL, 'OPNSENSE_API_URL is not set');
assert(env.OPNSENSE_API_KEY, 'OPNSENSE_API_KEY is not set');
assert(env.OPNSENSE_API_SECRET, 'OPNSENSE_API_SECRET is not set');
assert(env.OPNSENSE_WG_IFNAME, 'OPNSENSE_WG_IFNAME is not set');
export const opnsenseUrl = env.OPNSENSE_API_URL;
export const opnsenseAuth = "Basic " + encodeBasicCredentials(env.OPNSENSE_API_KEY, env.OPNSENSE_API_SECRET);
export const opnsenseIfname = env.OPNSENSE_WG_IFNAME;
// unset secret for security
if (!dev) env.OPNSENSE_API_SECRET = "";

View File

@@ -0,0 +1,33 @@
import { env } from '$env/dynamic/private';
import assert from 'node:assert';
import { encodeBasicCredentials } from 'arctic/dist/request';
import { dev } from '$app/environment';
import type { OpnsenseWgServers } from '$lib/opnsense/wg';
assert(env.OPNSENSE_API_URL, 'OPNSENSE_API_URL is not set');
assert(env.OPNSENSE_API_KEY, 'OPNSENSE_API_KEY is not set');
assert(env.OPNSENSE_API_SECRET, 'OPNSENSE_API_SECRET is not set');
assert(env.OPNSENSE_WG_IFNAME, 'OPNSENSE_WG_IFNAME is not set');
export const opnsenseUrl = env.OPNSENSE_API_URL;
export const opnsenseAuth = "Basic " + encodeBasicCredentials(env.OPNSENSE_API_KEY, env.OPNSENSE_API_SECRET);
export const opnsenseIfname = env.OPNSENSE_WG_IFNAME;
// unset secret for security
if (!dev) env.OPNSENSE_API_SECRET = "";
// this might be pretty bad if the server is down and in a bunch of other cases
// TODO: write a retry loop later
const resServers = await fetch(`${opnsenseUrl}/api/wireguard/client/list_servers`, {
method: 'GET',
headers: {
Authorization: opnsenseAuth,
Accept: 'application/json',
}
});
assert(resServers.ok, 'Failed to fetch OPNsense WireGuard servers');
const servers = await resServers.json() as OpnsenseWgServers;
assert.equal(servers.status, 'ok', 'Failed to fetch OPNsense WireGuard servers');
export const serverUuid = servers.rows.find(server => server.name === opnsenseIfname)?.uuid;
assert(serverUuid, 'Failed to find server UUID for OPNsense WireGuard server');
console.log('OPNsense WireGuard server UUID:', serverUuid);