如何正确配置 node.js 以使用自签名根证书?
How to properly configure node.js to use Self Signed root certificates?
所以,在绝望的道路上,我想知道是否有人在某个地方可以帮助我配置 nodejs 以接受根 CA 自签名。我需要它才能通过带 TLS 的节点获取访问开发中的自定义 API。
环境
- OS : Ubuntu 20.04 作为虚拟机中的访客。 Windows 10 个主机。
- Node.js v15.12.0
- Apache2.4服务器
我正在处理的 API 是一个 PHP 脚本,它允许我的 nodejs 后端查询一些数据。
自签名根证书和 API 证书是用 openssl 生成的,非常好,因为我可以使用 HTTPS 从浏览器查询 API 没有任何问题。
错误
当尝试从 nodejs 后端查询 API 时,出现此错误:
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)"
尝试与失败
首先,我尝试使用 dpkg-reconfigure ca-certificates 在 ubuntu 上安装证书,但后来我发现 nodejs 使用硬编码列表。
所以,因为我不想使用 env
变量 NODE_TLS_REJECT_UNAUTHORIZED=0
为了安全起见,我尝试使用 NODE_EXTRA_CA_CERTS=pathToMycert.pem
en 变量,但它没有改变任何东西我找不到任何信息来了解发生了什么。
在我的 nodejs 后端,如果我执行 console.log(process.env.NODE_EXTRA_CA_CERTS)
,它会打印正确的路径。
我试图通过此检查将我的 CA 与 tls.rootCertificates 相匹配:
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...`);
它打印 'CA is not in rootCertificates list'。不足为奇。
因此,我尝试对 tls secureContext 进行 monkeypatch 以包含我的证书:
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;
};
无效。
我试过(在这个问题之后:https://github.com/nodejs/node/issues/27079)这样做:
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
];
运气不好。
我做错了什么?
我明白是怎么回事了。这是两个问题的结合。
首先,我使用相同的 CN 生成了我的 CA 证书和其他自签名证书 。它适用于所有浏览器和网络服务器,但不适用于节点。对于节点,请确保您的所有 CN 都具有 不同的名称(如 this answer 中所述)。
第二个问题是环境变量 NODE_EXTRA_CA_CERTS
在我的环境中出于某种原因无法正常工作。尝试在我尝试时尝试打补丁但很丑陋,因为 addCACert
不是 public nodejs API 的一部分。它不应该被使用。
因为我使用依赖于 https
包的 fetch
API,所以我在后端 nodejs 应用程序的顶部创建了一个我需要的小模块:
if(!process.env.NODE_EXTRA_CA_CERTS) return;
const https = require('https');
const tls = require('tls');
const fs = require('fs');
const list = (process.env.NODE_EXTRA_CA_CERTS || '').split(',');
const additionalCerts = list.map(extraCert => fs.readFileSync(extraCert, 'utf8'));
https.globalAgent.options.ca = [
...tls.rootCertificates,
...additionalCerts
];
这样,所有使用 https
且未重新定义 ca
选项的请求将从 globalAgent
读取 ca 列表,而您不必污染您的代码库使用 ca 特定代码。就我而言,我不希望我的开发环境生成我必须在生产中删除的代码。
所以,现在它对我有用了,即使我不知道 NODE_EXTRA_CA_CERTS
环境变量是怎么回事,它没有发挥作用。
所以,在绝望的道路上,我想知道是否有人在某个地方可以帮助我配置 nodejs 以接受根 CA 自签名。我需要它才能通过带 TLS 的节点获取访问开发中的自定义 API。
环境
- OS : Ubuntu 20.04 作为虚拟机中的访客。 Windows 10 个主机。
- Node.js v15.12.0
- Apache2.4服务器
我正在处理的 API 是一个 PHP 脚本,它允许我的 nodejs 后端查询一些数据。
自签名根证书和 API 证书是用 openssl 生成的,非常好,因为我可以使用 HTTPS 从浏览器查询 API 没有任何问题。
错误
当尝试从 nodejs 后端查询 API 时,出现此错误:
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)"
尝试与失败
首先,我尝试使用 dpkg-reconfigure ca-certificates 在 ubuntu 上安装证书,但后来我发现 nodejs 使用硬编码列表。
所以,因为我不想使用 env
变量 NODE_TLS_REJECT_UNAUTHORIZED=0
为了安全起见,我尝试使用 NODE_EXTRA_CA_CERTS=pathToMycert.pem
en 变量,但它没有改变任何东西我找不到任何信息来了解发生了什么。
在我的 nodejs 后端,如果我执行 console.log(process.env.NODE_EXTRA_CA_CERTS)
,它会打印正确的路径。
我试图通过此检查将我的 CA 与 tls.rootCertificates 相匹配:
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...`);
它打印 'CA is not in rootCertificates list'。不足为奇。
因此,我尝试对 tls secureContext 进行 monkeypatch 以包含我的证书:
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;
};
无效。
我试过(在这个问题之后:https://github.com/nodejs/node/issues/27079)这样做:
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
];
运气不好。
我做错了什么?
我明白是怎么回事了。这是两个问题的结合。
首先,我使用相同的 CN 生成了我的 CA 证书和其他自签名证书 。它适用于所有浏览器和网络服务器,但不适用于节点。对于节点,请确保您的所有 CN 都具有 不同的名称(如 this answer 中所述)。
第二个问题是环境变量 NODE_EXTRA_CA_CERTS
在我的环境中出于某种原因无法正常工作。尝试在我尝试时尝试打补丁但很丑陋,因为 addCACert
不是 public nodejs API 的一部分。它不应该被使用。
因为我使用依赖于 https
包的 fetch
API,所以我在后端 nodejs 应用程序的顶部创建了一个我需要的小模块:
if(!process.env.NODE_EXTRA_CA_CERTS) return;
const https = require('https');
const tls = require('tls');
const fs = require('fs');
const list = (process.env.NODE_EXTRA_CA_CERTS || '').split(',');
const additionalCerts = list.map(extraCert => fs.readFileSync(extraCert, 'utf8'));
https.globalAgent.options.ca = [
...tls.rootCertificates,
...additionalCerts
];
这样,所有使用 https
且未重新定义 ca
选项的请求将从 globalAgent
读取 ca 列表,而您不必污染您的代码库使用 ca 特定代码。就我而言,我不希望我的开发环境生成我必须在生产中删除的代码。
所以,现在它对我有用了,即使我不知道 NODE_EXTRA_CA_CERTS
环境变量是怎么回事,它没有发挥作用。