added ability to scope config by domain (issue #25)

This commit is contained in:
tigerbot 2017-05-23 18:26:03 -06:00
parent 1f8e44947f
commit 1e3021c669
3 changed files with 110 additions and 44 deletions

View File

@ -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:

View File

@ -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);
}); });

View File

@ -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,31 +245,46 @@ 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);
return false;
} }
return true; handled = (config.tls.modules || []).some(function (mod) {
var relevant = mod.domains.some(function (pattern) {
return domainMatches(pattern, opts.servername);
}); });
if (!relevant) {
return false;
}
return checkModule(mod);
});
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 {
emit: function (type, socket) { emit: function (type, socket) {