/** * Zone Detector * Auto-detects network zone from IP address by matching against system subnets */ import { subnetContains } from '@celilo/capabilities'; import { eq } from 'drizzle-orm'; import { getDb } from '../db/client'; import { NETWORK_ZONES, type NetworkZone, systemConfig } from '../db/schema'; /** * The zones a subnet can be declared for — every `NetworkZone` except * `external`. * * `external` is deliberately absent and must stay absent: it has no * `network.external.subnet` and must never be given one. It is not a segment * celilo manages, it is whatever the ISP handed you, so it is the RESIDUAL — * decided by `isPubliclyRoutable`, not by containment (design D1, amended). * * Derived from NETWORK_ZONES rather than listed by hand. The hand-written list * that used to be here had already dropped `control-plane-vpn`, which is the * third instance of that bug class in this repo. */ const SUBNET_BACKED_ZONES: readonly NetworkZone[] = NETWORK_ZONES.filter( (zone) => zone !== 'external', ); /** * Get system network configuration for a zone */ async function getZoneSubnet(zone: NetworkZone): Promise { const db = getDb(); // Try to get subnet from system config // Format: network.{zone}.subnet (e.g., network.dmz.subnet) const key = `network.${zone}.subnet`; const result = await db.select().from(systemConfig).where(eq(systemConfig.key, key)).limit(1); if (result.length === 0) { return null; } return result[0].value; } /** * Which zone an address belongs to, or `'unknown'` when celilo cannot say. * * **`'unknown'` is the honest answer, and it used to be unreachable.** This * returned `'external'` on no-match — so on a firewall with five RFC1918 legs * and three declared zones, private gateway addresses were each reported as * facing the internet. `NetworkInterface.zone` was already typed * `NetworkZone | 'unknown'` and nothing could produce it, because this claimed * `external` instead. The slot for the honest answer existed and was * unreachable. * * This answers ONLY the containment question. Whether an unmatched address is * an external edge is a different question, answered by `isPubliclyRoutable` — * see design D1 on why conflating the two was the defect. */ export async function detectZoneFromIp(ip: string): Promise { for (const zone of SUBNET_BACKED_ZONES) { const subnet = await getZoneSubnet(zone); if (subnet && subnetContains(subnet, ip)) { return zone; } } return 'unknown'; } /** * Validate IP address format */ export function isValidIp(ip: string): boolean { const parts = ip.split('.'); if (parts.length !== 4) { return false; } return parts.every((part) => { const num = Number.parseInt(part, 10); return !Number.isNaN(num) && num >= 0 && num <= 255; }); }