termify-agent 1.0.32 → 1.0.34

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/dist/agent.d.ts +13 -0
  2. package/dist/agent.d.ts.map +1 -1
  3. package/dist/agent.js +236 -0
  4. package/dist/agent.js.map +1 -1
  5. package/dist/config.d.ts +2 -0
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +5 -0
  8. package/dist/config.js.map +1 -1
  9. package/dist/index.js +184 -15
  10. package/dist/index.js.map +1 -1
  11. package/dist/network/backends/windows-wireguard-backend.d.ts +29 -0
  12. package/dist/network/backends/windows-wireguard-backend.d.ts.map +1 -0
  13. package/dist/network/backends/windows-wireguard-backend.js +190 -0
  14. package/dist/network/backends/windows-wireguard-backend.js.map +1 -0
  15. package/dist/network/key-rotation.d.ts +55 -0
  16. package/dist/network/key-rotation.d.ts.map +1 -0
  17. package/dist/network/key-rotation.js +105 -0
  18. package/dist/network/key-rotation.js.map +1 -0
  19. package/dist/network/nat-traversal.d.ts +27 -0
  20. package/dist/network/nat-traversal.d.ts.map +1 -0
  21. package/dist/network/nat-traversal.js +76 -0
  22. package/dist/network/nat-traversal.js.map +1 -0
  23. package/dist/network/uapi-client.d.ts +50 -0
  24. package/dist/network/uapi-client.d.ts.map +1 -0
  25. package/dist/network/uapi-client.js +260 -0
  26. package/dist/network/uapi-client.js.map +1 -0
  27. package/dist/network/wireguard-backend.d.ts +60 -0
  28. package/dist/network/wireguard-backend.d.ts.map +1 -0
  29. package/dist/network/wireguard-backend.js +9 -0
  30. package/dist/network/wireguard-backend.js.map +1 -0
  31. package/dist/network/wireguard-installer.d.ts +24 -0
  32. package/dist/network/wireguard-installer.d.ts.map +1 -0
  33. package/dist/network/wireguard-installer.js +362 -0
  34. package/dist/network/wireguard-installer.js.map +1 -0
  35. package/dist/network/wireguard-manager.d.ts +92 -0
  36. package/dist/network/wireguard-manager.d.ts.map +1 -0
  37. package/dist/network/wireguard-manager.js +575 -0
  38. package/dist/network/wireguard-manager.js.map +1 -0
  39. package/dist/network/wireguard-state-store.d.ts +55 -0
  40. package/dist/network/wireguard-state-store.d.ts.map +1 -0
  41. package/dist/network/wireguard-state-store.js +196 -0
  42. package/dist/network/wireguard-state-store.js.map +1 -0
  43. package/dist/ssh-manager.d.ts +5 -0
  44. package/dist/ssh-manager.d.ts.map +1 -1
  45. package/dist/ssh-manager.js +104 -0
  46. package/dist/ssh-manager.js.map +1 -1
  47. package/dist/tunnel-manager.d.ts +65 -0
  48. package/dist/tunnel-manager.d.ts.map +1 -0
  49. package/dist/tunnel-manager.js +267 -0
  50. package/dist/tunnel-manager.js.map +1 -0
  51. package/dist/ws-client.d.ts +145 -0
  52. package/dist/ws-client.d.ts.map +1 -1
  53. package/dist/ws-client.js +136 -1
  54. package/dist/ws-client.js.map +1 -1
  55. package/package.json +1 -1
  56. package/scripts/postinstall.js +100 -35
@@ -0,0 +1,55 @@
1
+ import { EventEmitter } from 'node:events';
2
+ export interface KeyRotationConfig {
3
+ /** Interval between key rotations in ms (default: 24 hours) */
4
+ rotationIntervalMs: number;
5
+ /** Random jitter to prevent thundering herd (default: up to 1 hour) */
6
+ jitterMs: number;
7
+ }
8
+ /**
9
+ * Manages periodic key rotation for WireGuard.
10
+ *
11
+ * Flow:
12
+ * 1. Timer fires -> emits 'rotate-needed' event
13
+ * 2. WireGuardManager generates new keypair
14
+ * 3. Agent sends agent.wg.rotate.propose to server
15
+ * 4. Server distributes update to peers
16
+ * 5. Peers ACK with agent.wg.rotate.applied
17
+ * 6. Server sends server.wg.rotate.commit to agent
18
+ * 7. Agent applies the new key on its interface
19
+ *
20
+ * Events:
21
+ * - 'rotate-needed' : time to rotate
22
+ * - 'rotate-committed' : { keyVersion } -- rotation confirmed by server
23
+ */
24
+ export declare class KeyRotation extends EventEmitter {
25
+ private config;
26
+ private rotationTimer;
27
+ private _currentVersion;
28
+ private _pendingVersion;
29
+ private _isRotating;
30
+ constructor(config?: Partial<KeyRotationConfig>);
31
+ get currentVersion(): number;
32
+ get isRotating(): boolean;
33
+ /**
34
+ * Start the rotation timer.
35
+ */
36
+ start(): void;
37
+ /**
38
+ * Stop the rotation timer.
39
+ */
40
+ stop(): void;
41
+ /**
42
+ * Called when the agent initiates a rotation (increments version, marks rotating).
43
+ */
44
+ proposeRotation(): number;
45
+ /**
46
+ * Called when server confirms the rotation is committed.
47
+ */
48
+ confirmRotation(keyVersion: number): void;
49
+ /**
50
+ * Force an immediate rotation (for security incidents).
51
+ */
52
+ forceRotation(): void;
53
+ private scheduleNextRotation;
54
+ }
55
+ //# sourceMappingURL=key-rotation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-rotation.d.ts","sourceRoot":"","sources":["../../src/network/key-rotation.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,MAAM,WAAW,iBAAiB;IAChC,+DAA+D;IAC/D,kBAAkB,EAAE,MAAM,CAAC;IAC3B,uEAAuE;IACvE,QAAQ,EAAE,MAAM,CAAC;CAClB;AAOD;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,WAAY,SAAQ,YAAY;IAC3C,OAAO,CAAC,MAAM,CAAoB;IAClC,OAAO,CAAC,aAAa,CAA+B;IACpD,OAAO,CAAC,eAAe,CAAa;IACpC,OAAO,CAAC,eAAe,CAAuB;IAC9C,OAAO,CAAC,WAAW,CAAkB;gBAEzB,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC;IAK/C,IAAI,cAAc,IAAI,MAAM,CAE3B;IAED,IAAI,UAAU,IAAI,OAAO,CAExB;IAED;;OAEG;IACH,KAAK,IAAI,IAAI;IAMb;;OAEG;IACH,IAAI,IAAI,IAAI;IASZ;;OAEG;IACH,eAAe,IAAI,MAAM;IAOzB;;OAEG;IACH,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAWzC;;OAEG;IACH,aAAa,IAAI,IAAI;IAQrB,OAAO,CAAC,oBAAoB;CAgB7B"}
@@ -0,0 +1,105 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { logger } from '../utils/logger.js';
3
+ const DEFAULT_CONFIG = {
4
+ rotationIntervalMs: 24 * 60 * 60 * 1000, // 24 hours
5
+ jitterMs: 60 * 60 * 1000, // 1 hour jitter
6
+ };
7
+ /**
8
+ * Manages periodic key rotation for WireGuard.
9
+ *
10
+ * Flow:
11
+ * 1. Timer fires -> emits 'rotate-needed' event
12
+ * 2. WireGuardManager generates new keypair
13
+ * 3. Agent sends agent.wg.rotate.propose to server
14
+ * 4. Server distributes update to peers
15
+ * 5. Peers ACK with agent.wg.rotate.applied
16
+ * 6. Server sends server.wg.rotate.commit to agent
17
+ * 7. Agent applies the new key on its interface
18
+ *
19
+ * Events:
20
+ * - 'rotate-needed' : time to rotate
21
+ * - 'rotate-committed' : { keyVersion } -- rotation confirmed by server
22
+ */
23
+ export class KeyRotation extends EventEmitter {
24
+ config;
25
+ rotationTimer = null;
26
+ _currentVersion = 0;
27
+ _pendingVersion = null;
28
+ _isRotating = false;
29
+ constructor(config) {
30
+ super();
31
+ this.config = { ...DEFAULT_CONFIG, ...config };
32
+ }
33
+ get currentVersion() {
34
+ return this._currentVersion;
35
+ }
36
+ get isRotating() {
37
+ return this._isRotating;
38
+ }
39
+ /**
40
+ * Start the rotation timer.
41
+ */
42
+ start() {
43
+ if (this.rotationTimer)
44
+ return;
45
+ this.scheduleNextRotation();
46
+ logger.info(`[KeyRotation] Started with interval=${this.config.rotationIntervalMs}ms, jitter=${this.config.jitterMs}ms`);
47
+ }
48
+ /**
49
+ * Stop the rotation timer.
50
+ */
51
+ stop() {
52
+ if (this.rotationTimer) {
53
+ clearTimeout(this.rotationTimer);
54
+ this.rotationTimer = null;
55
+ }
56
+ this._isRotating = false;
57
+ this._pendingVersion = null;
58
+ }
59
+ /**
60
+ * Called when the agent initiates a rotation (increments version, marks rotating).
61
+ */
62
+ proposeRotation() {
63
+ this._currentVersion++;
64
+ this._pendingVersion = this._currentVersion;
65
+ this._isRotating = true;
66
+ return this._currentVersion;
67
+ }
68
+ /**
69
+ * Called when server confirms the rotation is committed.
70
+ */
71
+ confirmRotation(keyVersion) {
72
+ if (this._pendingVersion === keyVersion) {
73
+ this._isRotating = false;
74
+ this._pendingVersion = null;
75
+ this.emit('rotate-committed', { keyVersion });
76
+ logger.info(`[KeyRotation] Rotation committed: v${keyVersion}`);
77
+ // Schedule next rotation
78
+ this.scheduleNextRotation();
79
+ }
80
+ }
81
+ /**
82
+ * Force an immediate rotation (for security incidents).
83
+ */
84
+ forceRotation() {
85
+ if (this._isRotating) {
86
+ logger.warn('[KeyRotation] Already rotating, ignoring force');
87
+ return;
88
+ }
89
+ this.emit('rotate-needed');
90
+ }
91
+ scheduleNextRotation() {
92
+ if (this.rotationTimer) {
93
+ clearTimeout(this.rotationTimer);
94
+ }
95
+ const jitter = Math.floor(Math.random() * this.config.jitterMs);
96
+ const delay = this.config.rotationIntervalMs + jitter;
97
+ this.rotationTimer = setTimeout(() => {
98
+ if (!this._isRotating) {
99
+ this.emit('rotate-needed');
100
+ }
101
+ }, delay);
102
+ logger.debug(`[KeyRotation] Next rotation in ${Math.round(delay / 1000 / 60)} minutes`);
103
+ }
104
+ }
105
+ //# sourceMappingURL=key-rotation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-rotation.js","sourceRoot":"","sources":["../../src/network/key-rotation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import { EventEmitter } from 'node:events';
2
+ export interface NatProbeConfig {
3
+ token: string;
4
+ servers: Array<{
5
+ host: string;
6
+ port: number;
7
+ id: 'a' | 'b';
8
+ }>;
9
+ }
10
+ /**
11
+ * Handles NAT traversal probing from the agent side.
12
+ * Uses the WireGuard listen port to send UDP probes to discovery servers.
13
+ */
14
+ export declare class NatTraversal extends EventEmitter {
15
+ private listenPort;
16
+ private probeSocket;
17
+ private probeTimeout;
18
+ setListenPort(port: number): void;
19
+ /**
20
+ * Execute a NAT probe: send UDP datagrams to both discovery servers
21
+ * from the WG listen port. Probe datagram format: "TERMIFY_PROBE:<token>"
22
+ */
23
+ probe(config: NatProbeConfig): Promise<void>;
24
+ cleanup(): void;
25
+ shutdown(): void;
26
+ }
27
+ //# sourceMappingURL=nat-traversal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nat-traversal.d.ts","sourceRoot":"","sources":["../../src/network/nat-traversal.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAG3C,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,GAAG,GAAG,GAAG,CAAA;KAAE,CAAC,CAAC;CAC/D;AAED;;;GAGG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,YAAY,CAA+B;IAEnD,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIjC;;;OAGG;IACG,KAAK,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAyClD,OAAO,IAAI,IAAI;IAWf,QAAQ,IAAI,IAAI;CAGjB"}
@@ -0,0 +1,76 @@
1
+ import { createSocket } from 'node:dgram';
2
+ import { EventEmitter } from 'node:events';
3
+ import { logger } from '../utils/logger.js';
4
+ /**
5
+ * Handles NAT traversal probing from the agent side.
6
+ * Uses the WireGuard listen port to send UDP probes to discovery servers.
7
+ */
8
+ export class NatTraversal extends EventEmitter {
9
+ listenPort = null;
10
+ probeSocket = null;
11
+ probeTimeout = null;
12
+ setListenPort(port) {
13
+ this.listenPort = port;
14
+ }
15
+ /**
16
+ * Execute a NAT probe: send UDP datagrams to both discovery servers
17
+ * from the WG listen port. Probe datagram format: "TERMIFY_PROBE:<token>"
18
+ */
19
+ async probe(config) {
20
+ if (!this.listenPort) {
21
+ logger.warn('[NAT] Cannot probe: listen port not set');
22
+ return;
23
+ }
24
+ // Create UDP socket, try to bind to WG listen port with SO_REUSEADDR
25
+ try {
26
+ this.probeSocket = createSocket({ type: 'udp4', reuseAddr: true });
27
+ await new Promise((resolve, reject) => {
28
+ this.probeSocket.bind(this.listenPort, () => resolve());
29
+ this.probeSocket.once('error', reject);
30
+ });
31
+ }
32
+ catch {
33
+ // Fallback: ephemeral port (less accurate but still useful)
34
+ logger.warn('[NAT] Could not bind to WG port, using ephemeral port');
35
+ this.probeSocket = createSocket({ type: 'udp4' });
36
+ }
37
+ const message = Buffer.from(`TERMIFY_PROBE:${config.token}`);
38
+ for (const server of config.servers) {
39
+ try {
40
+ await new Promise((resolve, reject) => {
41
+ this.probeSocket.send(message, 0, message.length, server.port, server.host, (err) => {
42
+ if (err)
43
+ reject(err);
44
+ else
45
+ resolve();
46
+ });
47
+ });
48
+ logger.debug(`[NAT] Probe sent to ${server.id} (${server.host}:${server.port})`);
49
+ }
50
+ catch (err) {
51
+ logger.error(`[NAT] Failed to send probe to ${server.id}:`, err);
52
+ }
53
+ }
54
+ // Close socket after packets are sent
55
+ this.probeTimeout = setTimeout(() => {
56
+ this.cleanup();
57
+ }, 2000);
58
+ }
59
+ cleanup() {
60
+ if (this.probeTimeout) {
61
+ clearTimeout(this.probeTimeout);
62
+ this.probeTimeout = null;
63
+ }
64
+ if (this.probeSocket) {
65
+ try {
66
+ this.probeSocket.close();
67
+ }
68
+ catch { /* already closed */ }
69
+ this.probeSocket = null;
70
+ }
71
+ }
72
+ shutdown() {
73
+ this.cleanup();
74
+ }
75
+ }
76
+ //# sourceMappingURL=nat-traversal.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"nat-traversal.js","sourceRoot":"","sources":["../../src/network/nat-traversal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAe,MAAM,YAAY,CAAC;AACvD,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AAO5C;;;GAGG;AACH,MAAM,OAAO,YAAa,SAAQ,YAAY;IACpC,UAAU,GAAkB,IAAI,CAAC;IACjC,WAAW,GAAkB,IAAI,CAAC;IAClC,YAAY,GAA0B,IAAI,CAAC;IAEnD,aAAa,CAAC,IAAY;QACxB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,KAAK,CAAC,MAAsB;QAChC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,CAAC;YACrB,MAAM,CAAC,IAAI,CAAC,yCAAyC,CAAC,CAAC;YACvD,OAAO;QACT,CAAC;QAED,qEAAqE;QACrE,IAAI,CAAC;YACH,IAAI,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;YACnE,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;gBAC1C,IAAI,CAAC,WAAY,CAAC,IAAI,CAAC,IAAI,CAAC,UAAW,EAAE,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC;gBAC1D,IAAI,CAAC,WAAY,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;YAC1C,CAAC,CAAC,CAAC;QACL,CAAC;QAAC,MAAM,CAAC;YACP,4DAA4D;YAC5D,MAAM,CAAC,IAAI,CAAC,uDAAuD,CAAC,CAAC;YACrE,IAAI,CAAC,WAAW,GAAG,YAAY,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QACpD,CAAC;QAED,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,iBAAiB,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC;QAE7D,KAAK,MAAM,MAAM,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;YACpC,IAAI,CAAC;gBACH,MAAM,IAAI,OAAO,CAAO,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;oBAC1C,IAAI,CAAC,WAAY,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,CAAC,GAAG,EAAE,EAAE;wBACnF,IAAI,GAAG;4BAAE,MAAM,CAAC,GAAG,CAAC,CAAC;;4BAChB,OAAO,EAAE,CAAC;oBACjB,CAAC,CAAC,CAAC;gBACL,CAAC,CAAC,CAAC;gBACH,MAAM,CAAC,KAAK,CAAC,uBAAuB,MAAM,CAAC,EAAE,KAAK,MAAM,CAAC,IAAI,IAAI,MAAM,CAAC,IAAI,GAAG,CAAC,CAAC;YACnF,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACb,MAAM,CAAC,KAAK,CAAC,iCAAiC,MAAM,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;YACnE,CAAC;QACH,CAAC;QAED,sCAAsC;QACtC,IAAI,CAAC,YAAY,GAAG,UAAU,CAAC,GAAG,EAAE;YAClC,IAAI,CAAC,OAAO,EAAE,CAAC;QACjB,CAAC,EAAE,IAAI,CAAC,CAAC;IACX,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,YAAY,EAAE,CAAC;YACtB,YAAY,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAChC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC;QAC3B,CAAC;QACD,IAAI,IAAI,CAAC,WAAW,EAAE,CAAC;YACrB,IAAI,CAAC;gBAAC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,oBAAoB,CAAC,CAAC;YAChE,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC;QAC1B,CAAC;IACH,CAAC;IAED,QAAQ;QACN,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;CACF"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * WireGuard UAPI (Userspace API) client.
3
+ *
4
+ * Communicates with wireguard-go via its Unix domain socket at
5
+ * /var/run/wireguard/<iface>.sock using the UAPI text protocol.
6
+ *
7
+ * This eliminates the need for the `wg` CLI tool when using wireguard-go,
8
+ * enabling a fully self-contained agent (same model as Tailscale).
9
+ *
10
+ * Protocol spec: https://www.wireguard.com/xplatform/#configuration-protocol
11
+ */
12
+ import type { WgHealthSnapshot } from './wireguard-backend.js';
13
+ /**
14
+ * Convert a WireGuard base64 key to hex (UAPI requires hex-encoded keys).
15
+ */
16
+ export declare function wgKeyToHex(base64Key: string): string;
17
+ /**
18
+ * Convert a hex-encoded key back to WireGuard base64 format.
19
+ */
20
+ export declare function hexToWgKey(hexKey: string): string;
21
+ /**
22
+ * Configure the WireGuard interface (private key + listen port).
23
+ * Equivalent to: wg set <iface> private-key <path> listen-port <port>
24
+ */
25
+ export declare function uapiSetInterface(iface: string, privateKeyBase64: string, listenPort: number): Promise<void>;
26
+ /**
27
+ * Add or update a peer.
28
+ * Equivalent to: wg set <iface> peer <pubkey> allowed-ips <ips> endpoint <ep> persistent-keepalive <ka>
29
+ */
30
+ export declare function uapiAddPeer(iface: string, publicKeyBase64: string, allowedIps: string, persistentKeepalive: number, endpoint?: string): Promise<void>;
31
+ /**
32
+ * Remove a peer by its public key.
33
+ * Equivalent to: wg set <iface> peer <pubkey> remove
34
+ */
35
+ export declare function uapiRemovePeer(iface: string, publicKeyBase64: string): Promise<void>;
36
+ /**
37
+ * Update only the endpoint of an existing peer.
38
+ * Equivalent to: wg set <iface> peer <pubkey> endpoint <ep>
39
+ */
40
+ export declare function uapiUpdatePeerEndpoint(iface: string, publicKeyBase64: string, endpoint: string): Promise<void>;
41
+ /**
42
+ * Get the full interface status (self + all peers).
43
+ * Equivalent to: wg show <iface> dump
44
+ *
45
+ * Note: UAPI GET response contains private_key and listen_port for the
46
+ * interface section, then each peer starts with a public_key line.
47
+ * The interface's own public key is derived from the private key.
48
+ */
49
+ export declare function uapiGetStatus(iface: string): Promise<WgHealthSnapshot>;
50
+ //# sourceMappingURL=uapi-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uapi-client.d.ts","sourceRoot":"","sources":["../../src/network/uapi-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,gBAAgB,EAAgB,MAAM,wBAAwB,CAAC;AAM7E;;GAEG;AACH,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;GAEG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAEjD;AA+DD;;;GAGG;AACH,wBAAsB,gBAAgB,CACpC,KAAK,EAAE,MAAM,EACb,gBAAgB,EAAE,MAAM,EACxB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;;GAGG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,EACvB,UAAU,EAAE,MAAM,EAClB,mBAAmB,EAAE,MAAM,EAC3B,QAAQ,CAAC,EAAE,MAAM,GAChB,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED;;;GAGG;AACH,wBAAsB,cAAc,CAClC,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,GACtB,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;;GAGG;AACH,wBAAsB,sBAAsB,CAC1C,KAAK,EAAE,MAAM,EACb,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;;;;;;GAOG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA+H5E"}
@@ -0,0 +1,260 @@
1
+ /**
2
+ * WireGuard UAPI (Userspace API) client.
3
+ *
4
+ * Communicates with wireguard-go via its Unix domain socket at
5
+ * /var/run/wireguard/<iface>.sock using the UAPI text protocol.
6
+ *
7
+ * This eliminates the need for the `wg` CLI tool when using wireguard-go,
8
+ * enabling a fully self-contained agent (same model as Tailscale).
9
+ *
10
+ * Protocol spec: https://www.wireguard.com/xplatform/#configuration-protocol
11
+ */
12
+ import { createConnection } from 'node:net';
13
+ import { logger } from '../utils/logger.js';
14
+ // ---------------------------------------------------------------------------
15
+ // Key encoding helpers
16
+ // ---------------------------------------------------------------------------
17
+ /**
18
+ * Convert a WireGuard base64 key to hex (UAPI requires hex-encoded keys).
19
+ */
20
+ export function wgKeyToHex(base64Key) {
21
+ return Buffer.from(base64Key, 'base64').toString('hex');
22
+ }
23
+ /**
24
+ * Convert a hex-encoded key back to WireGuard base64 format.
25
+ */
26
+ export function hexToWgKey(hexKey) {
27
+ return Buffer.from(hexKey, 'hex').toString('base64');
28
+ }
29
+ // ---------------------------------------------------------------------------
30
+ // Socket communication
31
+ // ---------------------------------------------------------------------------
32
+ const SOCKET_DIR = '/var/run/wireguard';
33
+ function socketPath(iface) {
34
+ return `${SOCKET_DIR}/${iface}.sock`;
35
+ }
36
+ /**
37
+ * Send a UAPI request and read the full response.
38
+ * Returns the raw response string.
39
+ */
40
+ async function uapiRequest(iface, request) {
41
+ return new Promise((resolve, reject) => {
42
+ const sock = createConnection(socketPath(iface));
43
+ let response = '';
44
+ sock.on('connect', () => {
45
+ sock.write(request);
46
+ });
47
+ sock.on('data', (data) => {
48
+ response += data.toString();
49
+ });
50
+ sock.on('end', () => {
51
+ resolve(response);
52
+ });
53
+ sock.on('error', (err) => {
54
+ reject(new Error(`UAPI socket error (${iface}): ${err.message}`));
55
+ });
56
+ // Safety timeout
57
+ sock.setTimeout(5000, () => {
58
+ sock.destroy();
59
+ reject(new Error(`UAPI socket timeout (${iface})`));
60
+ });
61
+ });
62
+ }
63
+ /**
64
+ * Parse errno from UAPI response. Returns 0 on success, throws on error.
65
+ */
66
+ function checkErrno(response, operation) {
67
+ const match = /^errno=(\d+)$/m.exec(response);
68
+ if (!match) {
69
+ throw new Error(`UAPI ${operation}: no errno in response`);
70
+ }
71
+ const errno = parseInt(match[1], 10);
72
+ if (errno !== 0) {
73
+ throw new Error(`UAPI ${operation} failed: errno=${errno}`);
74
+ }
75
+ }
76
+ // ---------------------------------------------------------------------------
77
+ // Public API
78
+ // ---------------------------------------------------------------------------
79
+ /**
80
+ * Configure the WireGuard interface (private key + listen port).
81
+ * Equivalent to: wg set <iface> private-key <path> listen-port <port>
82
+ */
83
+ export async function uapiSetInterface(iface, privateKeyBase64, listenPort) {
84
+ const request = `set=1\n` +
85
+ `private_key=${wgKeyToHex(privateKeyBase64)}\n` +
86
+ `listen_port=${listenPort}\n` +
87
+ `\n`;
88
+ const response = await uapiRequest(iface, request);
89
+ checkErrno(response, 'setInterface');
90
+ logger.debug('[UAPI] Interface configured: port=' + listenPort);
91
+ }
92
+ /**
93
+ * Add or update a peer.
94
+ * Equivalent to: wg set <iface> peer <pubkey> allowed-ips <ips> endpoint <ep> persistent-keepalive <ka>
95
+ */
96
+ export async function uapiAddPeer(iface, publicKeyBase64, allowedIps, persistentKeepalive, endpoint) {
97
+ let request = `set=1\n` +
98
+ `public_key=${wgKeyToHex(publicKeyBase64)}\n`;
99
+ if (endpoint) {
100
+ request += `endpoint=${endpoint}\n`;
101
+ }
102
+ // allowed_ips: each CIDR on its own line
103
+ const ips = allowedIps.split(',').map((s) => s.trim());
104
+ for (const ip of ips) {
105
+ request += `allowed_ip=${ip}\n`;
106
+ }
107
+ request += `persistent_keepalive_interval=${persistentKeepalive}\n`;
108
+ request += `\n`;
109
+ const response = await uapiRequest(iface, request);
110
+ checkErrno(response, 'addPeer');
111
+ logger.debug('[UAPI] Peer added: ' + publicKeyBase64.slice(0, 8) + '...');
112
+ }
113
+ /**
114
+ * Remove a peer by its public key.
115
+ * Equivalent to: wg set <iface> peer <pubkey> remove
116
+ */
117
+ export async function uapiRemovePeer(iface, publicKeyBase64) {
118
+ const request = `set=1\n` +
119
+ `public_key=${wgKeyToHex(publicKeyBase64)}\n` +
120
+ `remove=true\n` +
121
+ `\n`;
122
+ const response = await uapiRequest(iface, request);
123
+ checkErrno(response, 'removePeer');
124
+ logger.debug('[UAPI] Peer removed: ' + publicKeyBase64.slice(0, 8) + '...');
125
+ }
126
+ /**
127
+ * Update only the endpoint of an existing peer.
128
+ * Equivalent to: wg set <iface> peer <pubkey> endpoint <ep>
129
+ */
130
+ export async function uapiUpdatePeerEndpoint(iface, publicKeyBase64, endpoint) {
131
+ const request = `set=1\n` +
132
+ `public_key=${wgKeyToHex(publicKeyBase64)}\n` +
133
+ `endpoint=${endpoint}\n` +
134
+ `\n`;
135
+ const response = await uapiRequest(iface, request);
136
+ checkErrno(response, 'updatePeerEndpoint');
137
+ logger.debug('[UAPI] Endpoint updated for peer ' + publicKeyBase64.slice(0, 8) + '...');
138
+ }
139
+ /**
140
+ * Get the full interface status (self + all peers).
141
+ * Equivalent to: wg show <iface> dump
142
+ *
143
+ * Note: UAPI GET response contains private_key and listen_port for the
144
+ * interface section, then each peer starts with a public_key line.
145
+ * The interface's own public key is derived from the private key.
146
+ */
147
+ export async function uapiGetStatus(iface) {
148
+ const request = `get=1\n\n`;
149
+ let response;
150
+ try {
151
+ response = await uapiRequest(iface, request);
152
+ }
153
+ catch {
154
+ return {
155
+ interfaceUp: false,
156
+ publicKey: '',
157
+ listenPort: 0,
158
+ peers: [],
159
+ };
160
+ }
161
+ checkErrno(response, 'getStatus');
162
+ const lines = response.trim().split('\n');
163
+ let privateKeyHex = '';
164
+ let listenPort = 0;
165
+ const peers = [];
166
+ let currentPeer = null;
167
+ for (const line of lines) {
168
+ if (line.startsWith('errno='))
169
+ continue;
170
+ const eqIdx = line.indexOf('=');
171
+ if (eqIdx === -1)
172
+ continue;
173
+ const key = line.substring(0, eqIdx);
174
+ const value = line.substring(eqIdx + 1);
175
+ switch (key) {
176
+ case 'private_key':
177
+ privateKeyHex = value;
178
+ break;
179
+ case 'listen_port':
180
+ if (!currentPeer) {
181
+ listenPort = parseInt(value, 10);
182
+ }
183
+ break;
184
+ case 'public_key':
185
+ // Every public_key line in UAPI GET marks the start of a new peer
186
+ if (currentPeer) {
187
+ peers.push(currentPeer);
188
+ }
189
+ currentPeer = {
190
+ publicKey: hexToWgKey(value),
191
+ allowedIps: '',
192
+ transferRx: 0,
193
+ transferTx: 0,
194
+ };
195
+ break;
196
+ case 'endpoint':
197
+ if (currentPeer && value && value !== '(none)') {
198
+ currentPeer.endpoint = value;
199
+ }
200
+ break;
201
+ case 'allowed_ip':
202
+ if (currentPeer) {
203
+ currentPeer.allowedIps = currentPeer.allowedIps
204
+ ? currentPeer.allowedIps + ',' + value
205
+ : value;
206
+ }
207
+ break;
208
+ case 'last_handshake_time_sec': {
209
+ if (currentPeer) {
210
+ const epoch = parseInt(value, 10);
211
+ if (epoch > 0) {
212
+ currentPeer.latestHandshake = new Date(epoch * 1000);
213
+ }
214
+ }
215
+ break;
216
+ }
217
+ case 'rx_bytes':
218
+ if (currentPeer) {
219
+ currentPeer.transferRx = parseInt(value, 10);
220
+ }
221
+ break;
222
+ case 'tx_bytes':
223
+ if (currentPeer) {
224
+ currentPeer.transferTx = parseInt(value, 10);
225
+ }
226
+ break;
227
+ default:
228
+ break;
229
+ }
230
+ }
231
+ // Push last peer
232
+ if (currentPeer) {
233
+ peers.push(currentPeer);
234
+ }
235
+ // Derive public key from private key using X25519
236
+ let publicKey = '';
237
+ if (privateKeyHex) {
238
+ try {
239
+ const { createPublicKey, createPrivateKey } = await import('node:crypto');
240
+ // Build PKCS#8 DER: 16-byte header + 32-byte raw private key
241
+ const privRaw = Buffer.from(privateKeyHex, 'hex');
242
+ const pkcs8Header = Buffer.from('302e020100300506032b656e04220420', 'hex');
243
+ const pkcs8Der = Buffer.concat([pkcs8Header, privRaw]);
244
+ const privKeyObj = createPrivateKey({ key: pkcs8Der, format: 'der', type: 'pkcs8' });
245
+ const pubKeyObj = createPublicKey(privKeyObj);
246
+ const spkiDer = pubKeyObj.export({ type: 'spki', format: 'der' });
247
+ publicKey = spkiDer.subarray(12).toString('base64');
248
+ }
249
+ catch {
250
+ // If derivation fails, leave empty — caller can fill from state store
251
+ }
252
+ }
253
+ return {
254
+ interfaceUp: true,
255
+ publicKey,
256
+ listenPort,
257
+ peers,
258
+ };
259
+ }
260
+ //# sourceMappingURL=uapi-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uapi-client.js","sourceRoot":"","sources":["../../src/network/uapi-client.ts"],"names":[],"mappings":"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"}