Skip to content

Commit cd5046c

Browse files
Marfuenclaude
andauthored
fix(db): drop inlined RDS CA bundle, use Node default trust store (#2775)
URGENT: production runtime fix. Staging is hitting: Error [PrismaClientKnownRequestError] Invalid `prisma.member.findFirst()` invocation Error opening a TLS connection: unable to get local issuer certificate code: 'P1011', driverAdapterError: TlsConnectionError Cause: PR #2772 set `ssl.ca = RDS_CA_BUNDLE` in the prisma adapter, which *replaces* Node's trust store rather than augmenting it. Our bundle only contains the 108 RDS-specific regional self-signed CAs — it does NOT contain Amazon Root CA 1, which is where AWS RDS Proxy chains terminate (and which lives in Node's default Mozilla bundle). So the chain failed to validate at runtime under the strict-TLS branch. Why apps/app and apps/portal didn't trip this in earlier checks: - The /auth route returned 200 because that codepath doesn't query the DB; it talks to apps/api over HTTP, and apps/api uses a different prisma client (Docker, NODE_EXTRA_CA_CERTS at OS level). - DB-touching SSR routes (e.g., /[orgId]/overview) are exactly what the reported staging failure exercises. Fix: drop the `ca:` field. Node's default trust store includes Amazon Root CA 1, which is sufficient for chain validation against RDS Proxy. Hostname check is still skipped (NLB topology — chain check still rejects forged or wrong-CA certs). PRISMA_ALLOW_INSECURE_TLS=1 remains the explicit insecure opt-out — the original Cubic finding fix is preserved. Files: - packages/db/src/ssl-config.ts: drop RDS_CA_BUNDLE import + usage - packages/db/src/client.test.ts: rewrite tests for new behavior (6 pass) - apps/{app,portal,framework-editor}/prisma/client.ts: drop the ca: branch - Delete: packages/db/{certs/rds-global-bundle.pem,src/rds-ca-bundle.ts, scripts/generate-ca-bundle-ts.mjs} and the inlined rds-ca-bundle.ts copies in apps/{app,portal,framework-editor}/prisma/ (~660KB removed) - packages/db: 2.2.0 → 2.3.0 (also drops `certs` from `files` array) - apps/api/prisma/client.ts: unchanged — Docker still uses NODE_EXTRA_CA_CERTS at OS level and that path is fine. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 852dca7 commit cd5046c

12 files changed

Lines changed: 55 additions & 2871 deletions

File tree

apps/app/prisma/client.ts

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { PrismaClient } from '@prisma/client';
22
import { PrismaPg } from '@prisma/adapter-pg';
33

4-
import { RDS_CA_BUNDLE } from './rds-ca-bundle';
5-
64
const globalForPrisma = global as unknown as { prisma?: PrismaClient };
75

86
const LOCAL_HOSTNAMES = new Set(['localhost', '127.0.0.1', '::1']);
@@ -28,21 +26,24 @@ function createPrismaClient(): PrismaClient {
2826
const isLocalhost = isLocalhostUrl(rawUrl);
2927
const allowInsecure = process.env.PRISMA_ALLOW_INSECURE_TLS === '1';
3028

31-
let ssl:
32-
| undefined
33-
| { ca: string; checkServerIdentity: () => undefined }
34-
| { rejectUnauthorized: false };
35-
if (isLocalhost) {
36-
ssl = undefined;
37-
} else if (allowInsecure) {
38-
ssl = { rejectUnauthorized: false };
39-
} else {
40-
// Verified TLS using the inlined AWS RDS CA bundle. Skip hostname check
41-
// because connections may traverse an AWS NLB whose hostname isn't in the
42-
// RDS Proxy cert's SAN list. The chain check still rejects forged or
43-
// wrong-CA certs.
44-
ssl = { ca: RDS_CA_BUNDLE, checkServerIdentity: () => undefined };
45-
}
29+
// Verified TLS via Node's default trust store, which includes Amazon Root
30+
// CA 1 — where AWS RDS Proxy chains terminate. Hostname check is skipped
31+
// because connections traverse an AWS NLB whose hostname isn't in the RDS
32+
// Proxy cert's SAN list; the chain check still rejects forged or wrong-CA
33+
// certs. PRISMA_ALLOW_INSECURE_TLS=1 is an explicit opt-out (no silent
34+
// fallback to unverified TLS).
35+
//
36+
// Previously this passed `ca: RDS_CA_BUNDLE` — but `ssl.ca` *replaces*
37+
// Node's trust store rather than augmenting it, and our bundle only
38+
// contains regional RDS CAs (not Amazon Root CA 1), so the RDS Proxy
39+
// chain failed to validate. Surfaced as P1011 TlsConnectionError /
40+
// "unable to get local issuer certificate" at runtime.
41+
const ssl: undefined | { checkServerIdentity: () => undefined } | { rejectUnauthorized: false } =
42+
isLocalhost
43+
? undefined
44+
: allowInsecure
45+
? { rejectUnauthorized: false }
46+
: { checkServerIdentity: () => undefined };
4647

4748
const url = ssl !== undefined ? stripSslMode(rawUrl) : rawUrl;
4849
const adapter = new PrismaPg({ connectionString: url, ssl });

apps/app/prisma/rds-ca-bundle.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

apps/framework-editor/prisma/client.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { PrismaClient } from '@prisma/client';
22
import { PrismaPg } from '@prisma/adapter-pg';
33

4-
import { RDS_CA_BUNDLE } from './rds-ca-bundle';
5-
64
const globalForPrisma = global as unknown as { prisma?: PrismaClient };
75

86
const LOCAL_HOSTNAMES = new Set(['localhost', '127.0.0.1', '::1']);
@@ -28,17 +26,15 @@ function createPrismaClient(): PrismaClient {
2826
const isLocalhost = isLocalhostUrl(rawUrl);
2927
const allowInsecure = process.env.PRISMA_ALLOW_INSECURE_TLS === '1';
3028

31-
let ssl:
32-
| undefined
33-
| { ca: string; checkServerIdentity: () => undefined }
34-
| { rejectUnauthorized: false };
35-
if (isLocalhost) {
36-
ssl = undefined;
37-
} else if (allowInsecure) {
38-
ssl = { rejectUnauthorized: false };
39-
} else {
40-
ssl = { ca: RDS_CA_BUNDLE, checkServerIdentity: () => undefined };
41-
}
29+
// See apps/app/prisma/client.ts for the rationale on dropping `ssl.ca`
30+
// (replaces rather than augments the trust store; broke RDS Proxy
31+
// chain validation).
32+
const ssl: undefined | { checkServerIdentity: () => undefined } | { rejectUnauthorized: false } =
33+
isLocalhost
34+
? undefined
35+
: allowInsecure
36+
? { rejectUnauthorized: false }
37+
: { checkServerIdentity: () => undefined };
4238

4339
const url = ssl !== undefined ? stripSslMode(rawUrl) : rawUrl;
4440
const adapter = new PrismaPg({ connectionString: url, ssl });

apps/framework-editor/prisma/rds-ca-bundle.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

apps/portal/prisma/client.ts

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { PrismaClient } from '../src/generated/prisma/client';
22
import { PrismaPg } from '@prisma/adapter-pg';
33

4-
import { RDS_CA_BUNDLE } from './rds-ca-bundle';
5-
64
const globalForPrisma = global as unknown as { prisma?: PrismaClient };
75

86
const LOCAL_HOSTNAMES = new Set(['localhost', '127.0.0.1', '::1']);
@@ -28,17 +26,15 @@ function createPrismaClient(): PrismaClient {
2826
const isLocalhost = isLocalhostUrl(rawUrl);
2927
const allowInsecure = process.env.PRISMA_ALLOW_INSECURE_TLS === '1';
3028

31-
let ssl:
32-
| undefined
33-
| { ca: string; checkServerIdentity: () => undefined }
34-
| { rejectUnauthorized: false };
35-
if (isLocalhost) {
36-
ssl = undefined;
37-
} else if (allowInsecure) {
38-
ssl = { rejectUnauthorized: false };
39-
} else {
40-
ssl = { ca: RDS_CA_BUNDLE, checkServerIdentity: () => undefined };
41-
}
29+
// See apps/app/prisma/client.ts for the rationale on dropping `ssl.ca`
30+
// (replaces rather than augments the trust store; broke RDS Proxy
31+
// chain validation).
32+
const ssl: undefined | { checkServerIdentity: () => undefined } | { rejectUnauthorized: false } =
33+
isLocalhost
34+
? undefined
35+
: allowInsecure
36+
? { rejectUnauthorized: false }
37+
: { checkServerIdentity: () => undefined };
4238

4339
const url = ssl !== undefined ? stripSslMode(rawUrl) : rawUrl;
4440
const adapter = new PrismaPg({ connectionString: url, ssl });

apps/portal/prisma/rds-ca-bundle.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.

0 commit comments

Comments
 (0)