多个SSL证书和带有Express.js的HTTP / 2

时间:2017-03-01 03:46:43

标签: node.js express ssl nginx http2

场景:

我有一个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

服务器现在需要:

  1. *.vegetables.com*.fruits.com*.rocks.net
  2. 提供单独的证书
  3. 可选择不为*.flowers.com
  4. 提供证书
  5. 提供HTTP2
  6. 问题在于HTTP2强制要求证书,现在有多个证书可供使用。

    似乎it's possible在一个Node.js(可能是扩展名为Express.js)服务器上使用多个证书,但是可以将它与spdy之类的模块结合使用,如果那怎么样?

    而不是黑客入侵节点,将http2和SSL整理到nginx的任务更聪明吗?像Imperva或Akamai这样的缓存网络应该处理这个吗?

3 个答案:

答案 0 :(得分:1)

Nginx可以很好地处理SSL终止,这将从您的应用服务器卸载ssl处理能力。

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

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

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

Good read

答案 1 :(得分:0)

您还可以使用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")
});

如果要测试:

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

  2. 使用网址https://example.com:443

  3. 打开浏览器

答案 2 :(得分:0)

让我们使用Greenlock Express v3加密

我是Greenlock Express的作者,Greenlock Express是让我们为Node.js,Express等进行加密的工具,而这种用例正是我所为的。

基本设置如下:

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"]
        }
    ]
}

非常可扩展

我无法在此处发布所有可能的选项,但是起步非常小且简单,并且可以根据需要使用高级选项轻松扩展。