I'm not sure if this is possible, but I don't see why it wouldn't be, so I'm a little stumped. I've been trying to connect to a remote SQL DB hosted on google cloud through a locally running instance of my Node application, but it keeps failing with the given setup for my DB:
//dbConnectorPlugin.ts
...
import typeormConfig from '../../ormconfig';
declare module 'fastify' {
interface FastifyInstance {
psqlDB: {
messages: Repository<messages>;
users: Repository<users>;
};
}
}
async function dbConnector(fastify: FastifyInstance) {
try {
const AppDataSource = await new DataSource(typeormConfig).initialize();
fastify.decorate('psqlDB', {
messages: AppDataSource.getRepository(messages),
users: AppDataSource.getRepository(users),
});
} catch (e) {
console.error(`Something went dreadfully wrong: ${e}`);
}
}
export default fp(dbConnector);
It throws the error:
Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match certificate's altnames: Host: localhost. is not cert's CN: <google-cloud-db-project-connection-name>
Where the typeOrmConfig variable in the dbConnector file holds the content:
//ormconfig.ts
export default {
type: 'postgres',
port: 5432,
host: process.env.DB_HOST,
username: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME,
logging: true,
synchronize: false,
ssl: { ...getSSLConfig() },
entities: ['dist/src/modules/**/entity.js'],
migrations: ['dist/src/migration/**/*.ts'],
subscribers: ['src/subscriber/**/*.ts'],
} as DataSourceOptions;
function getSSLConfig() {
if (process.env.SSL_CA && process.env.SSL_CERT && process.env.SSL_KEY) {
return {
sslmode: 'verify-full',
ca: process.env.SSL_CA.replace(/\\n/g, '\n'),
cert: process.env.SSL_CERT.replace(/\\n/g, '\n'),
key: process.env.SSL_KEY.replace(/\\n/g, '\n'),
};
}
return {};
}
Where I'm currently storing the SSL details I got from GCloud in my .env file for the time being.
And just for further context, a reduced version of my index file where I register my DB plugin is as follows:
//index.ts
import dbConnector from './plugins/PSQLDbConnector';
const server: FastifyInstance = fastify();
const port: number = parseInt(`${process.env.PORT}`, 10) || 8080;
server.register(dbConnector);
server.listen({ port: port }, (err, address) => {
if (err) {
console.error(err);
process.exit(1);
}
console.log(`Server listening at ${address}`);
});
I've read through various other postings but can't find any solution that applies to my issue here. Is there something that I should be doing revolving around TypeORM to alter the Host? Or could it be something related to Node itself? Just to try and deduce the issue, I've added 0.0.0.0/0 to my authorized networks on the google cloud side, but that's also done nothing. What am I missing?
This appears to be an open issue in pg. A value for the host
is not passed through to the node TLS connect options when using an IP. If you can connect with a resolvable host name that appears on the cert, then use that host name.
Otherwise, rather than changing the sslmode
to verify-ca
or lower, you may be able to implement a custom checkServerIdentity
as suggested in this comment. It's probably safer to rely on the original checkServerIdentity
rather than rolling your own so this is just a wrapper to inject the postgres CN/hostname from your config.
import tls from 'node:tls'
function getSSLConfig() {
if (process.env.SSL_CA && process.env.SSL_CERT && process.env.SSL_KEY) {
const pg_cn = process.env.DB_CN
return {
checkServerIdentity: (nohost, cert) => {
return tls.checkServerIdentity(pg_cn, cert)
},
sslmode: 'verify-full',
ca: process.env.SSL_CA.replace(/\\n/g, '\n'),
cert: process.env.SSL_CERT.replace(/\\n/g, '\n'),
key: process.env.SSL_KEY.replace(/\\n/g, '\n'),
};
Setting a fixed host name here could have cause issues, or worst case verify an incorrect host, if PG was to reuse this helper for another connection to another host.
The original cert validation error message including the name localhost
threw me, but that is a node default when no name is passed through to verify. That seems like a terrible default!