feat(proxies): introduce nftables command executor and utilities, default certificate provider, expanded route/socket helper modules, and security improvements
This commit is contained in:
125
ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.ts
Normal file
125
ts/proxies/nftables-proxy/utils/nft-port-spec-normalizer.ts
Normal file
@@ -0,0 +1,125 @@
|
||||
/**
|
||||
* NFTables Port Specification Normalizer
|
||||
*
|
||||
* Handles normalization and validation of port specifications
|
||||
* for nftables rules.
|
||||
*/
|
||||
|
||||
import type { PortRange } from '../models/index.js';
|
||||
import { NftValidationError } from '../models/index.js';
|
||||
|
||||
/**
|
||||
* Normalizes port specifications into an array of port ranges
|
||||
*/
|
||||
export function normalizePortSpec(portSpec: number | PortRange | Array<number | PortRange>): PortRange[] {
|
||||
const result: PortRange[] = [];
|
||||
|
||||
if (Array.isArray(portSpec)) {
|
||||
// If it's an array, process each element
|
||||
for (const spec of portSpec) {
|
||||
result.push(...normalizePortSpec(spec));
|
||||
}
|
||||
} else if (typeof portSpec === 'number') {
|
||||
// Single port becomes a range with the same start and end
|
||||
result.push({ from: portSpec, to: portSpec });
|
||||
} else {
|
||||
// Already a range
|
||||
result.push(portSpec);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates port numbers or ranges
|
||||
*/
|
||||
export function validatePorts(port: number | PortRange | Array<number | PortRange>): void {
|
||||
if (Array.isArray(port)) {
|
||||
port.forEach(p => validatePorts(p));
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof port === 'number') {
|
||||
if (port < 1 || port > 65535) {
|
||||
throw new NftValidationError(`Invalid port number: ${port}`);
|
||||
}
|
||||
} else if (typeof port === 'object') {
|
||||
if (port.from < 1 || port.from > 65535 || port.to < 1 || port.to > 65535 || port.from > port.to) {
|
||||
throw new NftValidationError(`Invalid port range: ${port.from}-${port.to}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Format port range for nftables rule
|
||||
*/
|
||||
export function formatPortRange(range: PortRange): string {
|
||||
if (range.from === range.to) {
|
||||
return String(range.from);
|
||||
}
|
||||
return `${range.from}-${range.to}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert port spec to nftables expression
|
||||
*/
|
||||
export function portSpecToNftExpr(portSpec: number | PortRange | Array<number | PortRange>): string {
|
||||
const ranges = normalizePortSpec(portSpec);
|
||||
|
||||
if (ranges.length === 1) {
|
||||
return formatPortRange(ranges[0]);
|
||||
}
|
||||
|
||||
// Multiple ports/ranges need to use a set
|
||||
const ports = ranges.map(formatPortRange);
|
||||
return `{ ${ports.join(', ')} }`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if two port ranges overlap
|
||||
*/
|
||||
export function rangesOverlap(range1: PortRange, range2: PortRange): boolean {
|
||||
return range1.from <= range2.to && range2.from <= range1.to;
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge overlapping port ranges
|
||||
*/
|
||||
export function mergeOverlappingRanges(ranges: PortRange[]): PortRange[] {
|
||||
if (ranges.length <= 1) return ranges;
|
||||
|
||||
// Sort by start port
|
||||
const sorted = [...ranges].sort((a, b) => a.from - b.from);
|
||||
const merged: PortRange[] = [sorted[0]];
|
||||
|
||||
for (let i = 1; i < sorted.length; i++) {
|
||||
const current = sorted[i];
|
||||
const lastMerged = merged[merged.length - 1];
|
||||
|
||||
if (current.from <= lastMerged.to + 1) {
|
||||
// Ranges overlap or are adjacent, merge them
|
||||
lastMerged.to = Math.max(lastMerged.to, current.to);
|
||||
} else {
|
||||
// No overlap, add as new range
|
||||
merged.push(current);
|
||||
}
|
||||
}
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the total number of ports in a port specification
|
||||
*/
|
||||
export function countPorts(portSpec: number | PortRange | Array<number | PortRange>): number {
|
||||
const ranges = normalizePortSpec(portSpec);
|
||||
return ranges.reduce((total, range) => total + (range.to - range.from + 1), 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a port is within the given specification
|
||||
*/
|
||||
export function isPortInSpec(port: number, portSpec: number | PortRange | Array<number | PortRange>): boolean {
|
||||
const ranges = normalizePortSpec(portSpec);
|
||||
return ranges.some(range => port >= range.from && port <= range.to);
|
||||
}
|
||||
Reference in New Issue
Block a user