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

@@ -2,7 +2,7 @@ import { tap, expect } from '@git.zone/tstest/tapbundle';
import * as net from 'net';
import * as dgram from 'dgram';
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
@@ -82,6 +82,8 @@ tap.test('setup: start VPN server in QUIC mode', async () => {
});
tap.test('QUIC client connects and gets IP', async () => {
const bundle = await server.createClient({ clientId: 'quic-client-1' });
const options: IVpnClientOptions = {
transport: { transport: 'stdio' },
};
@@ -92,6 +94,8 @@ tap.test('QUIC client connects and gets IP', async () => {
const result = await client.connect({
serverUrl: `127.0.0.1:${quicPort}`,
serverPublicKey: keypair.publicKey,
clientPrivateKey: bundle.secrets.noisePrivateKey,
clientPublicKey: bundle.smartvpnConfig.clientPublicKey,
transport: 'quic',
keepaliveIntervalSecs: 3,
});
@@ -162,12 +166,16 @@ tap.test('auto client connects to dual-mode server (QUIC preferred)', async () =
const started = await client.start();
expect(started).toBeTrue();
const bundle = await dualServer.createClient({ clientId: 'dual-auto-client' });
// "auto" mode (default): tries QUIC first at same host:port, falls back to WS
// Since the WS port and QUIC port differ, auto will try QUIC on WS port (fail),
// then fall back to WebSocket
const result = await client.connect({
serverUrl: `ws://127.0.0.1:${dualWsPort}`,
serverPublicKey: dualKeypair.publicKey,
clientPrivateKey: bundle.secrets.noisePrivateKey,
clientPublicKey: bundle.smartvpnConfig.clientPublicKey,
// transport defaults to 'auto'
keepaliveIntervalSecs: 3,
});
@@ -187,6 +195,8 @@ tap.test('auto client connects to dual-mode server (QUIC preferred)', async () =
});
tap.test('explicit QUIC client connects to dual-mode server', async () => {
const bundle = await dualServer.createClient({ clientId: 'dual-quic-client' });
const options: IVpnClientOptions = {
transport: { transport: 'stdio' },
};
@@ -197,6 +207,8 @@ tap.test('explicit QUIC client connects to dual-mode server', async () => {
const result = await client.connect({
serverUrl: `127.0.0.1:${dualQuicPort}`,
serverPublicKey: dualKeypair.publicKey,
clientPrivateKey: bundle.secrets.noisePrivateKey,
clientPublicKey: bundle.smartvpnConfig.clientPublicKey,
transport: 'quic',
keepaliveIntervalSecs: 3,
});
@@ -211,6 +223,8 @@ tap.test('explicit QUIC client connects to dual-mode server', async () => {
});
tap.test('keepalive exchange over QUIC', async () => {
const bundle = await dualServer.createClient({ clientId: 'dual-keepalive-client' });
const options: IVpnClientOptions = {
transport: { transport: 'stdio' },
};
@@ -220,6 +234,8 @@ tap.test('keepalive exchange over QUIC', async () => {
await client.connect({
serverUrl: `127.0.0.1:${dualQuicPort}`,
serverPublicKey: dualKeypair.publicKey,
clientPrivateKey: bundle.secrets.noisePrivateKey,
clientPublicKey: bundle.smartvpnConfig.clientPublicKey,
transport: 'quic',
keepaliveIntervalSecs: 3,
});