geonix 1.35.0 → 1.35.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/package.json +1 -1
- package/src/Gateway.js +34 -0
package/README.md
CHANGED
|
@@ -298,6 +298,7 @@ Each returned part has:
|
|
|
298
298
|
| `GX_HEALTH_TIMEOUT` | `2000` | Registry health-probe timeout per advertised address (ms) |
|
|
299
299
|
| `GX_SECRET` | — | Encryption key: AES-256-GCM payloads + HMAC-SHA256 subjects. Services without the same key cannot communicate. |
|
|
300
300
|
| `GX_DEBUG_ENDPOINT` | — | Mount path for the debug router (e.g. `/_debug`). Disabled when unset. |
|
|
301
|
+
| `GX_DEBUG_ENDPOINT_AUTH` | — | Basic-auth credentials for the debug router as `username:password`. When set, all debug routes (and the UI) require `Authorization: Basic …`. OPTIONS requests bypass to keep CORS preflight working. |
|
|
301
302
|
|
|
302
303
|
## Local transport (`GX_TRANSPORT=local://`)
|
|
303
304
|
|
package/package.json
CHANGED
package/src/Gateway.js
CHANGED
|
@@ -10,6 +10,7 @@ import { logger } from "./Logger.js";
|
|
|
10
10
|
import { readFileSync } from "node:fs";
|
|
11
11
|
import { fileURLToPath } from "node:url";
|
|
12
12
|
import { dirname, join } from "node:path";
|
|
13
|
+
import { timingSafeEqual } from "node:crypto";
|
|
13
14
|
|
|
14
15
|
const DEBUG_HTML = readFileSync(join(dirname(fileURLToPath(import.meta.url)), "debug.html"), "utf8");
|
|
15
16
|
|
|
@@ -342,6 +343,39 @@ export class Gateway {
|
|
|
342
343
|
#debugRouter() {
|
|
343
344
|
const router = Router();
|
|
344
345
|
|
|
346
|
+
// Basic Auth gate: when GX_DEBUG_ENDPOINT_AUTH is set as "user:pass", all
|
|
347
|
+
// debug routes require a matching Authorization: Basic <base64> header.
|
|
348
|
+
// OPTIONS preflight bypasses so cross-origin CORS still works.
|
|
349
|
+
router.use((req, res, next) => {
|
|
350
|
+
const expected = process.env.GX_DEBUG_ENDPOINT_AUTH;
|
|
351
|
+
if (!expected) {
|
|
352
|
+
return next();
|
|
353
|
+
}
|
|
354
|
+
if (req.method === "OPTIONS") {
|
|
355
|
+
return next();
|
|
356
|
+
}
|
|
357
|
+
const challenge = (msg) => {
|
|
358
|
+
res.set("WWW-Authenticate", 'Basic realm="Geonix Debug", charset="UTF-8"');
|
|
359
|
+
res.status(401).json({ error: msg });
|
|
360
|
+
};
|
|
361
|
+
const header = req.headers.authorization;
|
|
362
|
+
if (!header || !header.startsWith("Basic ")) {
|
|
363
|
+
return challenge("authentication required");
|
|
364
|
+
}
|
|
365
|
+
let provided;
|
|
366
|
+
try {
|
|
367
|
+
provided = Buffer.from(header.slice(6).trim(), "base64").toString("utf8");
|
|
368
|
+
} catch {
|
|
369
|
+
return challenge("invalid Authorization header");
|
|
370
|
+
}
|
|
371
|
+
const a = Buffer.from(expected);
|
|
372
|
+
const b = Buffer.from(provided);
|
|
373
|
+
if (a.length !== b.length || !timingSafeEqual(a, b)) {
|
|
374
|
+
return challenge("invalid credentials");
|
|
375
|
+
}
|
|
376
|
+
next();
|
|
377
|
+
});
|
|
378
|
+
|
|
345
379
|
router.use((req, res, next) => {
|
|
346
380
|
stats.debug_requests++;
|
|
347
381
|
|