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",
|
email: "jon.doe@example.com",
|
||||||
configDir: path.join(__dirname, "acme"),
|
configDir: path.join(__dirname, "acme"),
|
||||||
srv: "/srv/www/",
|
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,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"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": {
|
"@root/mkdirp": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/@root/mkdirp/-/mkdirp-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/@root/mkdirp/-/mkdirp-1.0.0.tgz",
|
||||||
"integrity": "sha512-hxGAYUx5029VggfG+U9naAhQkoMSXtOeXtbql97m3Hi6/sQSRL/4khKZPyOF6w11glyCOU38WCNLu9nUcSjOfA=="
|
"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": {
|
"accepts": {
|
||||||
"version": "1.3.5",
|
"version": "1.3.5",
|
||||||
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
|
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.5.tgz",
|
||||||
|
@ -38,12 +38,12 @@
|
||||||
"integrity": "sha512-Aa4bUpq6ftX1VODiShOetOY5U0tsXY5EV7+fQwme3Q8Y9rjYBArBXHgFCAVKtK1AF+Ev8pIuF6Z42hzMFa73/w=="
|
"integrity": "sha512-Aa4bUpq6ftX1VODiShOetOY5U0tsXY5EV7+fQwme3Q8Y9rjYBArBXHgFCAVKtK1AF+Ev8pIuF6Z42hzMFa73/w=="
|
||||||
},
|
},
|
||||||
"acme-v2": {
|
"acme-v2": {
|
||||||
"version": "1.7.7",
|
"version": "1.8.2",
|
||||||
"resolved": "https://registry.npmjs.org/acme-v2/-/acme-v2-1.7.7.tgz",
|
"resolved": "https://registry.npmjs.org/acme-v2/-/acme-v2-1.8.2.tgz",
|
||||||
"integrity": "sha512-Pg0EQ45h8N2e4K2goYedutCgWxAmtcruwDHr6hgPBgAWEORVb5SQEdXjtEhCrn+APtr7MyFPryyzXpYpDD5ecA==",
|
"integrity": "sha512-uYGA+DuTnA44EsGXE413XnbTotGHCzkucXjMk23QRwGnaGlnr0lNBoYjByyeIVLSzj0W6Y9FqA9h+15+H+ltMw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@coolaj86/urequest": "^1.3.6",
|
"@root/request": "^1.3.11",
|
||||||
"rsa-compat": "^2.0.6"
|
"rsa-compat": "^2.0.8"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"array-flatten": {
|
"array-flatten": {
|
||||||
|
@ -175,6 +175,12 @@
|
||||||
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
|
"integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=",
|
||||||
"dev": true
|
"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": {
|
"express": {
|
||||||
"version": "4.16.4",
|
"version": "4.16.4",
|
||||||
"resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
|
"resolved": "https://registry.npmjs.org/express/-/express-4.16.4.tgz",
|
||||||
|
@ -237,6 +243,32 @@
|
||||||
"unpipe": "~1.0.0"
|
"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": {
|
"forwarded": {
|
||||||
"version": "0.1.2",
|
"version": "0.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz",
|
||||||
|
@ -250,13 +282,13 @@
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"greenlock": {
|
"greenlock": {
|
||||||
"version": "2.7.24",
|
"version": "2.8.2",
|
||||||
"resolved": "https://registry.npmjs.org/greenlock/-/greenlock-2.7.24.tgz",
|
"resolved": "https://registry.npmjs.org/greenlock/-/greenlock-2.8.2.tgz",
|
||||||
"integrity": "sha512-GQb2LMF6IiEzhp01F6eIN7HlPVlUWpWsBZZn7DOIo9upFAWhFpn2w1PStjGb17VmTkg+lgxzcajqcy6AJhCHUQ==",
|
"integrity": "sha512-pCAYjgVova1ZoUHhuCfIw/3Rs5tE6DK1YF2LI7Cyh15QFBZJNU7pngMvDfeFft3It4WqnHezNgyDWAeV2pWFaw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"acme": "^1.3.0",
|
"acme": "^1.3.0",
|
||||||
"acme-dns-01-cli": "^3.0.0",
|
"acme-dns-01-cli": "^3.0.0",
|
||||||
"acme-v2": "^1.7.7",
|
"acme-v2": "^1.8.1",
|
||||||
"cert-info": "^1.5.1",
|
"cert-info": "^1.5.1",
|
||||||
"greenlock-store-fs": "^3.0.2",
|
"greenlock-store-fs": "^3.0.2",
|
||||||
"keypairs": "^1.2.14",
|
"keypairs": "^1.2.14",
|
||||||
|
@ -287,6 +319,17 @@
|
||||||
"statuses": ">= 1.4.0 < 2"
|
"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": {
|
"iconv-lite": {
|
||||||
"version": "0.4.23",
|
"version": "0.4.23",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.23.tgz",
|
||||||
|
@ -467,6 +510,12 @@
|
||||||
"escape-html": "^1.0.3"
|
"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": {
|
"rsa-compat": {
|
||||||
"version": "2.0.8",
|
"version": "2.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/rsa-compat/-/rsa-compat-2.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/rsa-compat/-/rsa-compat-2.0.8.tgz",
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
"example": "examples"
|
"example": "examples"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"greenlock": "^2.7.24",
|
"greenlock": "^2.8.2",
|
||||||
"redirect-https": "^1.1.5"
|
"redirect-https": "^1.1.5"
|
||||||
},
|
},
|
||||||
"files": [
|
"files": [
|
||||||
|
@ -18,6 +18,7 @@
|
||||||
"spdy": "^3.4.7"
|
"spdy": "^3.4.7"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"http-proxy": "^1.17.0",
|
||||||
"express": "^4.16.3",
|
"express": "^4.16.3",
|
||||||
"express-basic-auth": "^1.2.0",
|
"express-basic-auth": "^1.2.0",
|
||||||
"finalhandler": "^1.1.1",
|
"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) {
|
function myApproveDomains(opts) {
|
||||||
console.info("SNI:", opts.domain);
|
console.info("SNI:", opts.domain);
|
||||||
// In this example the filesystem is our "database".
|
// In this example the filesystem is our "database".
|
||||||
|
@ -67,6 +92,52 @@ function myApproveDomains(opts) {
|
||||||
var domains = [];
|
var domains = [];
|
||||||
var original = opts.domain;
|
var original = opts.domain;
|
||||||
var bare = original.replace(/^(www|api)\./, "");
|
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
|
// The goal here is to support both bare and www domains
|
||||||
//
|
//
|
||||||
// dns:example.com + fs:www.example.com => both
|
// dns:example.com + fs:www.example.com => both
|
||||||
|
@ -77,7 +148,6 @@ function myApproveDomains(opts) {
|
||||||
//
|
//
|
||||||
// dns:example.com + fs:example.com => example.com
|
// dns:example.com + fs:example.com => example.com
|
||||||
// dns:www.example.com + fs:www.example.com => www.example.com
|
// dns:www.example.com + fs:www.example.com => www.example.com
|
||||||
//
|
|
||||||
return checkWwws(bare)
|
return checkWwws(bare)
|
||||||
.then(function(hostname) {
|
.then(function(hostname) {
|
||||||
// hostname is either example.com or www.example.com
|
// 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 + "'"));
|
return Promise.reject(new Error("no bare, www., or api. domain matching '" + opts.domain + "'"));
|
||||||
}
|
}
|
||||||
|
|
||||||
console.info("Approved domains:", domains);
|
return approveThem();
|
||||||
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);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
exports.myApproveDomains = myApproveDomains;
|
exports.myApproveDomains = myApproveDomains;
|
||||||
|
@ -213,12 +256,35 @@ function checkWwws(_hostname) {
|
||||||
}
|
}
|
||||||
exports.checkWwws = checkWwws;
|
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) {
|
function myVhostApp(req, res) {
|
||||||
req.on("error", function(err) {
|
req.on("error", function(err) {
|
||||||
console.error("HTTPS Request Network Connection Error:");
|
console.error("HTTPS Request Network Connection Error:");
|
||||||
console.error(err);
|
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
|
// SECURITY greenlock pre-sanitizes hostnames to prevent unauthorized fs access so you don't have to
|
||||||
// (also: only domains approved above will get here)
|
// (also: only domains approved above will get here)
|
||||||
console.info("");
|
console.info("");
|
||||||
|
|
Loading…
Reference in New Issue