【问题标题】:How to properly configure node.js to use Self Signed root certificates?如何正确配置 node.js 以使用自签名根证书?
【发布时间】:2021-10-24 00:27:27
【问题描述】:

所以,在绝望的道路上,我想知道是否有人在某个地方可以帮助我配置 nodejs 以接受根 CA 自签名。我需要它才能通过带有 TLS 的 node-fetch 访问开发中的自定义 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 后端,如果我执行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
];

没有运气。

我做错了什么?

【问题讨论】:

  • (0) 你说得对,系统(ca-certificates)存储无关紧要(1)我不做奇怪的专业,我的 Ubuntu 20.04(带有标准 repos)只能找到 10.19,但是在 Windows NODE_EXTRA_CA_CERTS 上使用 14.15.5 进行测试对我有用,假设文件是​​好的,如果由 OpenSSL 创建并且没有修改,它应该是。你能检查openssl x509 -in file 显示的证书没有错误吗? (2) tls.rootCertificates 只给你一个内置列表的副本,而不是包含添加的实际 X509_STORE,并且修补它根本没有用处。 ...
  • ... (3) 阅读源代码我认为addCACert应该 工作,虽然你不需要 match+forEach (尽管它实际接受的单数名称一个序列,但会默默地忽略无效输入)并且您不需要删除 CR(或“评论”信息)
  • 感谢您的回答。 openssl x509 -in file 打印的证书没有错误。我重新生成了所有证书并再次尝试。还是行不通。 2 -> 这就是我所怀疑的...... 3:我尝试添加证书而不计算/更改文件内容:不工作。我还尝试使用 ca 参数在获取级别添加 CA:总是相同的错误。我试试最新版本的node看看是不是v15.12.0的bug。

标签: node.js ssl ssl-certificate self-signed-certificate


【解决方案1】:

我知道发生了什么。这是两个问题的结合。

首先,我生成了我的 CA 证书和我的其他自签名证书具有相同的 CN。它适用于所有浏览器和网络服务器,但不适用于 node。对于节点,请确保您的所有 CN 都有不同的名称(如 this answer 中所述)。

第二个问题是环境变量NODE_EXTRA_CA_CERTS 在我的环境中由于某种原因无法正常工作。像我尝试的那样尝试修补补丁是可行的,但很难看,因为 addCACert 不是公共 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 环境变量不起作用。

【讨论】:

    猜你喜欢
    • 2022-06-17
    • 1970-01-01
    • 2014-11-20
    • 2013-11-09
    • 1970-01-01
    • 2014-04-05
    • 2020-12-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多