So, on the road of desperation, I would want to know if someone, somewhere, can help me to configure nodejs to accept a root CA self signed. I need it in order to access a custom API in development via node-fetch with TLS.
Envrionment
- OS : Ubuntu 20.04 as a guest in a VM. Windows 10 host.
- Nodejs v15.12.0
- Apache2.4 server
The API I'm working on is a PHP script that allow my nodejs backend to query some data.
The self signed root cert and API cert have been generated with openssl and are eprfectly fine, since I can query the API from the browser with HTTPS without any problem.
The error
When trying to query the API from the nodejs backend, I get this error :
FetchError: request to https://myapi.dev.local failed, reason: self signed certificate
at ClientRequest.<anonymous> (./node_modules/node-fetch/lib/index.js:1461:11)
at ClientRequest.emit (node:events:369:20)
at TLSSocket.socketErrorListener (node:_http_client:462:9)
at TLSSocket.emit (node:events:369:20)
at emitErrorNT (node:internal/streams/destroy:188:8)
at emitErrorCloseNT (node:internal/streams/destroy:153:3)
at processTicksAndRejections (node:internal/process/task_queues:81:21)"
Tries & fails
First, I tried to install the cert on ubuntu with dpkg-reconfigure ca-certificates, but then I figured that nodejs use a hard coded list.
So, since I do not want to use the env
variable NODE_TLS_REJECT_UNAUTHORIZED=0
for security sakes, I tried to use the NODE_EXTRA_CA_CERTS=pathToMycert.pem
en variable, but it doesn't change anything and I can't find any info to know what's going on.
In my nodejs backend, if I do a console.log(process.env.NODE_EXTRA_CA_CERTS)
, it prints the good path.
I tried to match my CA against tls.rootCertificates whith this check :
const tls = require('tls');
const fs = require('fs');
const ca = await fs.readFileSync(process.env.NODE_EXTRA_CA_CERTS, 'utf8');
console.log(ca); //successfully print the CA, so it exists.
const inList = tls.rootCertificates.some( cert =>{
console.log('testing ca : \n',cert);
return cert == ca;
});
console.log(`CA is ${ !inList ? 'not' : '' } in rootCertificates list...`);
It prints 'CA is not in rootCertificates list'. Not a surprise.
So, I tried to monkeypatch the tls secureContext to include my certificate :
const tls = require('tls');
const fs = require('fs');
const origCreateSecureContext = tls.createSecureContext;
tls.createSecureContext = options => {
const context = origCreateSecureContext(options);
const list = (process.env.NODE_EXTRA_CA_CERTS || '').split(',');
list.forEach(extraCert => {
const pem = fs.readFileSync(extraCert, { encoding : 'utf8' }).replace(/\r\n/g, "\n");
const certs = pem.match(/-----BEGIN CERTIFICATE-----\n[\s\S]+?\n-----END CERTIFICATE-----/g);
if(!certs) throw new Error(
`SelfSignedCertSupport : Invalid extra certificate ${extraCert}`
);
certs.forEach(cert => context.context.addCACert(cert.trim()));
});
return context;
};
Doesn't work.
And I tried (following this issue : https://github.com/nodejs/node/issues/27079) to do this :
const tls = require('tls');
const fs = require('fs');
const additionalCerts = [];
const list = (process.env.NODE_EXTRA_CA_CERTS || '').split(',');
list.forEach(extraCert => {
const pem = fs.readFileSync(extraCert, { encoding : 'utf8' }).replace(/\r\n/g, "\n");
const certs = pem.match(/-----BEGIN CERTIFICATE-----\n[\s\S]+?\n-----END CERTIFICATE-----/g);
if(!certs) throw new Error(
`SelfSignedCertSupport : Invalid extra certificate ${extraCert}`
);
additionalCerts.push(...certs);
});
tls.rootCertificates = [
...tls.rootCertificates,
...additionalCerts
];
Without any luck.
What am I doing wrong ?