Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,28 +25,39 @@ jobs:
needs: lint
services:
postgres:
image: ghcr.io/railwayapp-templates/postgres-ssl
image: ghcr.io/railwayapp-templates/postgres-ssl:${{ matrix.postgres }}
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_HOST_AUTH_METHOD: 'md5'
POSTGRES_DB: ci_db_test
# PostgreSQL 18's official image defaults PGDATA to a versioned
# subdirectory (/var/lib/postgresql/18/docker), but the
# railwayapp-templates/postgres-ssl entrypoint requires PGDATA to
# start with /var/lib/postgresql/data so we pin it explicitly. This is
# also the default for the older images, so it is a no-op there.
PGDATA: /var/lib/postgresql/data
ports:
- 5432:5432
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5
strategy:
fail-fast: false
matrix:
node:
- '16'
- '18'
- '20'
- '22'
- '24'
- '26'
os:
- ubuntu-latest
name: Node.js ${{ matrix.node }}
include:
# Historical Node.js versions tested against a single PostgreSQL version
- { node: '16', postgres: &stable_postgres '18' }
- { node: '18', postgres: *stable_postgres }
- { node: '20', postgres: *stable_postgres }
- { node: '22', postgres: *stable_postgres }
- { node: '24', postgres: *stable_postgres }
# Latest Node.js version tested against multiple PostgreSQL versions
- { node: &latest_node '26', postgres: '13' }
- { node: *latest_node, postgres: '14' }
- { node: *latest_node, postgres: '15' }
- { node: *latest_node, postgres: '16' }
- { node: *latest_node, postgres: '17' }
- { node: *latest_node, postgres: '18' }
name: Node.js ${{ matrix.node }} x PostgreSQL ${{ matrix.postgres }}
runs-on: ubuntu-latest
env:
PGUSER: postgres
Expand Down
55 changes: 55 additions & 0 deletions packages/pg/test/integration/client/ssl-tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,58 @@ suite.test('can connect with ssl', function (done) {
})
)
})

async function getServerVersionNum() {
const client = new helper.pg.Client(helper.config)
await client.connect()
try {
const {
rows: [row],
} = await client.query('SHOW server_version_num')
return parseInt(row.server_version_num, 10)
} finally {
await client.end()
}
}

// The native client forwards sslnegotiation=direct to libpq, whose support
// for direct SSL depends on the linked libpq version (17+) rather than on
// this library. It also does not expose the underlying TLS socket, so the
// direct-negotiation check below is impossible. So we only test the pure-JS client.
if (!helper.args.native) {
suite.test('can connect with direct SSL negotiation', async () => {
// Direct SSL negotiation (sslnegotiation=direct) is only supported by
// PostgreSQL 17 and newer servers. Probe the server version first and skip
// on older servers rather than failing the test.
const serverVersionNum = await getServerVersionNum()
if (serverVersionNum < 170000) {
console.log(`(skipped: direct SSL requires PostgreSQL 17+, server_version_num=${serverVersionNum}) `)
return
}

const config = {
...helper.config,
ssl: { rejectUnauthorized: false },
sslnegotiation: 'direct',
}
const client = new helper.pg.Client(config)
await client.connect()
const { rows } = await client.query('SELECT NOW()')
assert.strictEqual(rows.length, 1)

// Verify the connection actually used direct SSL negotiation rather than
// silently falling back to the traditional SSLRequest handshake. pg only
// sends the 'postgresql' ALPN protocol on a direct SSL handshake (see
// Connection#upgradeToSSL), and a PostgreSQL 17+ server echoes it back, so
// its presence on the negotiated TLS socket confirms direct negotiation.
const tlsSocket = client.connection.stream
assert.ok(tlsSocket.encrypted, 'expected the connection to be upgraded to a TLS socket')
assert.strictEqual(
tlsSocket.alpnProtocol,
'postgresql',
'expected direct SSL negotiation to select the "postgresql" ALPN protocol'
)

await client.end()
})
}
Loading