使用Express的多个证书/虚拟主机

时间:2016-04-06 21:09:06

标签: node.js express https virtualhost

我在Cody-CMS中使用Express + vhost在一段时间内使用了nodejs和多个主机。现在我想要包括虚拟https服务器。

SNICallback被调用,但它结束了......我的Express应用程序“exp”从未被调用(即使我在createServer中用一个简单的函数替换它 - 在评论中)。我收到“请求:site1.com”(或site2.com),但没有任何内容返回给浏览器。

对于http服务器,它的工作非常完美。

欢迎任何帮助。

"use strict";

var express = require("express");
var vhost = require("vhost");
var fs = require("fs");


var app1 = express();
app1.all("/", function(req, res) {
  res.send('Hello World, site #1');
});

var app2 = express();
app2.all("/", function(req, res) {
  res.send('Hello World, site #2');
});


//////////////////
// certificates //
//////////////////
var crypto = require('crypto');

const site1 = {
  app: app1,
  context: crypto.createCredentials({
    key: fs.readFileSync('ws.key').toString(),
    cert: fs.readFileSync('ws.crt').toString()
  }).context

};
const site2 = {
  app: app2,
  context: crypto.createCredentials({
    key: fs.readFileSync('ws2.key').toString(),
    cert: fs.readFileSync('ws2.crt').toString()
  }).context
};

var sites = {
  "www.site1.com": site1,
  "site1.com": site1,

  "www.site2.com": site2,
  "site2.com": site2
};

// put (www.)site1/2.com in /etc/hosts to 127.0.0.1



//////////
// http //
//////////

var exp = express();
for (let s in sites) {
  console.log("http -> " + s);
  exp.use(vhost(s, sites[s].app));
}

exp.listen(80, function () {
   console.log("Listening https on port: 80")
});


///////////
// https //
///////////

var secureOpts = {
  SNICallback: function (domain) {
    console.log('request for: ', domain);
    return sites[domain].context;
  },
  key: fs.readFileSync('ws.key').toString(),
  cert: fs.readFileSync('ws.crt').toString()
};


var https = require('https');
var httpsServer = https.createServer(secureOpts, exp);
// var httpsServer = https.createServer(secureOpts, function(req, resp) { resp.send("hello"); });

httpsServer.listen(443, function () {
   console.log("Listening https on port: 443")
});

3 个答案:

答案 0 :(得分:2)

SNICallback有第二个参数:cbcb的签名为(error, context)。所以你的secureOpts应该是这样的:

var secureOpts = {
  SNICallback: function(domain, cb) {
    console.log('request for: ', domain);
    cb(null, sites[domain].context);
  },
  key: fs.readFileSync('ws.key').toString(),
  cert: fs.readFileSync('ws.crt').toString()
};

答案 1 :(得分:1)

在mscdex的帮助下,我让这个例子有效。 http://coppieters.blogspot.be/2016/04/nodejs-virtual-hosts-websitesapps-on.html

答案 2 :(得分:0)

您还可以将tls.createSecureContext与crypto.createCreadentials一起使用

我的例子在这里:

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. 打开浏览器
相关问题