106 lines
6.4 KiB
JavaScript
106 lines
6.4 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import {execFileSync} from 'node:child_process'
|
|
import {randomBytes, X509Certificate} from 'node:crypto'
|
|
import {mkdtemp, mkdir, cp, readFile, rm, chmod, access} from 'node:fs/promises'
|
|
import {createServer} from 'node:net'
|
|
import {tmpdir} from 'node:os'
|
|
import {dirname, join, resolve} from 'node:path'
|
|
import EmbeddedPostgres from 'embedded-postgres'
|
|
import pg from 'pg'
|
|
import {poolOptions, retainPublisherTlsPeer} from './writer-profile/operator-contract.mjs'
|
|
import {sha} from './writer-profile/operator-files.mjs'
|
|
|
|
const openssl = process.env.MMAPP_TLS_FIXTURE_OPENSSL || 'openssl'
|
|
const host = 'localhost'
|
|
const reservePort = async () => {
|
|
const listener = createServer()
|
|
await new Promise((ready, reject) => listener.once('error', reject).listen(0, '127.0.0.1', ready))
|
|
return listener
|
|
}
|
|
const releasePort = listener => new Promise((ready, reject) => listener.close(error => error ? reject(error) : ready()))
|
|
const sourcePort = await reservePort()
|
|
const clonePort = await reservePort()
|
|
const port = sourcePort.address().port, clonePortNumber = clonePort.address().port
|
|
await Promise.all([releasePort(sourcePort), releasePort(clonePort)])
|
|
const transport = {host, port, user: 'mcp_test', password: randomBytes(24).toString('hex'), database: 'postgres'}
|
|
const certFile = 'server.pem', keyFile = 'server-key.pem'
|
|
const root = await mkdtemp(join(tmpdir(), 'mm-writer-tls-owned-'))
|
|
const databaseDir = join(root, 'pg'), cloneDir = join(root, 'pg-clone')
|
|
const sourceTlsDir = join(root, 'source-tls'), cloneTlsDir = join(root, 'clone-tls')
|
|
const cluster = (databaseDir, port, tlsDir) => new EmbeddedPostgres({databaseDir, port, user: transport.user, password: transport.password,
|
|
persistent: true, createPostgresUser: false, postgresFlags: ['-h', '127.0.0.1', '-c', 'ssl=on', '-c', `ssl_cert_file=${join(tlsDir, certFile)}`, '-c', `ssl_key_file=${join(tlsDir, keyFile)}`],
|
|
onLog: () => {}, onError: () => {}})
|
|
const postgres = cluster(databaseDir, port, sourceTlsDir), clone = cluster(cloneDir, clonePortNumber, cloneTlsDir)
|
|
let sourceStarted = false, cloneStarted = false
|
|
const fingerprint = ca => sha(new X509Certificate(ca).publicKey.export({format:'der', type:'spki'}))
|
|
const issue = async tlsDir => {
|
|
await mkdir(tlsDir, {mode:0o700})
|
|
execFileSync(openssl, ['req', '-new', '-newkey', 'rsa:2048', '-x509', '-nodes', '-days', '1', '-subj', '/CN=localhost',
|
|
'-addext', 'subjectAltName=DNS:localhost', '-keyout', keyFile, '-out', certFile], {cwd: tlsDir, stdio: 'ignore', windowsHide: true})
|
|
await chmod(join(tlsDir, keyFile), 0o600)
|
|
return readFile(join(tlsDir, certFile))
|
|
}
|
|
const withClient = async (config, work) => {
|
|
const pool = new pg.Pool({connectionTimeoutMillis:5000, ...config})
|
|
let client
|
|
try { client = await pool.connect(); return await work(client) }
|
|
finally { client?.release(true); await pool.end() }
|
|
}
|
|
const query = "SELECT (pg_control_system()).system_identifier::text AS system, oid::text AS oid FROM pg_database WHERE datname=current_database()"
|
|
try {
|
|
await postgres.initialise()
|
|
const first = await issue(sourceTlsDir)
|
|
await postgres.start(); sourceStarted = true
|
|
const pin = {serverName:host, caSha256:sha(first), spkiSha256:fingerprint(first)}
|
|
const request = {tls:pin, roles:{publisher:{name:transport.user}}, source:{databaseName:transport.database}}
|
|
const baseline = await withClient(poolOptions(transport, request, first), async client => {
|
|
const retained = retainPublisherTlsPeer(client, pin)
|
|
retained()
|
|
const {rows:[source]} = await client.query(query)
|
|
const {rows:[status]} = await client.query('SELECT ssl FROM pg_stat_ssl WHERE pid=pg_backend_pid()')
|
|
assert.equal(status.ssl, true)
|
|
await client.query("CREATE TABLE owned_tls_fixture (note text PRIMARY KEY); INSERT INTO owned_tls_fixture VALUES ('original')")
|
|
const connection = client.connection, socket = connection.stream
|
|
connection.stream = undefined
|
|
try { assert.throws(retained, /retained TLS peer unavailable/) } finally { connection.stream = socket }
|
|
return source
|
|
})
|
|
await withClient({...transport, ssl:false}, async client => assert.throws(() => retainPublisherTlsPeer(client,pin), /retained TLS peer unavailable/))
|
|
await postgres.stop(); sourceStarted = false
|
|
await cp(databaseDir, cloneDir, {recursive:true, force:false, errorOnExist:true})
|
|
await assert.rejects(access(join(cloneDir, keyFile)), {code:'ENOENT'})
|
|
const second = await issue(cloneTlsDir)
|
|
assert.notEqual(fingerprint(second), pin.spkiSha256)
|
|
await postgres.start(); sourceStarted = true
|
|
await clone.start(); cloneStarted = true
|
|
const cloneTransport = {...transport, port:clonePortNumber}
|
|
const bothTrusted = Buffer.concat([first,second])
|
|
await assert.rejects(withClient(poolOptions(cloneTransport, {...request,tls:{...pin,caSha256:sha(bothTrusted)}}, bothTrusted),
|
|
() => assert.fail('Wrong physical cluster must not reach application SQL')), /TLS SPKI refused/)
|
|
await withClient({...cloneTransport, ssl:{ca:bothTrusted, servername:host, rejectUnauthorized:true}}, async client => {
|
|
assert.equal(client.connection.stream.authorized, true)
|
|
const {rows:[source]} = await client.query(query)
|
|
assert.deepEqual(source, baseline)
|
|
assert.throws(() => retainPublisherTlsPeer(client,pin), /TLS SPKI refused/)
|
|
retainPublisherTlsPeer(client,{...pin,spkiSha256:fingerprint(second)})()
|
|
assert.deepEqual((await client.query('SELECT note FROM owned_tls_fixture')).rows, [{note:'original'}])
|
|
await client.query("INSERT INTO owned_tls_fixture VALUES ('clone-only')")
|
|
})
|
|
await withClient(poolOptions(transport, request, first), async client => {
|
|
retainPublisherTlsPeer(client,pin)()
|
|
assert.deepEqual((await client.query('SELECT note FROM owned_tls_fixture')).rows, [{note:'original'}])
|
|
})
|
|
console.log('Genuine PostgreSQL TLS: original accepted; independently writable physical clone with identical DB coordinates and a different CA-valid TLS key refused')
|
|
} finally {
|
|
const stops = await Promise.allSettled([
|
|
cloneStarted ? clone.stop() : undefined,
|
|
sourceStarted ? postgres.stop() : undefined,
|
|
])
|
|
assert(stops.every(result => result.status === 'fulfilled'), 'Owned cluster stop unconfirmed')
|
|
assert.equal(dirname(resolve(root)), resolve(tmpdir()))
|
|
assert.match(root, /mm-writer-tls-owned-/)
|
|
await rm(root, {recursive:true, force:true, maxRetries:20, retryDelay:100})
|
|
await assert.rejects(access(root), {code:'ENOENT'})
|
|
console.log('Owned PostgreSQL and temporary TLS material removed')
|
|
}
|