Check private-network host before the CA cert when choosing TLS
CI / build-and-test (push) Canceled after 0s
CI / build-and-test (push) Canceled after 0s
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) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
605c0e0052
commit
319d94b03c
@@ -56,6 +56,7 @@ export function getPool(): pg.Pool {
|
|||||||
// Strip sslmode from the URL so our explicit `ssl` option fully controls TLS behavior.
|
// 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.
|
// Without this, pg merges URL-derived settings which can conflict with the options below.
|
||||||
let ssl: pg.PoolConfig['ssl'];
|
let ssl: pg.PoolConfig['ssl'];
|
||||||
|
const wantsTls = process.env.DATABASE_SSL === 'require' || process.env.DATABASE_SSL === 'verify';
|
||||||
if (process.env.DATABASE_SSL === 'disable') {
|
if (process.env.DATABASE_SSL === 'disable') {
|
||||||
// Explicit opt-out for databases that don't speak TLS at all (e.g. a disposable Docker
|
// 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
|
// 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;
|
ssl = false;
|
||||||
} else if (ca) {
|
} else if (isPrivateHost(connectionString) && !wantsTls) {
|
||||||
// Verified TLS against the managed-DB CA — the correct posture everywhere.
|
// Database on a private network (app + Postgres on the same Docker/Swarm overlay, or a local
|
||||||
ssl = { ca, rejectUnauthorized: true };
|
// container). The connection never leaves that network, so plaintext is fine here. This is
|
||||||
} else if (isProd && isPrivateHost(connectionString)) {
|
// checked BEFORE the CA branch on purpose: a leftover DATABASE_CA_CERT_PATH from a previous
|
||||||
// Production database on a private network (app + Postgres on the same Docker/Swarm overlay).
|
// managed-database provider must not force TLS onto a server that does not speak it. Set
|
||||||
// The connection never leaves that network, so plaintext is acceptable — and the managed-DB CA
|
// DATABASE_SSL=require (or =verify) to opt a private host back into TLS.
|
||||||
// that verified TLS requires does not exist for a self-hosted container.
|
|
||||||
ssl = false;
|
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) {
|
} else if (isProd) {
|
||||||
// Public database host in production: never connect over unverified TLS. Fail fast so a missing
|
// 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.
|
// CA cert is a loud deploy error instead of a silent man-in-the-middle exposure.
|
||||||
|
|||||||
Reference in New Issue
Block a user