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?
CodePudding user response:
The error says that host
param provided in ormconfig.ts
fetched from the env variable process.env.DB_HOST
doesn't match the CN against which the certificate is issued.
Check the domain name of the SSL cert either in the GCloud or if you have cert file with you, you can get the same using openssl
(if you have it installed in your system).
openssl x509 -noout -subject -in server.pem
More examples here.
Make sure that the CN that you get from above is pointing to the correct IP address of the database and in this case use this endpoint to connect to the database setting it in process.env.DB_HOST
.
If the CN from the cert comes out to be a malformed domain like locahost
, you need to generate a fresh certificate in the GCloud console. I'm not a GCloud expert but there should be an option to tell which hostname you want to generate the SSL cert for. You can specify the public address that you have or the FQDN url pointing to that ip address.
CodePudding user response:
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 real thing rather than rolling your own:
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'),
};
I'm not sure if setting a fixed host name here has unintended consequences as this config should be specific to this TLS connection that pg opens, but it could possibly be verifying the wrong host name if pg could change the host it connects to underneath.
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!