Harden edge broker defaults and compose exposure

This commit is contained in:
Jeppe B
2026-06-01 22:10:14 +02:00
parent 12205eefe8
commit f2db92de09
6 changed files with 60 additions and 29 deletions
+14 -9
View File
@@ -25,15 +25,24 @@ function jsonResponse(res, statusCode, body) {
}
export function createBrokerServer(options = {}) {
const authMode = options.authMode || process.env.EDGE_AUTH_MODE || "stub";
const authMode = options.authMode || process.env.EDGE_AUTH_MODE || "strict";
const sharedSecret = options.sharedSecret ?? process.env.EDGE_BROKER_SHARED_SECRET ?? "";
const commandTimeoutMs = options.commandTimeoutMs ?? 10000;
const agents = new Map();
const pendingCommands = new Map();
const browserSessions = new Map();
const isStubMode = authMode === "stub";
const validateAgent = options.validateAgent || (async ({ gatewayId }) => ({ id: gatewayId }));
const validateShellSession = options.validateShellSession || (async ({ token }) => ({ id: token, gateway_id: 1, reason: "stub" }));
const validateAgent = options.validateAgent || (isStubMode
? (async ({ gatewayId }) => ({ id: gatewayId }))
: (async () => {
throw new Error("Unauthorized");
}));
const validateShellSession = options.validateShellSession || (isStubMode
? (async ({ token }) => ({ id: token, gateway_id: 1, reason: "stub" }))
: (async () => {
throw new Error("Unauthorized");
}));
const closeShellSession = options.closeShellSession || (async () => ({}));
const markBrowserSessionsClosed = (gatewayId, reason) => {
@@ -113,9 +122,7 @@ export function createBrokerServer(options = {}) {
socket.destroy();
return;
}
if (authMode !== "stub") {
await validateAgent({ gatewayId, token, headers: req.headers });
}
await validateAgent({ gatewayId, token, headers: req.headers });
wss.handleUpgrade(req, socket, head, (ws) => {
ws.gatewayId = gatewayId;
agents.set(gatewayId, ws);
@@ -130,9 +137,7 @@ export function createBrokerServer(options = {}) {
socket.destroy();
return;
}
const session = authMode === "stub"
? await validateShellSession({ token })
: await validateShellSession({ token, headers: req.headers });
const session = await validateShellSession({ token, headers: req.headers });
wss.handleUpgrade(req, socket, head, (ws) => {
ws.sessionToken = token;
+23
View File
@@ -24,6 +24,18 @@ function waitForClose(socket) {
});
}
function waitForCloseOrError(socket) {
return new Promise((resolve) => {
const onDone = () => {
socket.off("error", onDone);
socket.off("close", onDone);
resolve();
};
socket.once("error", onDone);
socket.once("close", onDone);
});
}
async function waitFor(predicate, { timeoutMs = 1000, intervalMs = 10, description = "condition" } = {}) {
const deadline = Date.now() + timeoutMs;
@@ -215,3 +227,14 @@ test("broker closes browser shell sessions when the agent disconnects before she
await broker.close();
});
test("broker defaults to strict auth when no validators are configured", async () => {
const broker = createBrokerServer();
const address = await broker.listen(0);
const port = address.port;
const agent = new WebSocket(`ws://127.0.0.1:${port}/ws/agent?gatewayId=701&token=agent-token`);
await waitForCloseOrError(agent);
await broker.close();
});
+6 -6
View File
@@ -24,19 +24,19 @@ test("traefik does not expose a dedicated public edge broker port", () => {
assert.doesNotMatch(traefikSource, /edge-broker:\s*\n\s*address:\s*":4300"/);
});
test("base docker compose exposes the edge broker service on port 4300", () => {
test("base docker compose restricts edge broker host binding to loopback", () => {
assert.match(baseComposeSource, /\bedge-broker:\b/);
assert.match(baseComposeSource, /edge-broker:\s*\n[\s\S]*?\n\s+ports:\s*\n\s+- "4300:4300"/);
assert.match(baseComposeSource, /edge-broker:\s*\n[\s\S]*?\n\s+ports:\s*\n\s+- "127\.0\.0\.1:4300:4300"/);
});
test("example docker compose exposes the edge broker service on port 4300", () => {
test("example docker compose restricts edge broker host binding to loopback", () => {
assert.match(exampleComposeSource, /\bedge-broker:\b/);
assert.match(exampleComposeSource, /edge-broker:\s*\n[\s\S]*?\n\s+ports:\s*\n\s+- "4300:4300"/);
assert.match(exampleComposeSource, /edge-broker:\s*\n[\s\S]*?\n\s+ports:\s*\n\s+- "127\.0\.0\.1:4300:4300"/);
});
test("php services receive broker websocket environment defaults", () => {
test("compose config does not provide insecure broker secret defaults", () => {
for (const composeSource of [baseComposeSource, exampleComposeSource]) {
assert.match(composeSource, /EDGE_BROKER_URL:\s*\$\{EDGE_BROKER_URL:-http:\/\/edge-broker:4300\}/);
assert.match(composeSource, /EDGE_BROKER_SHARED_SECRET:\s*\$\{EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev\}/);
assert.match(composeSource, /EDGE_BROKER_SHARED_SECRET:\s*\$\{EDGE_BROKER_SHARED_SECRET\}/);
}
});