From 1e3021c66928a5c9f72c42ce36c237b919976060 Mon Sep 17 00:00:00 2001 From: tigerbot Date: Tue, 23 May 2017 18:26:03 -0600 Subject: [PATCH] added ability to scope config by domain (issue #25) --- goldilocks.example.yml | 16 +++++++ lib/modules/http.js | 98 ++++++++++++++++++++++++++++-------------- lib/modules/tls.js | 40 +++++++++++------ 3 files changed, 110 insertions(+), 44 deletions(-) diff --git a/goldilocks.example.yml b/goldilocks.example.yml index 80f1283..54e128b 100644 --- a/goldilocks.example.yml +++ b/goldilocks.example.yml @@ -10,6 +10,12 @@ tcp: address: '127.0.0.1:8022' tls: + domains: + - names: + - localhost.gamma.daplie.me + modules: + - name: proxy + address: '127.0.0.1:6443' modules: - name: proxy domains: @@ -21,6 +27,16 @@ http: trust_proxy: true allow_insecure: false 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: - name: redirect domains: diff --git a/lib/modules/http.js b/lib/modules/http.js index d5fa572..c6f2b1b 100644 --- a/lib/modules/http.js +++ b/lib/modules/http.js @@ -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; - return mod.domains.some(function (pattern) { + return domains.some(function (pattern) { 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 // 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. @@ -100,18 +129,14 @@ module.exports.create = function (deps, conf, greenlockMiddleware) { // 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. if (ipv4Re.test(host.host) || ipv6Re.test(host.host)) { - if (!conf.http.primaryDomain) { - (conf.http.modules || []).some(function (mod) { - return mod.domains.some(function (domain) { - if (domain[0] !== '*') { - conf.http.primaryDomain = domain; - return true; - } - }); - }); - } + var dest; 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) { - if (!moduleMatchesHost(headers, mod)) { - return false; - } - var index = opts.firstChunk.indexOf('\r\n\r\n'); var body = opts.firstChunk.slice(index); @@ -216,10 +237,6 @@ module.exports.create = function (deps, conf, greenlockMiddleware) { } function checkRedirect(mod, conn, opts, headers) { - if (!moduleMatchesHost(headers, mod)) { - return false; - } - if (!mod.fromRe || mod.fromRe.origSrc !== mod.from) { // Escape any characters that (can) have special meaning in regular expression // 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) { - if (!moduleMatchesHost(headers, mod)) { - return false; - } - var rootDir = mod.root.replace(':hostname', separatePort(headers.host).host); return statAsync(rootDir) .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) { var opts = conn.__opts; parseHeaders(conn, opts) @@ -318,19 +337,34 @@ module.exports.create = function (deps, conf, greenlockMiddleware) { if (checkAdmin(conn, opts, headers)) { return; } 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) { prom = prom.then(function (handled) { if (handled) { return handled; } - if (mod.name === 'proxy') { - return checkProxy(mod, conn, opts, headers); + if (!hostMatchesDomains(headers, mod.domains)) { + return false; } - if (mod.name === 'redirect') { - return checkRedirect(mod, conn, opts, headers); - } - if (mod.name === 'static') { - return checkStatic(mod, conn, opts, headers); + + if (moduleChecks[mod.name]) { + return moduleChecks[mod.name](mod, conn, opts, headers); } console.warn('unknown HTTP module found', mod); }); diff --git a/lib/modules/tls.js b/lib/modules/tls.js index 9c00ae1..a93c1da 100644 --- a/lib/modules/tls.js +++ b/lib/modules/tls.js @@ -204,6 +204,7 @@ module.exports.create = function (deps, config, netHandler) { return new tls.TLSSocket(wrapSocket(socket, opts), tlsOpts); } }); + return true; } function terminate(socket, opts) { @@ -244,30 +245,45 @@ module.exports.create = function (deps, config, netHandler) { return; } - var handled = (config.tls.modules || []).some(function (mod) { - var relevant = mod.domains.some(function (pattern) { + function checkModule(mod) { + 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); }); if (!relevant) { return false; } - if (mod.name === 'proxy') { - proxy(socket, opts, mod); - } - else { - console.error('saw unknown TLS module', mod); + return dom.modules.some(checkModule); + }); + if (handled) { + return; + } + + handled = (config.tls.modules || []).some(function (mod) { + var relevant = mod.domains.some(function (pattern) { + return domainMatches(pattern, opts.servername); + }); + if (!relevant) { return false; } - - return true; + return checkModule(mod); }); + if (handled) { + return; + } // TODO: figure out all of the domains that the other modules intend to handle, and only // terminate those ones, closing connections for all others. - if (!handled) { - terminate(socket, opts); - } + terminate(socket, opts); } return {