From 319d94b03cdc9938e2a4f795238e2e8dfbd2c65c Mon Sep 17 00:00:00 2001 From: Leon Serfaty <80597822+silkoserfo@users.noreply.github.com> Date: Wed, 26 Aug 2026 11:31:25 -0400 Subject: [PATCH] Check private-network host before the CA cert when choosing TLS A leftover DATABASE_CA_CERT_PATH (the DigitalOcean CA, still set in the production env and still committed at certs/ca-certificate.crt) made the `ca` branch win before the private-host check, so the app forced verified TLS onto the internal Postgres container and failed with "The server does not support SSL connections". Move the private-host branch ahead of the CA branch: a Docker service name, localhost, or an RFC1918 address connects in plaintext regardless of a stale CA path. Public hosts are unaffected and still require a CA in production. Set DATABASE_SSL=require (or =verify) to opt a private host back into TLS. Co-Authored-By: Claude Opus 5 (1M context) --- packages/db/src/index.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts index 4264e64..5c0dbbe 100644 --- a/packages/db/src/index.ts +++ b/packages/db/src/index.ts @@ -56,6 +56,7 @@ export function getPool(): pg.Pool { // Strip sslmode from the URL so our explicit `ssl` option fully controls TLS behavior. // Without this, pg merges URL-derived settings which can conflict with the options below. let ssl: pg.PoolConfig['ssl']; + const wantsTls = process.env.DATABASE_SSL === 'require' || process.env.DATABASE_SSL === 'verify'; if (process.env.DATABASE_SSL === 'disable') { // Explicit opt-out for databases that don't speak TLS at all (e.g. a disposable Docker // Postgres). In production this is honored ONLY for a private-network host; pointing it at @@ -66,14 +67,16 @@ export function getPool(): pg.Pool { ); } ssl = false; - } else if (ca) { - // Verified TLS against the managed-DB CA — the correct posture everywhere. - ssl = { ca, rejectUnauthorized: true }; - } else if (isProd && isPrivateHost(connectionString)) { - // Production database on a private network (app + Postgres on the same Docker/Swarm overlay). - // The connection never leaves that network, so plaintext is acceptable — and the managed-DB CA - // that verified TLS requires does not exist for a self-hosted container. + } else if (isPrivateHost(connectionString) && !wantsTls) { + // Database on a private network (app + Postgres on the same Docker/Swarm overlay, or a local + // container). The connection never leaves that network, so plaintext is fine here. This is + // checked BEFORE the CA branch on purpose: a leftover DATABASE_CA_CERT_PATH from a previous + // managed-database provider must not force TLS onto a server that does not speak it. Set + // DATABASE_SSL=require (or =verify) to opt a private host back into TLS. ssl = false; + } else if (ca) { + // Verified TLS against the managed-DB CA — the correct posture for any public host. + ssl = { ca, rejectUnauthorized: true }; } else if (isProd) { // Public database host in production: never connect over unverified TLS. Fail fast so a missing // CA cert is a loud deploy error instead of a silent man-in-the-middle exposure.