/** * NFTables Route Helper Functions * * This module provides utility functions for creating NFTables-based route configurations * for high-performance packet forwarding at the kernel level. */ import type { IRouteConfig, IRouteMatch, IRouteAction, TPortRange } from '../../models/route-types.js'; import { createHttpToHttpsRedirect } from './https-helpers.js'; /** * Create an NFTables-based route for high-performance packet forwarding * @param nameOrDomains Name or domain(s) to match * @param target Target host and port * @param options Additional route options * @returns Route configuration object */ export function createNfTablesRoute( nameOrDomains: string | string[], target: { host: string; port: number | 'preserve' }, options: { ports?: TPortRange; protocol?: 'tcp' | 'udp' | 'all'; preserveSourceIP?: boolean; ipAllowList?: string[]; ipBlockList?: string[]; maxRate?: string; priority?: number; useTls?: boolean; tableName?: string; useIPSets?: boolean; useAdvancedNAT?: boolean; } = {} ): IRouteConfig { // Determine if this is a name or domain let name: string; let domains: string | string[] | undefined; if (Array.isArray(nameOrDomains) || (typeof nameOrDomains === 'string' && nameOrDomains.includes('.'))) { domains = nameOrDomains; name = Array.isArray(nameOrDomains) ? nameOrDomains[0] : nameOrDomains; } else { name = nameOrDomains; domains = undefined; // No domains } // Create route match const match: IRouteMatch = { domains, ports: options.ports || 80 }; // Create route action const action: IRouteAction = { type: 'forward', targets: [{ host: target.host, port: target.port }], forwardingEngine: 'nftables', nftables: { protocol: options.protocol || 'tcp', preserveSourceIP: options.preserveSourceIP, maxRate: options.maxRate, priority: options.priority, tableName: options.tableName, useIPSets: options.useIPSets, useAdvancedNAT: options.useAdvancedNAT } }; // Add TLS options if needed if (options.useTls) { action.tls = { mode: 'passthrough' }; } // Create the route config const routeConfig: IRouteConfig = { name, match, action }; // Add security if allowed or blocked IPs are specified if (options.ipAllowList?.length || options.ipBlockList?.length) { routeConfig.security = { ipAllowList: options.ipAllowList, ipBlockList: options.ipBlockList }; } return routeConfig; } /** * Create an NFTables-based TLS termination route * @param nameOrDomains Name or domain(s) to match * @param target Target host and port * @param options Additional route options * @returns Route configuration object */ export function createNfTablesTerminateRoute( nameOrDomains: string | string[], target: { host: string; port: number | 'preserve' }, options: { ports?: TPortRange; protocol?: 'tcp' | 'udp' | 'all'; preserveSourceIP?: boolean; ipAllowList?: string[]; ipBlockList?: string[]; maxRate?: string; priority?: number; tableName?: string; useIPSets?: boolean; useAdvancedNAT?: boolean; certificate?: 'auto' | { key: string; cert: string }; } = {} ): IRouteConfig { // Create basic NFTables route const route = createNfTablesRoute( nameOrDomains, target, { ...options, ports: options.ports || 443, useTls: false } ); // Set TLS termination route.action.tls = { mode: 'terminate', certificate: options.certificate || 'auto' }; return route; } /** * Create a complete NFTables-based HTTPS setup with HTTP redirect * @param nameOrDomains Name or domain(s) to match * @param target Target host and port * @param options Additional route options * @returns Array of two route configurations (HTTPS and HTTP redirect) */ export function createCompleteNfTablesHttpsServer( nameOrDomains: string | string[], target: { host: string; port: number | 'preserve' }, options: { httpPort?: TPortRange; httpsPort?: TPortRange; protocol?: 'tcp' | 'udp' | 'all'; preserveSourceIP?: boolean; ipAllowList?: string[]; ipBlockList?: string[]; maxRate?: string; priority?: number; tableName?: string; useIPSets?: boolean; useAdvancedNAT?: boolean; certificate?: 'auto' | { key: string; cert: string }; } = {} ): IRouteConfig[] { // Create the HTTPS route using NFTables const httpsRoute = createNfTablesTerminateRoute( nameOrDomains, target, { ...options, ports: options.httpsPort || 443 } ); // Determine the domain(s) for HTTP redirect const domains = typeof nameOrDomains === 'string' && !nameOrDomains.includes('.') ? undefined : nameOrDomains; // Extract the HTTPS port for the redirect destination const httpsPort = typeof options.httpsPort === 'number' ? options.httpsPort : Array.isArray(options.httpsPort) && typeof options.httpsPort[0] === 'number' ? options.httpsPort[0] : 443; // Create the HTTP redirect route (this uses standard forwarding, not NFTables) const httpRedirectRoute = createHttpToHttpsRedirect( domains as any, // Type cast needed since domains can be undefined now httpsPort, { match: { ports: options.httpPort || 80, domains: domains as any // Type cast needed since domains can be undefined now }, name: `HTTP to HTTPS Redirect for ${Array.isArray(domains) ? domains.join(', ') : domains || 'all domains'}` } ); return [httpsRoute, httpRedirectRoute]; }