feat(device-edge): add fail-closed ingress admission

This commit is contained in:
Codex
2026-08-04 11:53:18 +03:00
parent 3bb5e6dc27
commit 1eb6c462e3
11 changed files with 335 additions and 60 deletions
@@ -38,6 +38,9 @@ test("single-NIC ingress source has no host publication and a fixed ipvlan", asy
"subnet: 192.168.68.0/22",
"gateway: 192.168.68.1",
"gw_priority: 100",
"DEVICE_EDGE_RELAY_SOURCE_POLICY: public-ipv4-only",
'DEVICE_EDGE_RELAY_MAX_TRACKED_SOURCE_ADDRESSES: "2048"',
'DEVICE_EDGE_RELAY_MAX_BYTES_PER_DIRECTION: "262144"',
]) {
assert.ok(ingress.includes(required), `missing ingress boundary: ${required}`);
}
@@ -82,3 +85,26 @@ test("ingress descriptor keeps address approval and router exposure gated", asyn
assert.equal(descriptor.amneziaHostFullTunnel, "preserved");
assert.equal(descriptor.routerNatFirewall, "separate-manual-gate");
});
test("admission-gate descriptor pins the fail-closed relay boundary", async () => {
const descriptor = JSON.parse(await readFile(
resolve(
devicePlaneRoot,
"deployment/device-edge-admission-gate-v1.json",
),
"utf8",
));
assert.equal(
descriptor.schemaVersion,
"nodedc.device-edge.admission-gate.v1",
);
assert.equal(descriptor.sourceAdmission, "public-ipv4-only");
assert.equal(descriptor.maxTrackedSourceAddresses, 2048);
assert.equal(descriptor.maxBytesPerDirection, 262144);
assert.equal(descriptor.hostPortPublication, "disabled");
assert.equal(descriptor.healthPublication, "disabled");
assert.equal(descriptor.commandTransport, "disabled");
assert.equal(descriptor.gelios, "untouched");
assert.equal(descriptor.routerNatFirewall, "separate-manual-gate");
});
@@ -31,6 +31,7 @@ test("relay is transparent and never emits its own protocol bytes", async () =>
tcpPort: 0,
upstreamHost: "127.0.0.1",
upstreamPort: upstream.port,
resolveRemoteAddress: () => "8.8.8.8",
});
const addresses = await runtime.start();
try {
@@ -47,6 +48,73 @@ test("relay is transparent and never emits its own protocol bytes", async () =>
}
});
test("enabled ingress rejects a non-public source before opening upstream", async () => {
const upstream = await startEchoServer();
const runtime = createDeviceEdgeRelayRuntime({
healthPort: 0,
ingressEnabled: true,
tcpHost: "0.0.0.0",
tcpPort: 0,
upstreamHost: "127.0.0.1",
upstreamPort: upstream.port,
resolveRemoteAddress: () => "127.0.0.1",
});
const addresses = await runtime.start();
try {
const response = await sendAndCollect(
addresses.tcpAddress.port,
Buffer.from("denied"),
);
assert.equal(response.length, 0);
assert.equal(runtime.status().totalAccepted, 0);
assert.equal(runtime.status().totalForwarded, 0);
assert.equal(runtime.status().sourceAdmission, "public-ipv4-only");
} finally {
await runtime.stop();
await closeServer(upstream.server);
}
});
test("relay terminates a byte stream that exceeds its per-direction budget", async () => {
const upstream = await startEchoServer();
const runtime = createDeviceEdgeRelayRuntime({
healthPort: 0,
ingressEnabled: true,
tcpHost: "0.0.0.0",
tcpPort: 0,
upstreamHost: "127.0.0.1",
upstreamPort: upstream.port,
resolveRemoteAddress: () => "8.8.8.8",
maxBytesPerDirection: 1024,
});
const addresses = await runtime.start();
try {
const response = await sendAndCollect(
addresses.tcpAddress.port,
Buffer.alloc(1025, 0x5d),
);
assert.ok(response.length <= 1024);
assert.equal(runtime.status().totalForwarded, 1);
assert.ok(runtime.status().totalRejected >= 1);
} finally {
await runtime.stop();
await closeServer(upstream.server);
}
});
test("production ingress cannot opt out of public IPv4 admission", () => {
assert.throws(
() => createDeviceEdgeRelayRuntime({
ingressEnabled: true,
tcpHost: "0.0.0.0",
upstreamHost: "device-edge-backhaul",
upstreamPort: 19921,
sourcePolicy: "any",
}),
/device_edge_relay_ingress_source_policy_invalid/,
);
});
test("enabled relay requires a concrete private upstream", () => {
assert.throws(
() => createDeviceEdgeRelayRuntime({
@@ -77,14 +145,14 @@ function startEchoServer() {
}
function sendAndCollect(port, payload) {
return new Promise((resolve, reject) => {
return new Promise((resolve) => {
const chunks = [];
const socket = connect({ host: "127.0.0.1", port }, () => {
socket.end(payload);
});
socket.on("data", (chunk) => chunks.push(chunk));
socket.on("close", () => resolve(Buffer.concat(chunks)));
socket.on("error", reject);
socket.on("error", () => {});
});
}