多个 certificates/vhosts 与 Express
multiple certificates/vhosts with Express
我在 Cody-CMS 中使用 Express + vhost 在多个主机上使用 nodejs 有一段时间了。现在我还想包括虚拟 https 服务器。
SNICallback 被调用,但它到此为止...我的 Express 应用程序 "exp" 从未被调用(即使我在 createServer 中用一个简单的函数替换它也不会——在注释中)。我得到 "request for: 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")
});
SNICallback
还有第二个参数:cb
。 cb
的签名为 (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()
};
在 mscdex 的帮助下,我使示例运行起来。
http://coppieters.blogspot.be/2016/04/nodejs-virtual-hosts-websitesapps-on.html
你也可以使用 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")
});
如果你想测试一下:
编辑 /etc/hosts 并添加记录 127.0.0.1 example.com
打开浏览器 url https://example.com:443
我在 Cody-CMS 中使用 Express + vhost 在多个主机上使用 nodejs 有一段时间了。现在我还想包括虚拟 https 服务器。
SNICallback 被调用,但它到此为止...我的 Express 应用程序 "exp" 从未被调用(即使我在 createServer 中用一个简单的函数替换它也不会——在注释中)。我得到 "request for: 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")
});
SNICallback
还有第二个参数:cb
。 cb
的签名为 (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()
};
在 mscdex 的帮助下,我使示例运行起来。 http://coppieters.blogspot.be/2016/04/nodejs-virtual-hosts-websitesapps-on.html
你也可以使用 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")
});
如果你想测试一下:
编辑 /etc/hosts 并添加记录
127.0.0.1 example.com
打开浏览器 url
https://example.com:443