使用 Express.js 的多个 SSL 证书和 HTTP/2

Posted

技术标签:

【中文标题】使用 Express.js 的多个 SSL 证书和 HTTP/2【英文标题】:Multiple SSL Certificates and HTTP/2 with Express.js 【发布时间】:2017-07-20 06:05:13 【问题描述】:

场景:

我有一个 express.js 服务器,它根据 req.headers.host 所说的用户来自哪里提供相同静态登录页面的变体 - 有点像 A/B 测试。

GET tulip.flower.com 服务于pages/flower.com/tulip.html

GET rose.flower.com 服务于pages/flower.com/rose.html

同时,这个IP还负责:

GET potato.vegetable.com服务pages/vegetable.com/potato.html

提供这些页面FAST很重要,因此它们经过预编译和优化in all sorts of ways。

服务器现在需要:

    *.vegetables.com*.fruits.com*.rocks.net 提供单独的证书 可选择不为*.flowers.com 提供证书 提供 HTTP2

问题在于 HTTP2 需要一个证书,而现在有多个证书在起作用。

it's possible 似乎在一个 Node.js(并且可能通过扩展名 Express.js)服务器上使用多个证书,但是否可以将其与 spdy 之类的模块结合使用,如果可以,如何?

与其破解节点,不如将整理http2和SSL的任务典当给nginx会不会更聪明?像 Imperva 或 Akamai 这样的缓存网络应该处理这个问题吗?

【问题讨论】:

【参考方案1】:

你也可以使用 tls.createSecureContext,Nginx 不是必需的。

这里是我的例子:

const https = require("https");
const tls = require("tls");

const certs = 
    "localhost": 
        key: "./certs/localhost.key",
        cert: "./certs/localhost.crt",
    ,
    "example.com": 
        key: "./certs/example.key",
        cert: "./certs/example.cert",
        ca: "./certs/example.ca",
    ,
 

function getSecureContexts(certs) 

    if (!certs || Object.keys(certs).length === 0) 
      throw new Error("Any certificate wasn't found.");
    

    const certsToReturn = ;

    for (const serverName of Object.keys(certs)) 
      const appCert = certs[serverName];

      certsToReturn[serverName] = tls.createSecureContext(
        key: fs.readFileSync(appCert.key),
        cert: fs.readFileSync(appCert.cert),
        // If the 'ca' option is not given, then node.js will use the default
        ca: appCert.ca ? sslCADecode(
          fs.readFileSync(appCert.ca, "utf8"),
        ) : null,
      );
    

    return certsToReturn;


// if CA contains more certificates it will be parsed to array
function sslCADecode(source) 

    if (!source || typeof (source) !== "string") 
        return [];
    

    return source.split(/-----END CERTIFICATE-----[\s\n]+-----BEGIN CERTIFICATE-----/)
        .map((value, index: number, array) => 
        if (index) 
            value = "-----BEGIN CERTIFICATE-----" + value;
        
        if (index !== array.length - 1) 
            value = value + "-----END CERTIFICATE-----";
        
        value = value.replace(/^\n+/, "").replace(/\n+$/, "");
        return value;
    );


const secureContexts = getSecureContexts(certs)

const options = 
    // A function that will be called if the client supports SNI TLS extension.
    SNICallback: (servername, cb) => 

        const ctx = secureContexts[servername];

        if (!ctx) 
            log.debug(`Not found SSL certificate for host: $servername`);
         else 
            log.debug(`SSL certificate has been found and assigned to $servername`);
        

        if (cb) 
            cb(null, ctx);
         else 
            return ctx;
        
    ,
;


var https = require('https');
var httpsServer = https.createServer(options, (req, res) =>  console.log(res, req));
httpsServer.listen(443, function () 
   console.log("Listening https on port: 443")
);

如果你想测试它:

    编辑 /etc/hosts 并添加记录 127.0.0.1 example.com

    用网址https://example.com:443打开浏览器

【讨论】:

我知道 Nginx 是执行此操作的首选方式,但我喜欢这个答案,因为它实际上回答了所提出的问题。此外,这个答案只是为我节省了今天需要完成的修补程序,并且我没有时间重新配置我的整个站点。谢谢!【参考方案2】:

Nginx 可以很好地处理 SSL 终止,这将减轻您的应用程序服务器的 ssl 处理能力。

如果您的 nginx 和应用程序服务器之间有一个安全的专用网络,我建议您通过 nginx 反向代理卸载 ssl。在这种实践中,nginx 将监听 ssl,(证书将在 nginx 服务器上管理)然后它将代理请求反向到非 ssl 上的应用程序服务器(因此应用程序服务器不需要证书,没有 ssl 配置和 ssl 进程负担)。

如果您的 nginx 和应用程序服务器之间没有安全的专用网络,您仍然可以通过将上游配置为 ssl 将 nginx 用作反向代理,但您将失去卸载的好处。

CDN 也可以做到这一点。它们基本上是反向代理 + 缓存,所以我看不出有问题。

Good read.

【讨论】:

所以你是说我应该从 node.js 服务器中完全删除 http2 并让其他人(nginx、cdn)处理这些事情? 这是可能的,您也可以在节点服务器上保留 http2,但在此处删除 ssl,并在 nginx 服务器上终止 ssl。更新了答案并阅读。【参考方案3】:

让我们用Greenlock Express v3 加密

我是 Greenlock Express 的作者,它是用于 Node.js、Express 等的 Let's Encrypt,而这个用例正是我所做的。

基本设置如下所示:

require("greenlock-express")
    .init(function getConfig() 
        return 
          package: require("./package.json")
          manager: 'greenlock-manager-fs',
          cluster: false,
          configFile: '~/.config/greenlock/manager.json'
        ;
    )
    .serve(httpsWorker);

function httpsWorker(server) 
    // Works with any Node app (Express, etc)
    var app = require("./my-express-app.js");

    // See, all normal stuff here
    app.get("/hello", function(req, res) 
        res.end("Hello, Encrypted World!");
    );

    // Serves on 80 and 443
    // Get's SSL certificates magically!
    server.serveApp(app);

它也适用于节点集群,因此您可以利用多个核心。

它使用SNICallback 动态添加证书。

网站管理

默认管理器插件使用文件系统上的文件,但有great documentation 说明如何构建自己的。

刚开始,基于文件的插件使用如下配置文件:

~/.config/greenlock/manager.json:


    "subscriberEmail": "letsencrypt-test@therootcompany.com",
    "agreeToTerms": true,
    "sites": [
        
            "subject": "example.com",
            "altnames": ["example.com", "www.example.com"]
        
    ]

非常可扩展

我无法在此处发布所有可能的选项,但它非常小且易于开始,并且很容易根据需要使用高级选项进行扩展。

【讨论】:

看起来不错,但是……缺少文档。更不用说使用自托管 git 上的代码而不是例如使用代码感到不安全。 Github,所以它可以消失:/我找不到任何关于为 v4 创建自定义管理器的文档。你能帮忙吗?

以上是关于使用 Express.js 的多个 SSL 证书和 HTTP/2的主要内容,如果未能解决你的问题,请参考以下文章

nginx配置多域名多个ssl证书

SSL证书可以给多个域名使用吗

SSL证书可以给多个域名使用吗

无法使用 Spring Boot 连接到具有多个 SSL 证书的多个 IBM MQ 通道

Service Fabric 多个 SSL 安全 WebAPI 和证书翻转

SSL证书是一个域名一个证书吗?多个域名能用一张SSL证书吗?