@lensmcp/cluster 1.16.6 → 1.16.9
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/executors/gateway/runtime/auth.d.ts +2 -1
- package/executors/gateway/runtime/auth.d.ts.map +1 -1
- package/executors/gateway/runtime/auth.js +2 -2
- package/executors/gateway/runtime/dev-auth.d.ts +9 -2
- package/executors/gateway/runtime/dev-auth.d.ts.map +1 -1
- package/executors/gateway/runtime/dev-auth.js +43 -14
- package/executors/gateway/runtime/discovery.d.ts +1 -1
- package/executors/gateway/runtime/discovery.d.ts.map +1 -1
- package/executors/gateway/runtime/discovery.js +7 -2
- package/executors/gateway/runtime/lifecycle.d.ts.map +1 -1
- package/executors/gateway/runtime/lifecycle.js +3 -1
- package/executors/gateway/runtime/server.d.ts.map +1 -1
- package/executors/gateway/runtime/server.js +69 -2
- package/executors/gateway/runtime/types.d.ts +4 -0
- package/executors/gateway/runtime/types.d.ts.map +1 -1
- package/main.devserver.js +26 -1
- package/package.json +3 -3
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { AuthVerdict, GatewayRuntime, GwTrace, Route } from './types';
|
|
2
2
|
import type { Observability } from './observability';
|
|
3
|
+
import type { JwtVerifier } from '../jwks-verify';
|
|
3
4
|
export interface EdgeAuth {
|
|
4
5
|
internal(req: import('node:http').IncomingMessage, route: Route, trace: GwTrace | undefined): AuthVerdict;
|
|
5
6
|
public(req: import('node:http').IncomingMessage, route: Route, trace: GwTrace | undefined): AuthVerdict;
|
|
6
7
|
}
|
|
7
|
-
export declare function createEdgeAuth(rt: GatewayRuntime, obs: Observability): EdgeAuth;
|
|
8
|
+
export declare function createEdgeAuth(rt: GatewayRuntime, obs: Observability, verifier?: JwtVerifier): EdgeAuth;
|
|
8
9
|
//# sourceMappingURL=auth.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/auth.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/auth.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AAC3E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AACrD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAElD,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,GAAG,EAAE,OAAO,WAAW,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,CAAC;IAC1G,MAAM,CAAC,GAAG,EAAE,OAAO,WAAW,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,CAAC;CACzG;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,WAAW,GAAG,QAAQ,CA8CvG"}
|
|
@@ -14,7 +14,7 @@ exports.createEdgeAuth = createEdgeAuth;
|
|
|
14
14
|
*/
|
|
15
15
|
const manifest_1 = require("../manifest");
|
|
16
16
|
const dev_auth_1 = require("./dev-auth");
|
|
17
|
-
function createEdgeAuth(rt, obs) {
|
|
17
|
+
function createEdgeAuth(rt, obs, verifier) {
|
|
18
18
|
const { traceStep } = obs;
|
|
19
19
|
const internal = (req, _route, trace) => {
|
|
20
20
|
// service-to-service: validate the CALLER's api key at the boundary
|
|
@@ -37,7 +37,7 @@ function createEdgeAuth(rt, obs) {
|
|
|
37
37
|
const method = (req.method ?? 'GET').toUpperCase();
|
|
38
38
|
const resolved = (0, manifest_1.authRuleFor)(policy, url0, method);
|
|
39
39
|
if (resolved.mode === 'jwt') {
|
|
40
|
-
const claims = (0, dev_auth_1.verifyDevJwt)(req);
|
|
40
|
+
const claims = (0, dev_auth_1.verifyDevJwt)(req, verifier);
|
|
41
41
|
if (!claims) {
|
|
42
42
|
traceStep(trace, 'auth', { mode: 'jwt', ok: false });
|
|
43
43
|
return { ok: false, status: 401, reason: 'unauthorized', detail: 'Unauthorized: invalid or missing token' };
|
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
import type * as http from 'node:http';
|
|
2
2
|
import type { Attrs } from './types';
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
import type { JwtVerifier } from '../jwks-verify';
|
|
4
|
+
/**
|
|
5
|
+
* Verify the end-user JWT and return its claims, or undefined.
|
|
6
|
+
*
|
|
7
|
+
* @param verifier when provided (the RS256/JWKS verifier wired by the composition root), it is the ONLY
|
|
8
|
+
* acceptor — the token is verified against the issuer's published keys by `kid`, so a rogue key / unknown
|
|
9
|
+
* kid / bad signature / `alg:none` / HS256-confusion / expired token all return undefined (fail closed).
|
|
10
|
+
*/
|
|
11
|
+
export declare function verifyDevJwt(req: http.IncomingMessage, verifier?: JwtVerifier): Record<string, unknown> | undefined;
|
|
5
12
|
/** Stamp verified identity onto the forwarded request — tenant from the `tid` CLAIM, never a client header. */
|
|
6
13
|
export declare function stampIdentity(req: http.IncomingMessage, claims: Record<string, unknown>): void;
|
|
7
14
|
/** Build the ABAC attribute bag from the verified claims + request envelope. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev-auth.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/dev-auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"dev-auth.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/dev-auth.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,KAAK,IAAI,MAAM,WAAW,CAAC;AACvC,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AASlD;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,QAAQ,CAAC,EAAE,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAyCnH;AAED,+GAA+G;AAC/G,wBAAgB,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAQ9F;AAED,gFAAgF;AAChF,eAAO,MAAM,QAAQ,GAAI,KAAK,IAAI,CAAC,eAAe,EAAE,QAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,KAKpF,CAAC"}
|
|
@@ -4,25 +4,45 @@ exports.devAttrs = void 0;
|
|
|
4
4
|
exports.verifyDevJwt = verifyDevJwt;
|
|
5
5
|
exports.stampIdentity = stampIdentity;
|
|
6
6
|
/**
|
|
7
|
-
* Dev edge auth
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Dev edge auth: verify the end-user token, then stamp the trusted identity headers the services read
|
|
8
|
+
* (via GatewayTrustGuard). It MIRRORS the prod gateway's posture — a real JWKS/RS256 verifier when one is
|
|
9
|
+
* wired (see `../jwks-verify` + the composition in `server.ts`), an HS256 shared-secret path as the legacy
|
|
10
|
+
* fallback. Pure functions (the verifier is injected, so unit tests supply their own JWKS).
|
|
11
|
+
*
|
|
12
|
+
* SECURITY (the invariant this file must hold): a `jwt` route MUST NOT accept a token whose signature the
|
|
13
|
+
* edge cannot verify. When a JWKS verifier is wired it is the SOLE acceptor — every alg goes through it, so
|
|
14
|
+
* an unknown `kid`, a bad signature, `alg:none`, an HS256-confusion token, and an expired token ALL fail
|
|
15
|
+
* closed (rejected). Without a verifier, an asymmetric (RS256/ES*) token cannot be checked (we don't hold
|
|
16
|
+
* the issuer's public keys), so it also fails closed — unless an operator EXPLICITLY opts into the old
|
|
17
|
+
* insecure decode-only path (`LENSMCP_GW_DEV_JWT_INSECURE_DECODE=1`) for local bring-up. The HS256
|
|
18
|
+
* shared-secret path stays for services using that convention (and is itself bypassed once JWKS is wired).
|
|
10
19
|
*/
|
|
11
20
|
const node_crypto_1 = require("node:crypto");
|
|
12
|
-
/**
|
|
13
|
-
function
|
|
14
|
-
const
|
|
15
|
-
const auth = String(
|
|
16
|
-
|
|
21
|
+
/** The compact JWS from the `Authorization: Bearer …` header, or undefined. */
|
|
22
|
+
function bearerToken(req) {
|
|
23
|
+
const raw = Array.isArray(req.headers['authorization']) ? req.headers['authorization'][0] : req.headers['authorization'];
|
|
24
|
+
const auth = String(raw ?? '');
|
|
25
|
+
return auth.startsWith('Bearer ') ? auth.slice(7) : undefined;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Verify the end-user JWT and return its claims, or undefined.
|
|
29
|
+
*
|
|
30
|
+
* @param verifier when provided (the RS256/JWKS verifier wired by the composition root), it is the ONLY
|
|
31
|
+
* acceptor — the token is verified against the issuer's published keys by `kid`, so a rogue key / unknown
|
|
32
|
+
* kid / bad signature / `alg:none` / HS256-confusion / expired token all return undefined (fail closed).
|
|
33
|
+
*/
|
|
34
|
+
function verifyDevJwt(req, verifier) {
|
|
35
|
+
const token = bearerToken(req);
|
|
36
|
+
if (!token)
|
|
17
37
|
return undefined;
|
|
18
|
-
const [h, p, s] =
|
|
38
|
+
const [h, p, s] = token.split('.');
|
|
19
39
|
if (!h || !p || !s)
|
|
20
40
|
return undefined;
|
|
21
|
-
//
|
|
22
|
-
//
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
//
|
|
41
|
+
// Preferred: a real JWKS/RS256 verifier (mirrors the prod gateway's `jwks ? jwks.verify(tok) : hs256`).
|
|
42
|
+
// When wired it is the SOLE acceptor, so every forgery vector below is closed at once.
|
|
43
|
+
if (verifier)
|
|
44
|
+
return verifier.verify(token);
|
|
45
|
+
// ── No verifier wired (legacy dev) ──────────────────────────────────────────────────────────────────
|
|
26
46
|
let alg;
|
|
27
47
|
try {
|
|
28
48
|
alg = JSON.parse(Buffer.from(h, 'base64url').toString())['alg'];
|
|
@@ -31,6 +51,13 @@ function verifyDevJwt(req) {
|
|
|
31
51
|
return undefined;
|
|
32
52
|
}
|
|
33
53
|
if (typeof alg === 'string' && alg !== 'HS256') {
|
|
54
|
+
// An asymmetric IdP (RS256 + JWKS) does NOT use the HS256 dev shared secret. Without the issuer's public
|
|
55
|
+
// keys we CANNOT verify the signature, so — unlike the old code, which decode-accepted any RS256 token
|
|
56
|
+
// (a full edge auth bypass: a rogue key or a forged `alg:none` sailed through) — we FAIL CLOSED. Wire a
|
|
57
|
+
// JWKS verifier (LENSMCP_GW_JWKS_URL, or the auto-derived auth-host JWKS) to accept RS256 tokens.
|
|
58
|
+
if (process.env['LENSMCP_GW_DEV_JWT_INSECURE_DECODE'] !== '1')
|
|
59
|
+
return undefined; // fail closed
|
|
60
|
+
// Explicit, dev-only opt-in to the legacy decode + exp-check (NO signature check) — bring-up escape hatch.
|
|
34
61
|
try {
|
|
35
62
|
const rsClaims = JSON.parse(Buffer.from(p, 'base64url').toString());
|
|
36
63
|
if (typeof rsClaims['exp'] === 'number' && Date.now() / 1000 > rsClaims['exp'])
|
|
@@ -41,6 +68,8 @@ function verifyDevJwt(req) {
|
|
|
41
68
|
return undefined;
|
|
42
69
|
}
|
|
43
70
|
}
|
|
71
|
+
// HS256 shared-secret convention (only reachable when NO JWKS verifier is wired).
|
|
72
|
+
const secret = process.env['AUTH_JWT_SECRET'] ?? 'tetros-dev-secret';
|
|
44
73
|
const expected = (0, node_crypto_1.createHmac)('sha256', secret).update(`${h}.${p}`).digest('base64url');
|
|
45
74
|
const a = Buffer.from(s);
|
|
46
75
|
const b = Buffer.from(expected);
|
|
@@ -10,7 +10,7 @@ export declare function lensPortFromUpstream(upstream?: string): number | undefi
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function discoverRoutes(rootDir: string, projects: Record<string, {
|
|
12
12
|
root: string;
|
|
13
|
-
}>, sockBaseDir?: string): {
|
|
13
|
+
}>, sockBaseDir?: string, wsKey?: string): {
|
|
14
14
|
routes: Route[];
|
|
15
15
|
services: ServiceCtl[];
|
|
16
16
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/discovery.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAoC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE7F,uFAAuF;AACvF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,SAAO,GAAG,MAAM,GAAG,SAAS,CAa7E;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI1E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAC1C,WAAW,SAAc,
|
|
1
|
+
{"version":3,"file":"discovery.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/discovery.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAoC,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAE7F,uFAAuF;AACvF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,SAAO,GAAG,MAAM,GAAG,SAAS,CAa7E;AAED,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAI1E;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAC5B,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,EAC1C,WAAW,SAAc,EAIzB,KAAK,SAAK,GACT;IAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAAC,QAAQ,EAAE,UAAU,EAAE,CAAA;CAAE,CA2D7C"}
|
|
@@ -43,7 +43,12 @@ function lensPortFromUpstream(upstream) {
|
|
|
43
43
|
* the route table: host routes first (plus auto `internal.<host>` for pod
|
|
44
44
|
* services), the `default` catch-all last.
|
|
45
45
|
*/
|
|
46
|
-
function discoverRoutes(rootDir, projects, sockBaseDir = os.tmpdir()
|
|
46
|
+
function discoverRoutes(rootDir, projects, sockBaseDir = os.tmpdir(),
|
|
47
|
+
// The workspace key namespaces the pod sock dir (planning/multi-workspace-gateway.md) — MUST match
|
|
48
|
+
// `main.devserver.ts`'s `SOCK_DIR` derivation so the gateway scans the same dir the devserver writes.
|
|
49
|
+
// Empty → the legacy un-namespaced path (single-workspace / pre-multi-workspace).
|
|
50
|
+
wsKey = '') {
|
|
51
|
+
const sockDirFor = (service) => wsKey ? path.join(sockBaseDir, wsKey, `${service}-devserver`) : path.join(sockBaseDir, `${service}-devserver`);
|
|
47
52
|
const routes = [];
|
|
48
53
|
const services = [];
|
|
49
54
|
let defaultRoute;
|
|
@@ -67,7 +72,7 @@ function discoverRoutes(rootDir, projects, sockBaseDir = os.tmpdir()) {
|
|
|
67
72
|
// instrumented by the serve executor's node-instrumentation graft.
|
|
68
73
|
if (decl.lens)
|
|
69
74
|
console.warn(`[gateway] ${name}: cluster.lens is ignored for pod (service) decls — backends are instrumented by serve-hmr.`);
|
|
70
|
-
pool = { dir:
|
|
75
|
+
pool = { dir: sockDirFor(decl.service), socks: [], idx: -1, scannedAt: 0 };
|
|
71
76
|
svc = { project: name, decl, pool, state: 'down', lastUsed: Date.now(), inflight: 0, lastScaleAt: 0 };
|
|
72
77
|
services.push(svc);
|
|
73
78
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/lifecycle.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAapE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA4CrD;;sGAEsG;AACtG,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBtE;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,oBAAoB,CAAC,EACpG,GAAG,EAAE,MAAM,EACX,kBAAkB,EAAE,MAAM,EAC1B,GAAG,EAAE,gBAAgB,GACpB,kBAAkB,GAAG,SAAS,GAAG,IAAI,CAQvC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,EAC1B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC7D,OAAO,CAMT;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAChC,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,2EAA2E;IAC3E,YAAY,IAAI,MAAM,CAAC,OAAO,CAAC;CAChC;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,aAAa,GAAG,YAAY,
|
|
1
|
+
{"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/lifecycle.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAapE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA4CrD;;sGAEsG;AACtG,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAuBtE;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,GAAG,oBAAoB,CAAC,EACpG,GAAG,EAAE,MAAM,EACX,kBAAkB,EAAE,MAAM,EAC1B,GAAG,EAAE,gBAAgB,GACpB,kBAAkB,GAAG,SAAS,GAAG,IAAI,CAQvC;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,kBAAkB,EAAE,MAAM,EAC1B,GAAG,EAAE,MAAM,EACX,GAAG,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC7D,OAAO,CAMT;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,CAAC;IAChC,YAAY,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IACpC,QAAQ,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C,OAAO,CAAC,GAAG,EAAE,UAAU,GAAG,IAAI,CAAC;IAC/B,WAAW,CAAC,GAAG,EAAE,UAAU,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IAChD,2EAA2E;IAC3E,YAAY,IAAI,MAAM,CAAC,OAAO,CAAC;CAChC;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,cAAc,EAAE,GAAG,EAAE,aAAa,GAAG,YAAY,CA6PvF"}
|
|
@@ -162,7 +162,9 @@ function createServiceLayer(rt, obs) {
|
|
|
162
162
|
// "idle-kill leaves live pods" leak. (--childCount as a CLI arg already
|
|
163
163
|
// avoids the env-loss the daemon was relied on for.) NX_DAEMON=false also
|
|
164
164
|
// means env vars DO reach the pod, so the east-west key + CA propagate.
|
|
165
|
-
|
|
165
|
+
// LENSMCP_WS_KEY namespaces this service's pod sock dir ($TMPDIR/<wsKey>/<service>-devserver) so a
|
|
166
|
+
// shared multi-workspace gateway's same-named services don't collide (planning/multi-workspace-gateway.md).
|
|
167
|
+
env: { ...process.env, NX_DAEMON: 'false', ...(rt.wsKey ? { LENSMCP_WS_KEY: rt.wsKey } : {}), ...eastWestEnv(rt, svc.project) },
|
|
166
168
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
167
169
|
});
|
|
168
170
|
svc.lastErrors = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/server.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/server.ts"],"names":[],"mappings":"AAwBA,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAkB,qBAAqB,EAAsB,MAAM,SAAS,CAAC;AAkDxH,wBAAsB,YAAY,CAChC,OAAO,EAAE,qBAAqB,EAC9B,OAAO,EAAE,cAAc,GACtB,OAAO,CAAC,aAAa,CAAC,CAgLxB"}
|
|
@@ -15,6 +15,7 @@ const tslib_1 = require("tslib");
|
|
|
15
15
|
const http = tslib_1.__importStar(require("node:http"));
|
|
16
16
|
const path = tslib_1.__importStar(require("node:path"));
|
|
17
17
|
const discovery_1 = require("./discovery");
|
|
18
|
+
const scope_1 = require("./scope");
|
|
18
19
|
const service_keys_1 = require("./service-keys");
|
|
19
20
|
const observability_1 = require("./observability");
|
|
20
21
|
const lifecycle_1 = require("./lifecycle");
|
|
@@ -24,6 +25,57 @@ const auth_1 = require("./auth");
|
|
|
24
25
|
const hooks_1 = require("./hooks");
|
|
25
26
|
const handler_1 = require("./handler");
|
|
26
27
|
const upgrade_1 = require("./upgrade");
|
|
28
|
+
const jwks_verify_1 = require("../jwks-verify");
|
|
29
|
+
/**
|
|
30
|
+
* A `fetch`-shaped shim for the JWKS pull that accepts the dev gateway's OWN self-signed cert. The dev
|
|
31
|
+
* IdP's JWKS is served behind the same local HTTPS front door (e.g. `https://auth.tetros.ai.local`), whose
|
|
32
|
+
* cert chains to the local basic-ssl CA — global `fetch` would reject it. Read-only GET of a public,
|
|
33
|
+
* gateway-served JWKS document; the SIGNATURE trust comes from verifying tokens against those keys, not
|
|
34
|
+
* from the transport. Falls back to plain `http` for an http URL. Dev-only (only the dev executor uses it).
|
|
35
|
+
*/
|
|
36
|
+
function insecureLocalFetch(url, init) {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
const mod = url.startsWith('https:') ? require('node:https') : require('node:http');
|
|
39
|
+
const req = mod.request(url, { method: 'GET', headers: init?.headers, rejectUnauthorized: false }, (res) => {
|
|
40
|
+
const chunks = [];
|
|
41
|
+
res.on('data', (c) => chunks.push(c));
|
|
42
|
+
res.on('end', () => {
|
|
43
|
+
const status = res.statusCode ?? 0;
|
|
44
|
+
const text = Buffer.concat(chunks).toString('utf8');
|
|
45
|
+
resolve({ ok: status >= 200 && status < 300, status, json: async () => JSON.parse(text) });
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
req.on('error', reject);
|
|
49
|
+
req.end();
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The dev edge's JWT verifier — matches the prod gateway's RS256/JWKS posture so the LOCAL edge rejects
|
|
54
|
+
* forged tokens instead of decode-accepting them. The JWKS URL is `LENSMCP_GW_JWKS_URL` (explicit, same
|
|
55
|
+
* env as prod) or, absent that, is AUTO-DERIVED from the trusted `auth.*` host in the gateway's own route
|
|
56
|
+
* table (`https://<auth-host>/jwks.json`) — so a standard `auth.<apex>` IdP needs zero config. The host is
|
|
57
|
+
* taken from project.json (trusted), never from a token, so there's no SSRF/attacker-chosen JWKS. Returns
|
|
58
|
+
* undefined when no source is found (→ non-HS256 tokens fail closed unless the insecure opt-in is set).
|
|
59
|
+
*/
|
|
60
|
+
function buildEdgeJwtVerifier(routes) {
|
|
61
|
+
const explicit = process.env['LENSMCP_GW_JWKS_URL'];
|
|
62
|
+
let jwksUrl = explicit;
|
|
63
|
+
let derived = false;
|
|
64
|
+
if (!jwksUrl) {
|
|
65
|
+
const authHost = routes
|
|
66
|
+
.map((r) => r.host)
|
|
67
|
+
.find((h) => typeof h === 'string' && /^auth\./.test(h) && !h.startsWith('internal.'));
|
|
68
|
+
if (authHost) {
|
|
69
|
+
jwksUrl = `https://${authHost}/jwks.json`;
|
|
70
|
+
derived = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if (!jwksUrl)
|
|
74
|
+
return undefined;
|
|
75
|
+
const issuer = process.env['LENSMCP_GW_JWT_ISS'];
|
|
76
|
+
console.log(`[gateway] edge JWT: verifying via JWKS ${jwksUrl}${derived ? ' (auto-derived from the auth route)' : ''}`);
|
|
77
|
+
return (0, jwks_verify_1.createJwksVerifier)({ jwksUrl, ...(issuer ? { issuer } : {}), fetchImpl: insecureLocalFetch });
|
|
78
|
+
}
|
|
27
79
|
async function startGateway(options, context) {
|
|
28
80
|
// NB: this local `https` (TLS-on flag) deliberately SHADOWS the `node:https`
|
|
29
81
|
// module — which is why TLS/agents are reached via `require('node:https')`.
|
|
@@ -39,7 +91,12 @@ async function startGateway(options, context) {
|
|
|
39
91
|
// The gateway is a first-class lens source — the shared event file the MCP tails.
|
|
40
92
|
const eventFile = process.env['LENSMCP_EVENT_FILE'] ?? path.join(context.root, '.lensmcp', 'events.jsonl');
|
|
41
93
|
// --- discover ---------------------------------------------------------------
|
|
42
|
-
|
|
94
|
+
// The workspace key namespaces this workspace's pod sock dirs ($TMPDIR/<wsKey>/<service>-devserver) so a
|
|
95
|
+
// SHARED multi-workspace gateway can host several workspaces without their same-named services colliding
|
|
96
|
+
// (planning/multi-workspace-gateway.md). It flows to each service's devserver via `LENSMCP_WS_KEY` (see
|
|
97
|
+
// lifecycle.ts), and `discoverRoutes` derives the matching pool dir the gateway scans.
|
|
98
|
+
const wsKey = (0, scope_1.readLensScope)(context.root).key;
|
|
99
|
+
const { routes, services } = (0, discovery_1.discoverRoutes)(context.root, context.projectsConfigurations?.projects ?? {}, undefined, wsKey);
|
|
43
100
|
if (routes.length === 0) {
|
|
44
101
|
throw new Error('[gateway] no project declares a `cluster` field — nothing to route.');
|
|
45
102
|
}
|
|
@@ -48,6 +105,7 @@ async function startGateway(options, context) {
|
|
|
48
105
|
// --- the shared runtime (state + knobs; stopped() is a live getter) ---------
|
|
49
106
|
const rt = {
|
|
50
107
|
root: context.root,
|
|
108
|
+
wsKey,
|
|
51
109
|
eventFile,
|
|
52
110
|
routes,
|
|
53
111
|
services,
|
|
@@ -65,7 +123,15 @@ async function startGateway(options, context) {
|
|
|
65
123
|
const svcLayer = (0, lifecycle_1.createServiceLayer)(rt, obs);
|
|
66
124
|
const lens = (0, lens_children_1.createLensChildren)(rt, obs, options);
|
|
67
125
|
const proxyLayer = (0, proxy_1.createProxy)(rt, obs, svcLayer);
|
|
68
|
-
|
|
126
|
+
// Edge JWT verification (mirrors the prod gateway): when a JWKS source is configured/derivable, the dev
|
|
127
|
+
// edge verifies RS256 tokens by `kid` against the IdP's published keys and REJECTS anything it can't
|
|
128
|
+
// verify (rogue kid / bad sig / alg:none / HS256-confusion / expired) — closing the dev decode-only bypass.
|
|
129
|
+
const jwtVerifier = buildEdgeJwtVerifier(routes);
|
|
130
|
+
const edgeAuth = (0, auth_1.createEdgeAuth)(rt, obs, jwtVerifier);
|
|
131
|
+
// Warm the key cache (non-fatal, non-blocking): the IdP pod may still be booting, so a failure here just
|
|
132
|
+
// means the first tokens fail closed until the on-miss/periodic refresh lands the keys (matches prod).
|
|
133
|
+
if (jwtVerifier)
|
|
134
|
+
void jwtVerifier.refresh().catch((e) => console.warn('[gateway] edge JWKS warm failed (will retry):', e.message));
|
|
69
135
|
// Custom user middleware (the JWT seam; internal.* routes skip it) is wired as
|
|
70
136
|
// the BUILT-IN FIRST `afterAuth` hook — so "custom middleware runs AFTER core
|
|
71
137
|
// auth" and the `{ mode:'middleware' }` trace step are preserved.
|
|
@@ -167,6 +233,7 @@ async function startGateway(options, context) {
|
|
|
167
233
|
clearInterval(edgeFlusher);
|
|
168
234
|
clearInterval(sweeper);
|
|
169
235
|
obs.emit('info', 'gateway down', 'cluster-gateway', { kind: 'gateway-down' });
|
|
236
|
+
jwtVerifier?.stop(); // clear the JWKS periodic-refresh timer
|
|
170
237
|
for (const svc of services)
|
|
171
238
|
svcLayer.killSpawned(svc, 'gateway shutdown');
|
|
172
239
|
// Reap the lens-mode managed children (dashboard + MCP singletons + each
|
|
@@ -237,6 +237,10 @@ export interface GwTrace {
|
|
|
237
237
|
*/
|
|
238
238
|
export interface GatewayRuntime {
|
|
239
239
|
readonly root: string;
|
|
240
|
+
/** The per-workspace key (from `readLensScope`) — namespaces this workspace's pod sock dirs so a shared
|
|
241
|
+
* multi-workspace gateway can host several workspaces without same-named services colliding. Flows to
|
|
242
|
+
* each service devserver as `LENSMCP_WS_KEY`. */
|
|
243
|
+
readonly wsKey: string;
|
|
240
244
|
readonly eventFile: string;
|
|
241
245
|
readonly routes: Route[];
|
|
242
246
|
readonly services: ServiceCtl[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/types.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../../../libs/cluster/src/executors/gateway/runtime/types.ts"],"names":[],"mappings":"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"}
|
package/main.devserver.js
CHANGED
|
@@ -109,7 +109,17 @@ const WORKER_NAMES = (() => {
|
|
|
109
109
|
}
|
|
110
110
|
})();
|
|
111
111
|
// ----------------------------- Socket Helpers ---------------------------------
|
|
112
|
-
|
|
112
|
+
// The pod sockets live under a per-WORKSPACE dir when the gateway sets `LENSMCP_WS_KEY` (the shared
|
|
113
|
+
// multi-workspace gateway — planning/multi-workspace-gateway.md): `$TMPDIR/<wsKey>/<service>-devserver`.
|
|
114
|
+
// Without a key (standalone `serve`, or a single-workspace gateway that predates this) it's the legacy
|
|
115
|
+
// `$TMPDIR/<service>-devserver`. Namespacing is REQUIRED so two workspaces each running a service named
|
|
116
|
+
// `agent`/`auth` don't share `$TMPDIR/agent-devserver` — which would cross-route the gateway into the wrong
|
|
117
|
+
// workspace's pods and let one workspace's reap unlink the other's sockets. The gateway's `discovery.ts`
|
|
118
|
+
// mirror MUST derive the identical path.
|
|
119
|
+
const LENSMCP_WS_KEY = process.env.LENSMCP_WS_KEY || '';
|
|
120
|
+
const SOCK_DIR = LENSMCP_WS_KEY
|
|
121
|
+
? path.join(os.tmpdir(), LENSMCP_WS_KEY, `${SERVICE_NAME || 'lensmcp-cluster'}-devserver`)
|
|
122
|
+
: path.join(os.tmpdir(), `${SERVICE_NAME || 'lensmcp-cluster'}-devserver`);
|
|
113
123
|
function childSockPath(id) {
|
|
114
124
|
return path.join(SOCK_DIR, `child-${id}.sock`);
|
|
115
125
|
}
|
|
@@ -692,6 +702,21 @@ else {
|
|
|
692
702
|
proxy.ws(req, socket, head, { target: { socketPath: targetChild.socketPath } }, () => socket.destroy());
|
|
693
703
|
};
|
|
694
704
|
server.on('upgrade', upgradeHandler);
|
|
705
|
+
// The public TCP port is VESTIGIAL under the gateway: the gateway routes to this devserver over the
|
|
706
|
+
// unix-socket POOL ($TMPDIR/<service>-devserver/child-*.sock), never the TCP port. So a port clash must
|
|
707
|
+
// NOT crash the devserver — without this handler an EADDRINUSE (two services falling back to the same
|
|
708
|
+
// default port, or a not-yet-released port from a prior instance) throws an UNHANDLED 'error' that kills
|
|
709
|
+
// the parent → the supervisors respawn it → the old port is still held → EADDRINUSE again → a zombie
|
|
710
|
+
// devserver cascade. Handle it: log once and keep serving on the socket pool (gateway routing is
|
|
711
|
+
// unaffected). A distinct `cluster.port` / `config.<env>.yaml` port silences the warning.
|
|
712
|
+
server.on('error', (err) => {
|
|
713
|
+
if (err.code === 'EADDRINUSE') {
|
|
714
|
+
console.warn(`[parent] public port ${publicPort} already in use — continuing on the unix-socket pool only ` +
|
|
715
|
+
`(gateway routing is unaffected). Give this service a distinct cluster.port to silence this.`);
|
|
716
|
+
return; // do NOT rethrow: the socket pool is the real routing path under the gateway.
|
|
717
|
+
}
|
|
718
|
+
console.error(`[parent] public server error:`, err);
|
|
719
|
+
});
|
|
695
720
|
server.listen(publicPort, async () => {
|
|
696
721
|
const scheme = GATEWAY_HTTPS ? 'https' : 'http';
|
|
697
722
|
console.log(`[parent] Listening on ${scheme}://localhost:${publicPort}`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lensmcp/cluster",
|
|
3
|
-
"version": "1.16.
|
|
3
|
+
"version": "1.16.9",
|
|
4
4
|
"description": "Run your Nx workspace as a local production cluster: pods on unix sockets with true HMR, one https gateway with per-project domains, scale-from-zero, autoscale, idle-kill, local-CA TLS — observed by the LensMCP lens.",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"types": "./index.d.ts",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
}
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@lensmcp/node-instrumentation": "1.16.
|
|
69
|
-
"@lensmcp/nx-plugin": "1.16.
|
|
68
|
+
"@lensmcp/node-instrumentation": "1.16.9",
|
|
69
|
+
"@lensmcp/nx-plugin": "1.16.9",
|
|
70
70
|
"fork-ts-checker-webpack-plugin": "^9.0.0",
|
|
71
71
|
"glob": "^11.0.0",
|
|
72
72
|
"http-proxy": "^1.18.0",
|