node.js - 如何正确配置 node.js 以使用自签名根证书?
问题描述
因此,在绝望的道路上,我想知道是否有人在某个地方可以帮助我配置 nodejs 以接受根 CA 自签名。我需要它来通过带有 TLS 的节点获取访问开发中的自定义 API。
环境
- 操作系统:Ubuntu 20.04 作为虚拟机中的来宾。Windows 10 主机。
- Nodejs 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 后端,如果我执行 a 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 不在 rootCertificates 列表中”。并不意外。
因此,我尝试对 tls secureContext 进行猴子补丁以包含我的证书:
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 证书和其他自签名证书。它适用于所有浏览器和网络服务器,但不适用于 node。对于节点,请确保您的所有 CN 具有不同的名称(如本答案中所述)。
第二个问题是 env varNODE_EXTRA_CA_CERTS
在我的环境中由于某种原因无法正常工作。像我尝试的那样尝试修补补丁是可行的,但很丑陋,因为addCACert
它不是公共 nodejs API 的一部分。它不应该被使用。</p>
由于我使用fetch
依赖于https
包的 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
选项的请求都将从中读取 ca 列表,globalAgent
并且您不必使用 ca 特定代码污染您的代码库。就我而言,我不希望我的开发环境生成必须在生产中删除的代码。
所以,现在它对我有用,即使我不知道NODE_EXTRA_CA_CERTS
不做它的工作的环境变量在做什么。
推荐阅读
- angularjs - AngularJS UI-Grid Tree View:子行如何具有与父行不同的列名?
- python - Python - 're.sub'ed 字符串在放入 sqlalchemy db.execute 命令时仍然包含特殊字符
- python - 您能否将 MYSQL 数据库链接到预先存在的 Python 代码而无需使用 Python 迭代器?
- reportportal - 如何在报告门户中创建从多个项目中获取数据的仪表板
- java - Spring 无法在类路径中找到 *is* 的驱动程序类
- javascript - 为什么函数 A 的返回值在函数 B 中的调用方式不同?
- spring - 在 Spring Security Filter 中获取请求参数
- request - 已超过未经验证使用的每日限制。所有日历的工作鳍,除了一个
- java - 有没有办法在 Java 中使用单个 Object 来运行来自其他继承类的方法?
- r - 在数据框R中选择具有替换的子组