added ability to scope config by domain (issue #25)
This commit is contained in:
parent
1f8e44947f
commit
1e3021c669
|
@ -10,6 +10,12 @@ tcp:
|
||||||
address: '127.0.0.1:8022'
|
address: '127.0.0.1:8022'
|
||||||
|
|
||||||
tls:
|
tls:
|
||||||
|
domains:
|
||||||
|
- names:
|
||||||
|
- localhost.gamma.daplie.me
|
||||||
|
modules:
|
||||||
|
- name: proxy
|
||||||
|
address: '127.0.0.1:6443'
|
||||||
modules:
|
modules:
|
||||||
- name: proxy
|
- name: proxy
|
||||||
domains:
|
domains:
|
||||||
|
@ -21,6 +27,16 @@ http:
|
||||||
trust_proxy: true
|
trust_proxy: true
|
||||||
allow_insecure: false
|
allow_insecure: false
|
||||||
primary_domain: localhost.foo.daplie.me
|
primary_domain: localhost.foo.daplie.me
|
||||||
|
domains:
|
||||||
|
- names:
|
||||||
|
- localhost.baz.daplie.me
|
||||||
|
modules:
|
||||||
|
- name: redirect
|
||||||
|
from: /nowhere/in/particular
|
||||||
|
to: /just/an/example
|
||||||
|
- name: proxy
|
||||||
|
address: '127.0.0.1:3001'
|
||||||
|
|
||||||
modules:
|
modules:
|
||||||
- name: redirect
|
- name: redirect
|
||||||
domains:
|
domains:
|
||||||
|
|
|
@ -70,14 +70,43 @@ module.exports.create = function (deps, conf, greenlockMiddleware) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function moduleMatchesHost(req, mod) {
|
function hostMatchesDomains(req, domains) {
|
||||||
var host = separatePort((req.headers || req).host).host;
|
var host = separatePort((req.headers || req).host).host;
|
||||||
|
|
||||||
return mod.domains.some(function (pattern) {
|
return domains.some(function (pattern) {
|
||||||
return domainMatches(pattern, host);
|
return domainMatches(pattern, host);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function determinePrimaryHost() {
|
||||||
|
var result;
|
||||||
|
if (Array.isArray(conf.http.domains)) {
|
||||||
|
conf.http.domains.some(function (dom) {
|
||||||
|
return dom.names.some(function (domain) {
|
||||||
|
if (domain[0] !== '*') {
|
||||||
|
result = domain;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Array.isArray(conf.http.modules)) {
|
||||||
|
conf.http.modules.some(function (mod) {
|
||||||
|
return mod.domains.some(function (domain) {
|
||||||
|
if (domain[0] !== '*') {
|
||||||
|
result = domain;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
// We handle both HTTPS and HTTP traffic on the same ports, and we want to redirect
|
// We handle both HTTPS and HTTP traffic on the same ports, and we want to redirect
|
||||||
// any unencrypted requests to the same port they came from unless it came in on
|
// any unencrypted requests to the same port they came from unless it came in on
|
||||||
// the default HTTP port, in which case there wont be a port specified in the host.
|
// the default HTTP port, in which case there wont be a port specified in the host.
|
||||||
|
@ -100,18 +129,14 @@ module.exports.create = function (deps, conf, greenlockMiddleware) {
|
||||||
// Test for IPv4 and IPv6 addresses. These patterns will match some invalid addresses,
|
// Test for IPv4 and IPv6 addresses. These patterns will match some invalid addresses,
|
||||||
// but since those still won't be valid domains that won't really be a problem.
|
// but since those still won't be valid domains that won't really be a problem.
|
||||||
if (ipv4Re.test(host.host) || ipv6Re.test(host.host)) {
|
if (ipv4Re.test(host.host) || ipv6Re.test(host.host)) {
|
||||||
if (!conf.http.primaryDomain) {
|
var dest;
|
||||||
(conf.http.modules || []).some(function (mod) {
|
|
||||||
return mod.domains.some(function (domain) {
|
|
||||||
if (domain[0] !== '*') {
|
|
||||||
conf.http.primaryDomain = domain;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
if (conf.http.primaryDomain) {
|
if (conf.http.primaryDomain) {
|
||||||
req.headers.host = conf.http.primaryDomain + (host.port ? ':'+host.port : '');
|
dest = conf.http.primaryDomain;
|
||||||
|
} else {
|
||||||
|
dest = determinePrimaryHost();
|
||||||
|
}
|
||||||
|
if (dest) {
|
||||||
|
req.headers.host = dest + (host.port ? ':'+host.port : '');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -175,10 +200,6 @@ module.exports.create = function (deps, conf, greenlockMiddleware) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkProxy(mod, conn, opts, headers) {
|
function checkProxy(mod, conn, opts, headers) {
|
||||||
if (!moduleMatchesHost(headers, mod)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var index = opts.firstChunk.indexOf('\r\n\r\n');
|
var index = opts.firstChunk.indexOf('\r\n\r\n');
|
||||||
var body = opts.firstChunk.slice(index);
|
var body = opts.firstChunk.slice(index);
|
||||||
|
|
||||||
|
@ -216,10 +237,6 @@ module.exports.create = function (deps, conf, greenlockMiddleware) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function checkRedirect(mod, conn, opts, headers) {
|
function checkRedirect(mod, conn, opts, headers) {
|
||||||
if (!moduleMatchesHost(headers, mod)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!mod.fromRe || mod.fromRe.origSrc !== mod.from) {
|
if (!mod.fromRe || mod.fromRe.origSrc !== mod.from) {
|
||||||
// Escape any characters that (can) have special meaning in regular expression
|
// Escape any characters that (can) have special meaning in regular expression
|
||||||
// but that aren't the special characters we have interest in.
|
// but that aren't the special characters we have interest in.
|
||||||
|
@ -283,10 +300,6 @@ module.exports.create = function (deps, conf, greenlockMiddleware) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
function checkStatic(mod, conn, opts, headers) {
|
function checkStatic(mod, conn, opts, headers) {
|
||||||
if (!moduleMatchesHost(headers, mod)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rootDir = mod.root.replace(':hostname', separatePort(headers.host).host);
|
var rootDir = mod.root.replace(':hostname', separatePort(headers.host).host);
|
||||||
return statAsync(rootDir)
|
return statAsync(rootDir)
|
||||||
.then(function (stats) {
|
.then(function (stats) {
|
||||||
|
@ -309,6 +322,12 @@ module.exports.create = function (deps, conf, greenlockMiddleware) {
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var moduleChecks = {
|
||||||
|
proxy: checkProxy
|
||||||
|
, redirect: checkRedirect
|
||||||
|
, static: checkStatic
|
||||||
|
};
|
||||||
|
|
||||||
function handleConnection(conn) {
|
function handleConnection(conn) {
|
||||||
var opts = conn.__opts;
|
var opts = conn.__opts;
|
||||||
parseHeaders(conn, opts)
|
parseHeaders(conn, opts)
|
||||||
|
@ -318,19 +337,34 @@ module.exports.create = function (deps, conf, greenlockMiddleware) {
|
||||||
if (checkAdmin(conn, opts, headers)) { return; }
|
if (checkAdmin(conn, opts, headers)) { return; }
|
||||||
|
|
||||||
var prom = PromiseA.resolve(false);
|
var prom = PromiseA.resolve(false);
|
||||||
|
(conf.http.domains || []).forEach(function (dom) {
|
||||||
|
prom = prom.then(function (handled) {
|
||||||
|
if (handled) {
|
||||||
|
return handled;
|
||||||
|
}
|
||||||
|
if (!hostMatchesDomains(headers, dom.names)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return dom.modules.some(function (mod) {
|
||||||
|
if (moduleChecks[mod.name]) {
|
||||||
|
return moduleChecks[mod.name](mod, conn, opts, headers);
|
||||||
|
}
|
||||||
|
console.warn('unknown HTTP module under domains', dom.names.join(','), mod);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
(conf.http.modules || []).forEach(function (mod) {
|
(conf.http.modules || []).forEach(function (mod) {
|
||||||
prom = prom.then(function (handled) {
|
prom = prom.then(function (handled) {
|
||||||
if (handled) {
|
if (handled) {
|
||||||
return handled;
|
return handled;
|
||||||
}
|
}
|
||||||
if (mod.name === 'proxy') {
|
if (!hostMatchesDomains(headers, mod.domains)) {
|
||||||
return checkProxy(mod, conn, opts, headers);
|
return false;
|
||||||
}
|
}
|
||||||
if (mod.name === 'redirect') {
|
|
||||||
return checkRedirect(mod, conn, opts, headers);
|
if (moduleChecks[mod.name]) {
|
||||||
}
|
return moduleChecks[mod.name](mod, conn, opts, headers);
|
||||||
if (mod.name === 'static') {
|
|
||||||
return checkStatic(mod, conn, opts, headers);
|
|
||||||
}
|
}
|
||||||
console.warn('unknown HTTP module found', mod);
|
console.warn('unknown HTTP module found', mod);
|
||||||
});
|
});
|
||||||
|
|
|
@ -204,6 +204,7 @@ module.exports.create = function (deps, config, netHandler) {
|
||||||
return new tls.TLSSocket(wrapSocket(socket, opts), tlsOpts);
|
return new tls.TLSSocket(wrapSocket(socket, opts), tlsOpts);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
function terminate(socket, opts) {
|
function terminate(socket, opts) {
|
||||||
|
@ -244,30 +245,45 @@ module.exports.create = function (deps, config, netHandler) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var handled = (config.tls.modules || []).some(function (mod) {
|
function checkModule(mod) {
|
||||||
var relevant = mod.domains.some(function (pattern) {
|
if (mod.name === 'proxy') {
|
||||||
|
return proxy(socket, opts, mod);
|
||||||
|
}
|
||||||
|
if (mod.name !== 'acme') {
|
||||||
|
console.error('saw unknown TLS module', mod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var handled = (config.tls.domains || []).some(function (dom) {
|
||||||
|
var relevant = dom.names.some(function (pattern) {
|
||||||
return domainMatches(pattern, opts.servername);
|
return domainMatches(pattern, opts.servername);
|
||||||
});
|
});
|
||||||
if (!relevant) {
|
if (!relevant) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mod.name === 'proxy') {
|
return dom.modules.some(checkModule);
|
||||||
proxy(socket, opts, mod);
|
});
|
||||||
}
|
if (handled) {
|
||||||
else {
|
return;
|
||||||
console.error('saw unknown TLS module', mod);
|
}
|
||||||
|
|
||||||
|
handled = (config.tls.modules || []).some(function (mod) {
|
||||||
|
var relevant = mod.domains.some(function (pattern) {
|
||||||
|
return domainMatches(pattern, opts.servername);
|
||||||
|
});
|
||||||
|
if (!relevant) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return checkModule(mod);
|
||||||
return true;
|
|
||||||
});
|
});
|
||||||
|
if (handled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: figure out all of the domains that the other modules intend to handle, and only
|
// TODO: figure out all of the domains that the other modules intend to handle, and only
|
||||||
// terminate those ones, closing connections for all others.
|
// terminate those ones, closing connections for all others.
|
||||||
if (!handled) {
|
terminate(socket, opts);
|
||||||
terminate(socket, opts);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|
Loading…
Reference in New Issue