IP hosts are not validated correctly against certificate altnames
For example, connecting to IP 1.2.3.4 yields the following error:
Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match certificate's altnames: Host: localhost. is not in the cert's altnames: IP Address:1.2.3.4
at Object.checkServerIdentity (tls.js:250:17)
at TLSSocket.onConnectSecure (_tls_wrap.js:1098:27)
at TLSSocket.emit (events.js:198:13)
at TLSSocket._finishInit (_tls_wrap.js:666:8)
Since TLS support was added to pg, it has passed a socket to tls.connect, meaning the host should be passed separately (it isn't). It passed servername, which is not valid for IP addresses and was removed in #1890.
The reason that the error message above uses localhost can be found in _tls_wrap.js.
I found a previous issue (#2178) about this but it wasn't fixed. The correct fix is to always pass host. The documentation for options.socket says:
If this option is specified, path, host and port are ignored, except for certificate validation.
I can submit a PR but I will need help if you'd like a test for this.
Related to https://github.com/brianc/node-postgres-docs/issues/79
We encountered the same issue when using sequelize to connect to PG database. It's blocking us from moving forward with changing DB connection architecture.
@RazerM @charmander Do you know when it will be closed and if it will be released soon? Do you need any help to finalize it?
The TLS issue can be demonstrated with https://1.1.1.1:
const net = require('net');
const tls = require('tls');
function connect(port, host) {
const stream = new net.Socket();
stream.connect(port, host);
const options = {
socket: stream,
// host,
};
tls.connect(options);
}
connect(443, '1.1.1.1');
Error [ERR_TLS_CERT_ALTNAME_INVALID]: Hostname/IP does not match certificate's altnames: Host: localhost. is not in the cert's altnames: DNS:cloudflare-dns.com, DNS:*.cloudflare-dns.com, DNS:one.one.one.one, IP Address:1.1.1.1, IP Address:1.0.0.1, IP Address:162.159.36.1, IP Address:162.159.46.1, IP Address:2606:4700:4700:0:0:0:0:1111, IP Address:2606:4700:4700:0:0:0:0:1001, IP Address:2606:4700:4700:0:0:0:0:64, IP Address:2606:4700:4700:0:0:0:0:6400
Uncommenting the host variable fixes it.
Was pulling my hair out about this issue. Am putting a solution here since search engines seem to find it.
Not sure if this is the 100% best fix (but much better than rejectUnauthorized=false).
My Error: 'Hostname/IP does not match certificate's altnames: Host: localhost. is not in the cert's altnames: DNS:[address]a.us-central1.sql.goog'
Fix:
7 ssl: {
8 rejectUnauthorized: process.env.RUNTIME_ENV === "production", <- Keep security in prod!
9 ca: process.env.POSTGRES_RO_SERVER_CA, <- ca cert string
10 key: process.env.POSTGRES_RO_CLIENT_KEY, <- client key string
11 cert: process.env.POSTGRES_RO_CLIENT_CERT, <- client cert string
12 servername: process.env.POSTGRES_RO_SERVER_NAME, <- DNS string value for db.
13 },
Adding the expected DNS value in the error (I thought it would be in the cert too but I can't see it with openssl) as the servername makes the certs not try to verify against localhost.
servername did the trick