Harden edge broker defaults and compose exposure
This commit is contained in:
+2
-1
@@ -49,7 +49,8 @@ ECONOMIC_API_APP_SECRET_TOKEN=
|
|||||||
|
|
||||||
# Edge broker defaults for shell relay and gateway dispatch.
|
# Edge broker defaults for shell relay and gateway dispatch.
|
||||||
EDGE_BROKER_URL=http://edge-broker:4300
|
EDGE_BROKER_URL=http://edge-broker:4300
|
||||||
EDGE_BROKER_SHARED_SECRET=truckwash-edge-dev
|
EDGE_BROKER_SHARED_SECRET=
|
||||||
|
EDGE_AUTH_MODE=strict
|
||||||
|
|
||||||
# Redis credentials
|
# Redis credentials
|
||||||
REDIS_CONFIG_HOST=redis
|
REDIS_CONFIG_HOST=redis
|
||||||
|
|||||||
@@ -52,9 +52,10 @@ services:
|
|||||||
dockerfile: services/edge-broker/Dockerfile
|
dockerfile: services/edge-broker/Dockerfile
|
||||||
container_name: edge-broker
|
container_name: edge-broker
|
||||||
environment:
|
environment:
|
||||||
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
|
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET}
|
||||||
|
EDGE_AUTH_MODE: ${EDGE_AUTH_MODE:-strict}
|
||||||
ports:
|
ports:
|
||||||
- "4300:4300"
|
- "127.0.0.1:4300:4300"
|
||||||
|
|
||||||
caddy:
|
caddy:
|
||||||
image: caddy:2.7.6-alpine
|
image: caddy:2.7.6-alpine
|
||||||
@@ -98,7 +99,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
AUTO_COMPOSER_INSTALL: "true"
|
AUTO_COMPOSER_INSTALL: "true"
|
||||||
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
||||||
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
|
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET}
|
||||||
volumes:
|
volumes:
|
||||||
- ./services/nginx/app:/var/www/html
|
- ./services/nginx/app:/var/www/html
|
||||||
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
||||||
@@ -120,7 +121,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
AUTO_COMPOSER_INSTALL: "false"
|
AUTO_COMPOSER_INSTALL: "false"
|
||||||
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
||||||
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
|
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET}
|
||||||
volumes:
|
volumes:
|
||||||
- ./services/nginx/app:/var/www/html
|
- ./services/nginx/app:/var/www/html
|
||||||
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
||||||
|
|||||||
+10
-9
@@ -86,9 +86,10 @@ services:
|
|||||||
dockerfile: services/edge-broker/Dockerfile
|
dockerfile: services/edge-broker/Dockerfile
|
||||||
container_name: edge-broker
|
container_name: edge-broker
|
||||||
environment:
|
environment:
|
||||||
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
|
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET}
|
||||||
|
EDGE_AUTH_MODE: ${EDGE_AUTH_MODE:-strict}
|
||||||
ports:
|
ports:
|
||||||
- "4300:4300"
|
- "127.0.0.1:4300:4300"
|
||||||
|
|
||||||
caddy:
|
caddy:
|
||||||
image: caddy:2.7.6-alpine
|
image: caddy:2.7.6-alpine
|
||||||
@@ -213,7 +214,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
AUTO_COMPOSER_INSTALL: "true"
|
AUTO_COMPOSER_INSTALL: "true"
|
||||||
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
||||||
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
|
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET}
|
||||||
volumes:
|
volumes:
|
||||||
- ./services/nginx/app:/var/www/html
|
- ./services/nginx/app:/var/www/html
|
||||||
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
||||||
@@ -234,7 +235,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
AUTO_COMPOSER_INSTALL: "false"
|
AUTO_COMPOSER_INSTALL: "false"
|
||||||
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
||||||
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
|
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET}
|
||||||
volumes:
|
volumes:
|
||||||
- ./services/nginx/app:/var/www/html
|
- ./services/nginx/app:/var/www/html
|
||||||
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
||||||
@@ -255,7 +256,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
AUTO_COMPOSER_INSTALL: "false"
|
AUTO_COMPOSER_INSTALL: "false"
|
||||||
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
||||||
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
|
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET}
|
||||||
volumes:
|
volumes:
|
||||||
- ./services/nginx/app:/var/www/html
|
- ./services/nginx/app:/var/www/html
|
||||||
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
||||||
@@ -276,7 +277,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
AUTO_COMPOSER_INSTALL: "false"
|
AUTO_COMPOSER_INSTALL: "false"
|
||||||
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
||||||
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
|
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET}
|
||||||
volumes:
|
volumes:
|
||||||
- ./services/nginx/app:/var/www/html
|
- ./services/nginx/app:/var/www/html
|
||||||
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
||||||
@@ -297,7 +298,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
AUTO_COMPOSER_INSTALL: "false"
|
AUTO_COMPOSER_INSTALL: "false"
|
||||||
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
||||||
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
|
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET}
|
||||||
volumes:
|
volumes:
|
||||||
- ./services/nginx/app:/var/www/html
|
- ./services/nginx/app:/var/www/html
|
||||||
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
||||||
@@ -318,7 +319,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
AUTO_COMPOSER_INSTALL: "false"
|
AUTO_COMPOSER_INSTALL: "false"
|
||||||
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
||||||
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
|
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET}
|
||||||
volumes:
|
volumes:
|
||||||
- ./services/nginx/staging:/var/www/html
|
- ./services/nginx/staging:/var/www/html
|
||||||
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
||||||
@@ -339,7 +340,7 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
AUTO_COMPOSER_INSTALL: "false"
|
AUTO_COMPOSER_INSTALL: "false"
|
||||||
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
EDGE_BROKER_URL: ${EDGE_BROKER_URL:-http://edge-broker:4300}
|
||||||
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev}
|
EDGE_BROKER_SHARED_SECRET: ${EDGE_BROKER_SHARED_SECRET}
|
||||||
volumes:
|
volumes:
|
||||||
- ./services/nginx/app:/var/www/html
|
- ./services/nginx/app:/var/www/html
|
||||||
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
- ./services/edge-agent/dist:/services/edge-agent/dist:ro
|
||||||
|
|||||||
@@ -25,15 +25,24 @@ function jsonResponse(res, statusCode, body) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function createBrokerServer(options = {}) {
|
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 sharedSecret = options.sharedSecret ?? process.env.EDGE_BROKER_SHARED_SECRET ?? "";
|
||||||
const commandTimeoutMs = options.commandTimeoutMs ?? 10000;
|
const commandTimeoutMs = options.commandTimeoutMs ?? 10000;
|
||||||
const agents = new Map();
|
const agents = new Map();
|
||||||
const pendingCommands = new Map();
|
const pendingCommands = new Map();
|
||||||
const browserSessions = new Map();
|
const browserSessions = new Map();
|
||||||
|
const isStubMode = authMode === "stub";
|
||||||
|
|
||||||
const validateAgent = options.validateAgent || (async ({ gatewayId }) => ({ id: gatewayId }));
|
const validateAgent = options.validateAgent || (isStubMode
|
||||||
const validateShellSession = options.validateShellSession || (async ({ token }) => ({ id: token, gateway_id: 1, reason: "stub" }));
|
? (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 closeShellSession = options.closeShellSession || (async () => ({}));
|
||||||
|
|
||||||
const markBrowserSessionsClosed = (gatewayId, reason) => {
|
const markBrowserSessionsClosed = (gatewayId, reason) => {
|
||||||
@@ -113,9 +122,7 @@ export function createBrokerServer(options = {}) {
|
|||||||
socket.destroy();
|
socket.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (authMode !== "stub") {
|
await validateAgent({ gatewayId, token, headers: req.headers });
|
||||||
await validateAgent({ gatewayId, token, headers: req.headers });
|
|
||||||
}
|
|
||||||
wss.handleUpgrade(req, socket, head, (ws) => {
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
||||||
ws.gatewayId = gatewayId;
|
ws.gatewayId = gatewayId;
|
||||||
agents.set(gatewayId, ws);
|
agents.set(gatewayId, ws);
|
||||||
@@ -130,9 +137,7 @@ export function createBrokerServer(options = {}) {
|
|||||||
socket.destroy();
|
socket.destroy();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const session = authMode === "stub"
|
const session = await validateShellSession({ token, headers: req.headers });
|
||||||
? await validateShellSession({ token })
|
|
||||||
: await validateShellSession({ token, headers: req.headers });
|
|
||||||
|
|
||||||
wss.handleUpgrade(req, socket, head, (ws) => {
|
wss.handleUpgrade(req, socket, head, (ws) => {
|
||||||
ws.sessionToken = token;
|
ws.sessionToken = token;
|
||||||
|
|||||||
@@ -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" } = {}) {
|
async function waitFor(predicate, { timeoutMs = 1000, intervalMs = 10, description = "condition" } = {}) {
|
||||||
const deadline = Date.now() + timeoutMs;
|
const deadline = Date.now() + timeoutMs;
|
||||||
|
|
||||||
@@ -215,3 +227,14 @@ test("broker closes browser shell sessions when the agent disconnects before she
|
|||||||
|
|
||||||
await broker.close();
|
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();
|
||||||
|
});
|
||||||
|
|||||||
@@ -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"/);
|
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, /\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, /\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]) {
|
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_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\}/);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user