feat(auth,client-registry): add Noise IK client authentication with managed client registry and per-client ACL controls

This commit is contained in:
2026-03-29 17:04:27 +00:00
parent 187a69028b
commit 01a0d8b9f4
20 changed files with 1930 additions and 897 deletions

View File

@@ -1,7 +1,7 @@
import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as net from 'net';
import { VpnClient, VpnServer } from '../ts/index.js';
import type { IVpnClientOptions, IVpnServerOptions, IVpnKeypair, IVpnServerConfig } from '../ts/index.js';
import type { IVpnClientOptions, IVpnServerOptions, IVpnKeypair, IVpnServerConfig, IClientConfigBundle } from '../ts/index.js';
// ---------------------------------------------------------------------------
// Helpers
@@ -40,7 +40,9 @@ let server: VpnServer;
let serverPort: number;
let keypair: IVpnKeypair;
let client: VpnClient;
let clientBundle: IClientConfigBundle;
const extraClients: VpnClient[] = [];
const extraBundles: IClientConfigBundle[] = [];
// ---------------------------------------------------------------------------
// Tests
@@ -64,7 +66,7 @@ tap.test('setup: start VPN server', async () => {
expect(keypair.publicKey).toBeTypeofString();
expect(keypair.privateKey).toBeTypeofString();
// Phase 3: start the VPN listener
// Phase 3: start the VPN listener (empty clients, will use createClient at runtime)
const serverConfig: IVpnServerConfig = {
listenAddr: `127.0.0.1:${serverPort}`,
privateKey: keypair.privateKey,
@@ -76,6 +78,11 @@ tap.test('setup: start VPN server', async () => {
// Verify server is now running
const status = await server.getStatus();
expect(status.state).toEqual('connected');
// Phase 4: create the first client via the hub
clientBundle = await server.createClient({ clientId: 'test-client-0' });
expect(clientBundle.secrets.noisePrivateKey).toBeTypeofString();
expect(clientBundle.smartvpnConfig.clientPublicKey).toBeTypeofString();
});
tap.test('single client connects and gets IP', async () => {
@@ -89,6 +96,8 @@ tap.test('single client connects and gets IP', async () => {
const result = await client.connect({
serverUrl: `ws://127.0.0.1:${serverPort}`,
serverPublicKey: keypair.publicKey,
clientPrivateKey: clientBundle.secrets.noisePrivateKey,
clientPublicKey: clientBundle.smartvpnConfig.clientPublicKey,
keepaliveIntervalSecs: 3,
});
@@ -175,11 +184,15 @@ tap.test('5 concurrent clients', async () => {
assignedIps.add(existingClients[0].assignedIp);
for (let i = 0; i < 5; i++) {
const bundle = await server.createClient({ clientId: `test-client-${i + 1}` });
extraBundles.push(bundle);
const c = new VpnClient({ transport: { transport: 'stdio' } });
await c.start();
const result = await c.connect({
serverUrl: `ws://127.0.0.1:${serverPort}`,
serverPublicKey: keypair.publicKey,
clientPrivateKey: bundle.secrets.noisePrivateKey,
clientPublicKey: bundle.smartvpnConfig.clientPublicKey,
keepaliveIntervalSecs: 3,
});
expect(result.assignedIp).toStartWith('10.8.0.');