wireguard-utils
Version:
A comprehensive TypeScript library for WireGuard utilities including key generation, IP management, config building, QR code generation, config parsing, validation, templates, routing, tunnel management, and security features using proper Curve25519 crypt
919 lines (918 loc) • 31.1 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.generatePrivateKey = generatePrivateKey;
exports.derivePublicKey = derivePublicKey;
exports.validatePublicKey = validatePublicKey;
exports.generateIPInClass = generateIPInClass;
exports.generatePreSharedKey = generatePreSharedKey;
exports.buildWireGuardConfig = buildWireGuardConfig;
exports.generateWireguardKeyPair = generateWireguardKeyPair;
exports.generateQRCode = generateQRCode;
exports.getDNSConfigs = getDNSConfigs;
exports.getDNSByType = getDNSByType;
exports.parseWireGuardConfig = parseWireGuardConfig;
exports.validateWireGuardConfig = validateWireGuardConfig;
exports.checkPortAvailability = checkPortAvailability;
exports.findAvailablePort = findAvailablePort;
exports.getRecommendedPorts = getRecommendedPorts;
exports.rotateKeys = rotateKeys;
exports.generateKeyRotationPlan = generateKeyRotationPlan;
exports.generateFromTemplate = generateFromTemplate;
exports.getAvailableTemplates = getAvailableTemplates;
exports.generateAdvancedRouting = generateAdvancedRouting;
exports.generateSplitTunnelConfig = generateSplitTunnelConfig;
exports.startWireGuardTunnel = startWireGuardTunnel;
exports.stopWireGuardTunnel = stopWireGuardTunnel;
exports.getTunnelStatus = getTunnelStatus;
exports.listActiveTunnels = listActiveTunnels;
const crypto_1 = require("crypto");
const ed25519_1 = require("@noble/curves/ed25519");
const QRCode = __importStar(require("qrcode"));
/**
* Generate a WireGuard private key
* @returns Base64 encoded private key (32 bytes)
*/
function generatePrivateKey() {
// Generate 32 random bytes for the private key
const privateKeyBytes = (0, crypto_1.randomBytes)(32);
// Encode as base64 string
return privateKeyBytes.toString('base64');
}
/**
* Derive a WireGuard public key from a private key
* Uses Curve25519 elliptic curve cryptography
* @param privateKeyStr Base64 encoded private key
* @returns Base64 encoded public key
*/
function derivePublicKey(privateKeyStr) {
// 1. Decode the base64 private key
const privateKeyBytes = Buffer.from(privateKeyStr, 'base64');
// 2. Validate length (must be exactly 32 bytes)
if (privateKeyBytes.length !== 32) {
throw new Error('Private key must be 32 bytes long');
}
// 3. Derive public key using Curve25519
// This is equivalent to: curve25519.X25519(privateKey, curve25519.Basepoint)
const publicKeyBytes = ed25519_1.x25519.getPublicKey(privateKeyBytes);
// 4. Encode as base64 string
return Buffer.from(publicKeyBytes).toString('base64');
}
/**
* Validate a WireGuard public key
* @param publicKey Base64 encoded public key to validate
* @returns true if valid, false otherwise
*/
function validatePublicKey(publicKey) {
// WireGuard public keys must be exactly 44 characters (32 bytes base64 encoded)
if (publicKey.length !== 44) {
return false;
}
// Must be valid base64
try {
const decoded = Buffer.from(publicKey, 'base64');
return decoded.length === 32;
}
catch {
return false;
}
}
/**
* Parse CIDR notation to extract network and subnet mask
* @param cidr CIDR notation string (e.g., "192.168.1.0/24")
* @returns Object with network IP and mask bits
*/
function parseCIDR(cidr) {
const [network, mask] = cidr.split('/');
const maskBits = parseInt(mask, 10);
if (!network || isNaN(maskBits) || maskBits < 0 || maskBits > 32) {
throw new Error('Invalid CIDR notation');
}
return { network, maskBits };
}
/**
* Convert IP address string to 32-bit integer
* @param ip IP address string (e.g., "192.168.1.1")
* @returns 32-bit integer representation
*/
function ipToInt(ip) {
return ip.split('.').reduce((acc, octet) => (acc << 8) + parseInt(octet, 10), 0) >>> 0;
}
/**
* Convert 32-bit integer to IP address string
* @param int 32-bit integer
* @returns IP address string
*/
function intToIp(int) {
return [(int >>> 24) & 255, (int >>> 16) & 255, (int >>> 8) & 255, int & 255].join('.');
}
/**
* Get network class IP ranges
* @param networkClass Network class ('A', 'B', or 'C')
* @returns Object with start and end IP integers
*/
function getClassRange(networkClass) {
switch (networkClass) {
case 'A':
// Class A: 10.0.0.0 - 10.255.255.255
return {
start: ipToInt('10.0.0.1'),
end: ipToInt('10.255.255.254')
};
case 'B':
// Class B: 172.16.0.0 - 172.31.255.255
return {
start: ipToInt('172.16.0.1'),
end: ipToInt('172.31.255.254')
};
case 'C':
// Class C: 192.168.0.0 - 192.168.255.255
return {
start: ipToInt('192.168.0.1'),
end: ipToInt('192.168.255.254')
};
default:
throw new Error('Invalid network class. Use A, B, or C');
}
}
/**
* Get subnet range from CIDR notation
* @param cidr CIDR notation string
* @returns Object with start and end IP integers
*/
function getSubnetRange(cidr) {
const { network, maskBits } = parseCIDR(cidr);
const networkInt = ipToInt(network);
const hostBits = 32 - maskBits;
const subnetMask = (0xFFFFFFFF << hostBits) >>> 0;
const networkAddress = (networkInt & subnetMask) >>> 0;
const broadcastAddress = (networkAddress | ((1 << hostBits) - 1)) >>> 0;
// Return range excluding network and broadcast addresses
return {
start: networkAddress + 1,
end: broadcastAddress - 1
};
}
/**
* Generate a random integer within a range
* @param min Minimum value (inclusive)
* @param max Maximum value (inclusive)
* @returns Random integer
*/
function randomIntInRange(min, max) {
const range = max - min + 1;
const bytes = (0, crypto_1.randomBytes)(4);
const randomValue = bytes.readUInt32BE(0);
return min + (randomValue % range);
}
/**
* Determine network class from IP address
* @param ip IP address string
* @returns Network class ('A', 'B', or 'C')
*/
function getNetworkClassFromIP(ip) {
const firstOctet = parseInt(ip.split('.')[0], 10);
// Class A: 10.0.0.0/8
if (firstOctet === 10) {
return 'A';
}
// Class B: 172.16.0.0/12 (172.16.x.x - 172.31.x.x)
if (firstOctet === 172) {
const secondOctet = parseInt(ip.split('.')[1], 10);
if (secondOctet >= 16 && secondOctet <= 31) {
return 'B';
}
}
// Class C: 192.168.0.0/16
if (firstOctet === 192 && parseInt(ip.split('.')[1], 10) === 168) {
return 'C';
}
throw new Error(`IP address ${ip} is not in a private network class (A: 10.x.x.x, B: 172.16-31.x.x, C: 192.168.x.x)`);
}
/**
* Generate a random IP address within the specified network class or subnet
* @param networkClassOrSubnet The IP class ('A', 'B', or 'C') or CIDR notation
* @param subnet Optional specific subnet in CIDR notation (e.g., '192.168.1.0/24')
* @returns Generated IP address as string
* @throws Error if subnet format is invalid or network class is unsupported
*/
function generateIPInClass(networkClassOrSubnet, subnet) {
let networkClass;
let actualSubnet;
// Determine if first parameter is a network class or subnet
if (networkClassOrSubnet.includes('/')) {
// First parameter is a CIDR subnet
actualSubnet = networkClassOrSubnet;
const { network } = parseCIDR(actualSubnet);
networkClass = getNetworkClassFromIP(network);
}
else {
// First parameter is a network class
networkClass = networkClassOrSubnet;
actualSubnet = subnet;
}
let range;
if (actualSubnet) {
// Use specific subnet range
range = getSubnetRange(actualSubnet);
// Validate that the subnet matches the detected/specified class
const { network } = parseCIDR(actualSubnet);
const detectedClass = getNetworkClassFromIP(network);
if (detectedClass !== networkClass) {
throw new Error(`Subnet ${actualSubnet} is Class ${detectedClass} but Class ${networkClass} was specified`);
}
}
else {
// Use full class range
range = getClassRange(networkClass);
}
if (range.start > range.end) {
throw new Error('Invalid IP range: start is greater than end');
}
const randomIP = randomIntInRange(range.start, range.end);
return intToIp(randomIP);
}
/**
* Generate a WireGuard pre-shared key
* Pre-shared keys provide additional quantum resistance and security
* @returns Base64 encoded pre-shared key (32 bytes)
*/
function generatePreSharedKey() {
// Generate 32 random bytes for the pre-shared key
const preSharedKeyBytes = (0, crypto_1.randomBytes)(32);
// Encode as base64 string
return preSharedKeyBytes.toString('base64');
}
/**
* Build a complete WireGuard configuration file from structured data
* @param config WireGuard configuration object
* @returns WireGuard configuration file as string
*/
function buildWireGuardConfig(config) {
let configText = '[Interface]\n';
// Add interface configuration
configText += `PrivateKey = ${config.interface.privateKey}\n`;
// Add addresses (can be multiple)
config.interface.address.forEach(addr => {
configText += `Address = ${addr}\n`;
});
// Add optional interface settings
if (config.interface.listenPort) {
configText += `ListenPort = ${config.interface.listenPort}\n`;
}
if (config.interface.dns && config.interface.dns.length > 0) {
configText += `DNS = ${config.interface.dns.join(', ')}\n`;
}
if (config.interface.preSharedKey) {
configText += `PreSharedKey = ${config.interface.preSharedKey}\n`;
}
// Add peers
config.peers.forEach(peer => {
configText += '\n[Peer]\n';
configText += `PublicKey = ${peer.publicKey}\n`;
// Add allowed IPs (can be multiple)
peer.allowedIPs.forEach(ip => {
configText += `AllowedIPs = ${ip}\n`;
});
// Add optional peer settings
if (peer.endpoint) {
configText += `Endpoint = ${peer.endpoint}\n`;
}
if (peer.preSharedKey) {
configText += `PreSharedKey = ${peer.preSharedKey}\n`;
}
if (peer.persistentKeepalive) {
configText += `PersistentKeepalive = ${peer.persistentKeepalive}\n`;
}
});
return configText;
}
/**
* Generate a complete WireGuard key pair
* Uses proper Curve25519 cryptography for WireGuard compatibility
* @returns Object containing base64 encoded private and public keys
*/
function generateWireguardKeyPair() {
const privateKey = generatePrivateKey();
const publicKey = derivePublicKey(privateKey);
return {
privateKey,
publicKey
};
}
/**
* Generate a QR code for WireGuard configuration
* Mobile WireGuard apps can scan these QR codes to automatically import configs
* @param configText WireGuard configuration as string
* @param options QR code generation options
* @returns Base64 encoded PNG image of the QR code
*/
async function generateQRCode(configText, options) {
try {
const qrOptions = {
errorCorrectionLevel: options?.errorCorrectionLevel || 'M',
type: 'image/png',
quality: 0.92,
width: options?.width || 256,
margin: options?.margin || 1,
color: {
dark: '#000000',
light: '#FFFFFF'
}
};
// Generate QR code as base64 data URL
const qrCodeDataURL = await QRCode.toDataURL(configText, qrOptions);
// Extract just the base64 part (remove "data:image/png;base64," prefix)
return qrCodeDataURL.split(',')[1];
}
catch (error) {
throw new Error(`Failed to generate QR code: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* DNS Configuration Helpers (Feature 6)
*/
/**
* Get predefined DNS configurations for different use cases
*/
function getDNSConfigs() {
return {
cloudflare: {
primary: '1.1.1.1',
secondary: '1.0.0.1',
description: 'Cloudflare DNS - Fast and privacy-focused',
type: 'privacy'
},
google: {
primary: '8.8.8.8',
secondary: '8.8.4.4',
description: 'Google DNS - Reliable and fast',
type: 'performance'
},
quad9: {
primary: '9.9.9.9',
secondary: '149.112.112.112',
description: 'Quad9 - Security and privacy focused with malware blocking',
type: 'security'
},
opendns: {
primary: '208.67.222.222',
secondary: '208.67.220.220',
description: 'OpenDNS - Family-safe filtering',
type: 'family'
},
adguard: {
primary: '94.140.14.14',
secondary: '94.140.15.15',
description: 'AdGuard DNS - Blocks ads and trackers',
type: 'privacy'
}
};
}
/**
* Get DNS servers by type
*/
function getDNSByType(type) {
const configs = getDNSConfigs();
return Object.values(configs).filter(config => config.type === type);
}
/**
* Config File Parser (Feature 7)
*/
/**
* Parse a WireGuard configuration file
*/
function parseWireGuardConfig(configText) {
const lines = configText.split('\n').map(line => line.trim());
const result = {
interface: {},
peers: [],
raw: configText,
valid: true
};
let currentSection = null;
let currentPeer = {};
for (const line of lines) {
// Skip empty lines and comments
if (!line || line.startsWith('#'))
continue;
// Section headers
if (line === '[Interface]') {
currentSection = 'interface';
continue;
}
if (line === '[Peer]') {
if (Object.keys(currentPeer).length > 0) {
result.peers.push(currentPeer);
}
currentPeer = {};
currentSection = 'peer';
continue;
}
// Parse key-value pairs
const match = line.match(/^(\w+)\s*=\s*(.+)$/);
if (!match)
continue;
const [, key, value] = match;
const cleanValue = value.trim();
if (currentSection === 'interface') {
parseInterfaceField(result.interface, key, cleanValue);
}
else if (currentSection === 'peer') {
parsePeerField(currentPeer, key, cleanValue);
}
}
// Add final peer if exists
if (Object.keys(currentPeer).length > 0) {
result.peers.push(currentPeer);
}
return result;
}
function parseInterfaceField(iface, key, value) {
switch (key.toLowerCase()) {
case 'privatekey':
iface.privateKey = value;
break;
case 'address':
iface.address = iface.address || [];
iface.address.push(value);
break;
case 'listenport':
iface.listenPort = parseInt(value);
break;
case 'dns':
iface.dns = value.split(',').map(s => s.trim());
break;
case 'presharedkey':
iface.preSharedKey = value;
break;
case 'postup':
iface.postUp = iface.postUp || [];
iface.postUp.push(value);
break;
case 'postdown':
iface.postDown = iface.postDown || [];
iface.postDown.push(value);
break;
case 'table':
iface.table = value;
break;
case 'mtu':
iface.mtu = parseInt(value);
break;
}
}
function parsePeerField(peer, key, value) {
switch (key.toLowerCase()) {
case 'publickey':
peer.publicKey = value;
break;
case 'allowedips':
peer.allowedIPs = value.split(',').map(s => s.trim());
break;
case 'endpoint':
peer.endpoint = value;
break;
case 'presharedkey':
peer.preSharedKey = value;
break;
case 'persistentkeepalive':
peer.persistentKeepalive = parseInt(value);
break;
}
}
/**
* Config Validation (Feature 8)
*/
/**
* Validate a WireGuard configuration
*/
function validateWireGuardConfig(config) {
const result = {
valid: true,
errors: [],
warnings: [],
suggestions: []
};
let parsedConfig;
if (typeof config === 'string') {
const parsed = parseWireGuardConfig(config);
parsedConfig = {
interface: {
privateKey: parsed.interface.privateKey || '',
address: parsed.interface.address || []
},
peers: parsed.peers.map(p => ({
publicKey: p.publicKey || '',
allowedIPs: p.allowedIPs || []
}))
};
}
else {
parsedConfig = config;
}
// Validate interface
if (!parsedConfig.interface.privateKey) {
result.errors.push('Interface private key is required');
result.valid = false;
}
else if (!validatePrivateKey(parsedConfig.interface.privateKey)) {
result.errors.push('Invalid private key format');
result.valid = false;
}
if (!parsedConfig.interface.address || parsedConfig.interface.address.length === 0) {
result.errors.push('Interface address is required');
result.valid = false;
}
// Validate peers
if (parsedConfig.peers.length === 0) {
result.warnings.push('No peers configured - this interface will not connect to anything');
}
for (let i = 0; i < parsedConfig.peers.length; i++) {
const peer = parsedConfig.peers[i];
if (!peer.publicKey) {
result.errors.push(`Peer ${i + 1}: Public key is required`);
result.valid = false;
}
else if (!validatePublicKey(peer.publicKey)) {
result.errors.push(`Peer ${i + 1}: Invalid public key format`);
result.valid = false;
}
if (!peer.allowedIPs || peer.allowedIPs.length === 0) {
result.errors.push(`Peer ${i + 1}: AllowedIPs is required`);
result.valid = false;
}
// Security suggestions
if (!peer.preSharedKey) {
result.suggestions.push(`Peer ${i + 1}: Consider adding a pre-shared key for quantum resistance`);
}
if (peer.allowedIPs?.includes('0.0.0.0/0')) {
result.warnings.push(`Peer ${i + 1}: Routes all traffic through VPN (full tunnel)`);
}
}
return result;
}
function validatePrivateKey(key) {
try {
const decoded = Buffer.from(key, 'base64');
return decoded.length === 32;
}
catch {
return false;
}
}
/**
* Port Management (Feature 9)
*/
/**
* Check if a port is available for WireGuard
*/
async function checkPortAvailability(port) {
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
try {
// Check if port is in use (Linux/macOS)
const result = execSync(`netstat -tuln | grep :${port}`, { encoding: 'utf8', stdio: 'pipe' });
return {
port,
available: false,
inUse: result.includes('LISTEN') ? 'Unknown service' : 'In use'
};
}
catch {
// If netstat fails or finds nothing, port is likely available
return {
port,
available: true
};
}
}
/**
* Find an available port in the WireGuard range
*/
async function findAvailablePort(startPort = 51820, endPort = 51900) {
for (let port = startPort; port <= endPort; port++) {
const status = await checkPortAvailability(port);
if (status.available) {
return port;
}
}
throw new Error(`No available ports found in range ${startPort}-${endPort}`);
}
/**
* Get recommended WireGuard ports
*/
function getRecommendedPorts() {
return [51820, 51821, 51822, 443, 53, 80]; // Common WireGuard and firewall-friendly ports
}
/**
* Key Rotation Utilities (Feature 10)
*/
/**
* Rotate keys for a WireGuard configuration
*/
function rotateKeys(config, rotatePSK = true) {
const oldKeys = {
privateKey: config.interface.privateKey,
publicKey: derivePublicKey(config.interface.privateKey),
psk: config.interface.preSharedKey
};
const newKeyPair = generateWireguardKeyPair();
const newPSK = rotatePSK ? generatePreSharedKey() : undefined;
const newConfig = {
...config,
interface: {
...config.interface,
privateKey: newKeyPair.privateKey,
preSharedKey: newPSK || config.interface.preSharedKey
},
peers: config.peers.map(peer => ({
...peer,
preSharedKey: newPSK || peer.preSharedKey
}))
};
return {
newConfig,
oldKeys,
newKeys: {
privateKey: newKeyPair.privateKey,
publicKey: newKeyPair.publicKey,
psk: newPSK
}
};
}
/**
* Generate key rotation plan
*/
function generateKeyRotationPlan(configs) {
return {
rotationOrder: configs.map((_, index) => index),
timeline: 'Rotate keys in sequence with 5-minute intervals',
instructions: [
'1. Generate new keys for each peer',
'2. Update server configuration first',
'3. Restart server with new configuration',
'4. Update each client configuration',
'5. Verify all connections are working',
'6. Archive old keys securely'
]
};
}
/**
* Config Templates (Feature 11)
*/
/**
* Generate configuration from template
*/
function generateFromTemplate(template, options = {}) {
const serverKeys = generateWireguardKeyPair();
const clientKeys = generateWireguardKeyPair();
const psk = generatePreSharedKey();
const defaultServerIP = options.serverIP || generateIPInClass('10.0.0.0/24');
const defaultClientIP = options.clientIP || generateIPInClass('10.0.0.0/24');
const defaultDNS = options.dns || ['1.1.1.1', '1.0.0.1'];
const defaultPort = options.port || 51820;
switch (template) {
case 'roadwarrior-server':
return {
interface: {
privateKey: serverKeys.privateKey,
address: [`${defaultServerIP}/24`],
listenPort: defaultPort,
dns: defaultDNS
},
peers: [{
publicKey: clientKeys.publicKey,
allowedIPs: [`${defaultClientIP}/32`],
preSharedKey: psk
}]
};
case 'roadwarrior-client':
return {
interface: {
privateKey: clientKeys.privateKey,
address: [`${defaultClientIP}/24`],
dns: defaultDNS
},
peers: [{
publicKey: serverKeys.publicKey,
allowedIPs: ['0.0.0.0/0'],
endpoint: options.serverEndpoint || 'vpn.example.com:51820',
preSharedKey: psk,
persistentKeepalive: 25
}]
};
case 'site-to-site':
return {
interface: {
privateKey: serverKeys.privateKey,
address: [`${defaultServerIP}/30`]
},
peers: [{
publicKey: clientKeys.publicKey,
allowedIPs: ['192.168.1.0/24'],
endpoint: options.serverEndpoint || 'site2.example.com:51820',
preSharedKey: psk
}]
};
case 'mesh-node':
return {
interface: {
privateKey: serverKeys.privateKey,
address: [`${defaultServerIP}/24`],
listenPort: defaultPort
},
peers: [{
publicKey: clientKeys.publicKey,
allowedIPs: [`${defaultClientIP}/32`],
preSharedKey: psk
}]
};
case 'gateway':
return {
interface: {
privateKey: serverKeys.privateKey,
address: [`${defaultServerIP}/24`],
listenPort: defaultPort
},
peers: [{
publicKey: clientKeys.publicKey,
allowedIPs: ['192.168.0.0/16', '10.0.0.0/8'],
preSharedKey: psk
}]
};
case 'split-tunnel':
return {
interface: {
privateKey: clientKeys.privateKey,
address: [`${defaultClientIP}/24`],
dns: defaultDNS
},
peers: [{
publicKey: serverKeys.publicKey,
allowedIPs: ['10.0.0.0/8', '192.168.0.0/16'], // Only private networks
endpoint: options.serverEndpoint || 'vpn.example.com:51820',
preSharedKey: psk,
persistentKeepalive: 25
}]
};
default:
throw new Error(`Unknown template: ${template}`);
}
}
/**
* Get available templates with descriptions
*/
function getAvailableTemplates() {
return {
'roadwarrior-server': 'Server configuration for road warrior VPN setup',
'roadwarrior-client': 'Client configuration for road warrior VPN setup',
'site-to-site': 'Site-to-site VPN connecting two networks',
'mesh-node': 'Mesh network node configuration',
'gateway': 'Gateway configuration for network access',
'split-tunnel': 'Split tunneling - only route private networks'
};
}
/**
* Advanced Routing Configuration (Feature 22)
*/
/**
* Generate advanced routing configuration
*/
function generateAdvancedRouting(options) {
const routing = {
allowedIPs: [],
defaultRoute: options.defaultRoute || false,
splitTunnel: options.splitTunnel || false
};
if (options.defaultRoute) {
routing.allowedIPs = ['0.0.0.0/0'];
}
else {
routing.allowedIPs = options.networks;
}
if (options.customRoutes) {
routing.routes = options.customRoutes;
}
return routing;
}
/**
* Generate split tunnel configuration
*/
function generateSplitTunnelConfig(privateNetworks = ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'], customNetworks = []) {
return {
allowedIPs: [...privateNetworks, ...customNetworks],
splitTunnel: true,
defaultRoute: false
};
}
/**
* WireGuard Tunnel Management
*/
/**
* Start a WireGuard tunnel
*/
async function startWireGuardTunnel(configPath, interfaceName = 'wg0') {
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
try {
// Bring up the interface
execSync(`sudo wg-quick up ${configPath}`, { stdio: 'pipe' });
return {
name: interfaceName,
active: true,
interface: interfaceName
};
}
catch (error) {
throw new Error(`Failed to start WireGuard tunnel: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Stop a WireGuard tunnel
*/
async function stopWireGuardTunnel(configPath, interfaceName = 'wg0') {
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
try {
// Bring down the interface
execSync(`sudo wg-quick down ${configPath}`, { stdio: 'pipe' });
return {
name: interfaceName,
active: false,
interface: interfaceName
};
}
catch (error) {
throw new Error(`Failed to stop WireGuard tunnel: ${error instanceof Error ? error.message : 'Unknown error'}`);
}
}
/**
* Get WireGuard tunnel status
*/
async function getTunnelStatus(interfaceName = 'wg0') {
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
try {
const output = execSync(`sudo wg show ${interfaceName}`, { encoding: 'utf8', stdio: 'pipe' });
// Parse output to get peer count and other info
const lines = output.split('\n');
const peers = lines.filter(line => line.includes('peer:')).length;
return {
name: interfaceName,
active: true,
interface: interfaceName,
peers
};
}
catch {
return {
name: interfaceName,
active: false,
interface: interfaceName
};
}
}
/**
* List all active WireGuard tunnels
*/
async function listActiveTunnels() {
const { execSync } = await Promise.resolve().then(() => __importStar(require('child_process')));
try {
const output = execSync('sudo wg show interfaces', { encoding: 'utf8', stdio: 'pipe' });
const interfaces = output.trim().split(/\s+/).filter(Boolean);
const statuses = await Promise.all(interfaces.map(iface => getTunnelStatus(iface)));
return statuses.filter(status => status.active);
}
catch {
return [];
}
}