feat(proxies): introduce nftables command executor and utilities, default certificate provider, expanded route/socket helper modules, and security improvements
This commit is contained in:
156
ts/proxies/nftables-proxy/utils/nft-rule-validator.ts
Normal file
156
ts/proxies/nftables-proxy/utils/nft-rule-validator.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
/**
|
||||
* NFTables Rule Validator
|
||||
*
|
||||
* Handles validation of settings and inputs for nftables operations.
|
||||
* Prevents command injection and ensures valid values.
|
||||
*/
|
||||
|
||||
import type { PortRange, NfTableProxyOptions } from '../models/index.js';
|
||||
import { NftValidationError } from '../models/index.js';
|
||||
import { validatePorts } from './nft-port-spec-normalizer.js';
|
||||
|
||||
// IP address validation patterns
|
||||
const IPV4_REGEX = /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(\/([0-9]|[1-2][0-9]|3[0-2]))?$/;
|
||||
const IPV6_REGEX = /^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))(\/([0-9]|[1-9][0-9]|1[0-1][0-9]|12[0-8]))?$/;
|
||||
const HOSTNAME_REGEX = /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/;
|
||||
const TABLE_NAME_REGEX = /^[a-zA-Z0-9_]+$/;
|
||||
const RATE_REGEX = /^[0-9]+[kKmMgG]?bps$/;
|
||||
|
||||
/**
|
||||
* Validates an IP address (IPv4 or IPv6)
|
||||
*/
|
||||
export function isValidIP(ip: string): boolean {
|
||||
return IPV4_REGEX.test(ip) || IPV6_REGEX.test(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an IPv4 address
|
||||
*/
|
||||
export function isValidIPv4(ip: string): boolean {
|
||||
return IPV4_REGEX.test(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an IPv6 address
|
||||
*/
|
||||
export function isValidIPv6(ip: string): boolean {
|
||||
return IPV6_REGEX.test(ip);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a hostname
|
||||
*/
|
||||
export function isValidHostname(hostname: string): boolean {
|
||||
return HOSTNAME_REGEX.test(hostname);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a table name for nftables
|
||||
*/
|
||||
export function isValidTableName(tableName: string): boolean {
|
||||
return TABLE_NAME_REGEX.test(tableName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a rate specification (e.g., "10mbps")
|
||||
*/
|
||||
export function isValidRate(rate: string): boolean {
|
||||
return RATE_REGEX.test(rate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an array of IP addresses
|
||||
*/
|
||||
export function validateIPs(ips?: string[]): void {
|
||||
if (!ips) return;
|
||||
|
||||
for (const ip of ips) {
|
||||
if (!isValidIP(ip)) {
|
||||
throw new NftValidationError(`Invalid IP address format: ${ip}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a host (can be hostname or IP)
|
||||
*/
|
||||
export function validateHost(host?: string): void {
|
||||
if (!host) return;
|
||||
|
||||
if (!isValidHostname(host) && !isValidIP(host)) {
|
||||
throw new NftValidationError(`Invalid host format: ${host}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates a table name
|
||||
*/
|
||||
export function validateTableName(tableName?: string): void {
|
||||
if (!tableName) return;
|
||||
|
||||
if (!isValidTableName(tableName)) {
|
||||
throw new NftValidationError(
|
||||
`Invalid table name: ${tableName}. Only alphanumeric characters and underscores are allowed.`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates QoS settings
|
||||
*/
|
||||
export function validateQosSettings(qos?: NfTableProxyOptions['qos']): void {
|
||||
if (!qos?.enabled) return;
|
||||
|
||||
if (qos.maxRate && !isValidRate(qos.maxRate)) {
|
||||
throw new NftValidationError(
|
||||
`Invalid rate format: ${qos.maxRate}. Use format like "10mbps", "1gbps", etc.`
|
||||
);
|
||||
}
|
||||
|
||||
if (qos.priority !== undefined) {
|
||||
if (qos.priority < 1 || qos.priority > 10 || !Number.isInteger(qos.priority)) {
|
||||
throw new NftValidationError(
|
||||
`Invalid priority: ${qos.priority}. Must be an integer between 1 and 10.`
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates all NfTablesProxy settings
|
||||
*/
|
||||
export function validateSettings(settings: NfTableProxyOptions): void {
|
||||
// Validate port numbers
|
||||
validatePorts(settings.fromPort);
|
||||
validatePorts(settings.toPort);
|
||||
|
||||
// Validate IP addresses
|
||||
validateIPs(settings.ipAllowList);
|
||||
validateIPs(settings.ipBlockList);
|
||||
|
||||
// Validate target host
|
||||
validateHost(settings.toHost);
|
||||
|
||||
// Validate table name
|
||||
validateTableName(settings.tableName);
|
||||
|
||||
// Validate QoS settings
|
||||
validateQosSettings(settings.qos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if an IP matches the given family (ip or ip6)
|
||||
*/
|
||||
export function isIPForFamily(ip: string, family: 'ip' | 'ip6'): boolean {
|
||||
if (family === 'ip6') {
|
||||
return ip.includes(':');
|
||||
}
|
||||
return ip.includes('.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter IPs by family
|
||||
*/
|
||||
export function filterIPsByFamily(ips: string[], family: 'ip' | 'ip6'): string[] {
|
||||
return ips.filter(ip => isIPForFamily(ip, family));
|
||||
}
|
||||
Reference in New Issue
Block a user