example server: add proxy and wildcard support
This commit is contained in:
parent
4d400a9828
commit
ada2b9ccfe
13
config.js
13
config.js
|
@ -5,5 +5,16 @@ module.exports = {
|
|||
email: "jon.doe@example.com",
|
||||
configDir: path.join(__dirname, "acme"),
|
||||
srv: "/srv/www/",
|
||||
api: "/srv/api/"
|
||||
api: "/srv/api/",
|
||||
proxy: {
|
||||
"example.com": "http://localhost:4080",
|
||||
"*.example.com": "http://localhost:4080"
|
||||
},
|
||||
|
||||
// DNS-01 challenges only
|
||||
challenges: {
|
||||
"*.example.com": require("acme-dns-01-YOUR_DNS_HOST").create({
|
||||
token: "xxxx"
|
||||
})
|
||||
}
|
||||
};
|
||||
|
|
|
@ -4,16 +4,16 @@
|
|||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@coolaj86/urequest": {
|
||||
"version": "1.3.7",
|
||||
"resolved": "https://registry.npmjs.org/@coolaj86/urequest/-/urequest-1.3.7.tgz",
|
||||
"integrity": "sha512-PPrVYra9aWvZjSCKl/x1pJ9ZpXda1652oJrPBYy5rQumJJMkmTBN3ux+sK2xAUwVvv2wnewDlaQaHLxLwSHnIA=="
|
||||
},
|
||||
"@root/mkdirp": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@root/mkdirp/-/mkdirp-1.0.0.tgz",
|
||||
"integrity": "sha512-hxGAYUx5029VggfG+U9naAhQkoMSXtOeXtbql97m3Hi6/sQSRL/4khKZPyOF6w11glyCOU38WCNLu9nUcSjOfA=="
|
||||
},
|
||||
"@root/request": {
|
||||
"version": "1.3.11",
|
||||
"resolved": "https://registry.npmjs.org/@root/request/-/request-1.3.11.tgz",
|
||||
"integrity": "sha512-3a4Eeghcjsfe6zh7EJ+ni1l8OK9Fz2wL1OjP4UCa0YdvtH39kdXB9RGWuzyNv7dZi0+Ffkc83KfH0WbPMiuJFw=="
|
||||
},
|
||||
"accepts": {
|
||||
"version": "1.3.5",
|
||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
|
||||
|
@ -38,12 +38,12 @@
|
|||
"integrity": "sha512-Aa4bUpq6ftX1VODiShOetOY5U0tsXY5EV7+fQwme3Q8Y9rjYBArBXHgFCAVKtK1AF+Ev8pIuF6Z42hzMFa73/w=="
|
||||
},
|
||||
"acme-v2": {
|
||||
"version": "1.7.7",
|
||||
"resolved": "https://registry.npmjs.org/acme-v2/-/acme-v2-1.7.7.tgz",
|
||||
"integrity": "sha512-Pg0EQ45h8N2e4K2goYedutCgWxAmtcruwDHr6hgPBgAWEORVb5SQEdXjtEhCrn+APtr7MyFPryyzXpYpDD5ecA==",
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/acme-v2/-/acme-v2-1.8.2.tgz",
|
||||
"integrity": "sha512-uYGA+DuTnA44EsGXE413XnbTotGHCzkucXjMk23QRwGnaGlnr0lNBoYjByyeIVLSzj0W6Y9FqA9h+15+H+ltMw==",
|
||||
"requires": {
|
||||
"@coolaj86/urequest": "^1.3.6",
|
||||
"rsa-compat": "^2.0.6"
|
||||
"@root/request": "^1.3.11",
|
||||
"rsa-compat": "^2.0.8"
|
||||
}
|
||||
},
|
||||
"array-flatten": {
|
||||
|
@ -175,6 +175,12 @@
|
|||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
|
||||
"dev": true
|
||||
},
|
||||
"eventemitter3": {
|
||||
"version": "3.1.2",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
|
||||
"integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==",
|
||||
"dev": true
|
||||
},
|
||||
"express": {
|
||||
"version": "4.16.4",
|
||||
"resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
|
||||
|
@ -237,6 +243,32 @@
|
|||
"unpipe": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.7.0.tgz",
|
||||
"integrity": "sha512-m/pZQy4Gj287eNy94nivy5wchN3Kp+Q5WgUPNy5lJSZ3sgkVKSYV/ZChMAQVIgx1SqfZ2zBZtPA2YlXIWxxJOQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"debug": "^3.2.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
|
||||
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"forwarded": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||
|
@ -250,13 +282,13 @@
|
|||
"dev": true
|
||||
},
|
||||
"greenlock": {
|
||||
"version": "2.7.24",
|
||||
"resolved": "https://registry.npmjs.org/greenlock/-/greenlock-2.7.24.tgz",
|
||||
"integrity": "sha512-GQb2LMF6IiEzhp01F6eIN7HlPVlUWpWsBZZn7DOIo9upFAWhFpn2w1PStjGb17VmTkg+lgxzcajqcy6AJhCHUQ==",
|
||||
"version": "2.8.2",
|
||||
"resolved": "https://registry.npmjs.org/greenlock/-/greenlock-2.8.2.tgz",
|
||||
"integrity": "sha512-pCAYjgVova1ZoUHhuCfIw/3Rs5tE6DK1YF2LI7Cyh15QFBZJNU7pngMvDfeFft3It4WqnHezNgyDWAeV2pWFaw==",
|
||||
"requires": {
|
||||
"acme": "^1.3.0",
|
||||
"acme-dns-01-cli": "^3.0.0",
|
||||
"acme-v2": "^1.7.7",
|
||||
"acme-v2": "^1.8.1",
|
||||
"cert-info": "^1.5.1",
|
||||
"greenlock-store-fs": "^3.0.2",
|
||||
"keypairs": "^1.2.14",
|
||||
|
@ -287,6 +319,17 @@
|
|||
"statuses": ">= 1.4.0 < 2"
|
||||
}
|
||||
},
|
||||
"http-proxy": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/http-proxy/-/http-proxy-1.17.0.tgz",
|
||||
"integrity": "sha512-Taqn+3nNvYRfJ3bGvKfBSRwy1v6eePlm3oc/aWVxZp57DQr5Eq3xhKJi7Z4hZpS8PC3H4qI+Yly5EmFacGuA/g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"eventemitter3": "^3.0.0",
|
||||
"follow-redirects": "^1.0.0",
|
||||
"requires-port": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.4.23",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
|
||||
|
@ -467,6 +510,12 @@
|
|||
"escape-html": "^1.0.3"
|
||||
}
|
||||
},
|
||||
"requires-port": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
|
||||
"integrity": "sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8=",
|
||||
"dev": true
|
||||
},
|
||||
"rsa-compat": {
|
||||
"version": "2.0.8",
|
||||
"resolved": "https://registry.npmjs.org/rsa-compat/-/rsa-compat-2.0.8.tgz",
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
"example": "examples"
|
||||
},
|
||||
"dependencies": {
|
||||
"greenlock": "^2.7.24",
|
||||
"greenlock": "^2.8.2",
|
||||
"redirect-https": "^1.1.5"
|
||||
},
|
||||
"files": [
|
||||
|
@ -18,6 +18,7 @@
|
|||
"spdy": "^3.4.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"http-proxy": "^1.17.0",
|
||||
"express": "^4.16.3",
|
||||
"express-basic-auth": "^1.2.0",
|
||||
"finalhandler": "^1.1.1",
|
||||
|
|
124
server.js
124
server.js
|
@ -58,6 +58,31 @@ if (require.main === module) {
|
|||
});
|
||||
}
|
||||
|
||||
function matchConfig(thing, domain) {
|
||||
if (!thing) {
|
||||
return false;
|
||||
}
|
||||
if (thing[domain]) {
|
||||
return domain;
|
||||
}
|
||||
|
||||
var keys = Object.keys(thing);
|
||||
var result = null;
|
||||
keys.some(function(k) {
|
||||
if ("*" !== k[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
// "foo.whatever.com".endsWith("*.whatever.com".slice(1))
|
||||
if (domain.endsWith(k.slice(1).toLowerCase())) {
|
||||
result = k;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
function myApproveDomains(opts) {
|
||||
console.info("SNI:", opts.domain);
|
||||
// In this example the filesystem is our "database".
|
||||
|
@ -67,6 +92,52 @@ function myApproveDomains(opts) {
|
|||
var domains = [];
|
||||
var original = opts.domain;
|
||||
var bare = original.replace(/^(www|api)\./, "");
|
||||
var challenger = matchConfig(config.challenges, original);
|
||||
if (challenger) {
|
||||
opts.challenges = {
|
||||
"dns-01": config.challenges[challenger]
|
||||
};
|
||||
domains.push(challenger);
|
||||
return approveThem();
|
||||
}
|
||||
|
||||
if (matchConfig(config.proxy, original)) {
|
||||
console.log("debug: found proxy for", original);
|
||||
domains.push(original);
|
||||
return approveThem();
|
||||
}
|
||||
|
||||
function approveThem() {
|
||||
console.info("Approved domains:", domains);
|
||||
opts.domains = domains;
|
||||
//opts.email = email;
|
||||
opts.agreeTos = true;
|
||||
// pick the shortest (bare) or latest (www. instead of api.) to be the subject
|
||||
opts.subject = opts.domains.sort(function(a, b) {
|
||||
var len = a.length - b.length;
|
||||
if (0 !== len) {
|
||||
return len;
|
||||
}
|
||||
if (a < b) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
})[0];
|
||||
|
||||
if (!opts.challenges) {
|
||||
opts.challenges = {};
|
||||
}
|
||||
opts.challenges["http-01"] = require("le-challenge-fs");
|
||||
//opts.challenges['dns-01'] = require('le-challenge-dns');
|
||||
|
||||
// explicitly set account id and certificate.id
|
||||
opts.account = { id: opts.email };
|
||||
opts.certificate = { id: opts.subject };
|
||||
|
||||
return Promise.resolve(opts);
|
||||
}
|
||||
|
||||
// The goal here is to support both bare and www domains
|
||||
//
|
||||
// dns:example.com + fs:www.example.com => both
|
||||
|
@ -77,7 +148,6 @@ function myApproveDomains(opts) {
|
|||
//
|
||||
// dns:example.com + fs:example.com => example.com
|
||||
// dns:www.example.com + fs:www.example.com => www.example.com
|
||||
//
|
||||
return checkWwws(bare)
|
||||
.then(function(hostname) {
|
||||
// hostname is either example.com or www.example.com
|
||||
|
@ -116,34 +186,7 @@ function myApproveDomains(opts) {
|
|||
return Promise.reject(new Error("no bare, www., or api. domain matching '" + opts.domain + "'"));
|
||||
}
|
||||
|
||||
console.info("Approved domains:", domains);
|
||||
opts.domains = domains;
|
||||
//opts.email = email;
|
||||
opts.agreeTos = true;
|
||||
// pick the shortest (bare) or latest (www. instead of api.) to be the subject
|
||||
opts.subject = opts.domains.sort(function(a, b) {
|
||||
var len = a.length - b.length;
|
||||
if (0 !== len) {
|
||||
return len;
|
||||
}
|
||||
if (a < b) {
|
||||
return 1;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
})[0];
|
||||
|
||||
if (!opts.challenges) {
|
||||
opts.challenges = {};
|
||||
}
|
||||
opts.challenges["http-01"] = require("le-challenge-fs");
|
||||
//opts.challenges['dns-01'] = require('le-challenge-dns');
|
||||
|
||||
// explicitly set account id and certificate.id
|
||||
opts.account = { id: opts.email };
|
||||
opts.certificate = { id: opts.subject };
|
||||
|
||||
return Promise.resolve(opts);
|
||||
return approveThem();
|
||||
});
|
||||
}
|
||||
exports.myApproveDomains = myApproveDomains;
|
||||
|
@ -213,12 +256,35 @@ function checkWwws(_hostname) {
|
|||
}
|
||||
exports.checkWwws = checkWwws;
|
||||
|
||||
var httpProxy = require("http-proxy");
|
||||
|
||||
var proxy = httpProxy.createProxyServer({
|
||||
xfwd: true
|
||||
});
|
||||
|
||||
proxy.on("error", function(req, res) {
|
||||
res.statusCode = 500;
|
||||
res.end("500: Server Error");
|
||||
});
|
||||
|
||||
function myVhostApp(req, res) {
|
||||
req.on("error", function(err) {
|
||||
console.error("HTTPS Request Network Connection Error:");
|
||||
console.error(err);
|
||||
});
|
||||
|
||||
// this is protected by greenlock-express from domain fronting attacks
|
||||
var host = req.headers.host;
|
||||
// ex: example.com
|
||||
// ex: example.com:4080
|
||||
console.log("debug: host is", host);
|
||||
var domain = matchConfig(config.proxy, host);
|
||||
if (domain) {
|
||||
console.log("debug: forwarding to", config.proxy[domain]);
|
||||
proxy.web(req, res, { target: config.proxy[domain] });
|
||||
return;
|
||||
}
|
||||
|
||||
// SECURITY greenlock pre-sanitizes hostnames to prevent unauthorized fs access so you don't have to
|
||||
// (also: only domains approved above will get here)
|
||||
console.info("");
|
||||
|
|
Loading…
Reference in New Issue