首页 > 解决方案 > 如何正确配置 node.js 以使用自签名根证书?

问题描述

因此,在绝望的道路上,我想知道是否有人在某个地方可以帮助我配置 nodejs 以接受根 CA 自签名。我需要它来通过带有 TLS 的节点获取访问开发中的自定义 API。

环境

我正在处理的 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.pemen 变量,但它并没有改变任何东西,而且我找不到任何信息来了解发生了什么。

在我的 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
];

没有任何运气。

我究竟做错了什么 ?

标签: node.jssslssl-certificateself-signed-certificate

解决方案


我弄清楚发生了什么事。这是两个问题的结合。

首先,我使用相同的 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不做它的工作的环境变量在做什么。


推荐阅读