added support for different ACME config for different domains

This commit is contained in:
tigerbot 2017-05-24 18:16:01 -06:00
parent 21a77ad10a
commit 3633c7570b
2 changed files with 100 additions and 51 deletions

View File

@ -10,18 +10,40 @@ tcp:
address: '127.0.0.1:8022' address: '127.0.0.1:8022'
tls: tls:
acme:
email: 'joe.shmoe@example.com'
server: 'https://acme-staging.api.letsencrypt.org/directory'
challenge_type: 'http-01'
approved_domains:
- localhost.baz.daplie.me
- localhost.beta.daplie.me
domains: domains:
- names: - names:
- localhost.gamma.daplie.me - localhost.gamma.daplie.me
modules: modules:
- name: proxy - name: proxy
address: '127.0.0.1:6443' address: '127.0.0.1:6443'
- names:
- beta.localhost.daplie.me
- baz.localhost.daplie.me
modules:
- name: acme
email: 'owner@example.com'
challenge_type: 'tls-sni-01'
# default server is 'https://acme-v01.api.letsencrypt.org/directory'
modules: modules:
- name: proxy - name: proxy
domains: domains:
- localhost.bar.daplie.me - localhost.bar.daplie.me
- localhost.foo.daplie.me - localhost.foo.daplie.me
address: '127.0.0.1:5443' address: '127.0.0.1:5443'
- name: acme
email: 'guest@example.com'
challenge_type: 'http-01'
domains:
- foo.localhost.daplie.me
- gamma.localhost.daplie.me
http: http:
trust_proxy: true trust_proxy: true

View File

@ -22,6 +22,12 @@ module.exports.create = function (deps, config, netHandler) {
return value || ''; return value || '';
} }
function nameMatchesDomains(name, domains) {
return domains.some(function (pattern) {
return domainMatches(pattern, name);
});
}
var addressNames = [ var addressNames = [
'remoteAddress' 'remoteAddress'
, 'remotePort' , 'remotePort'
@ -67,17 +73,17 @@ module.exports.create = function (deps, config, netHandler) {
} }
var le = greenlock.create({ var le = greenlock.create({
// server: 'staging'
server: 'https://acme-v01.api.letsencrypt.org/directory' server: 'https://acme-v01.api.letsencrypt.org/directory'
, challenges: { , challenges: {
'http-01': require('le-challenge-fs').create({ webrootPath: '/tmp/acme-challenges', debug: config.debug }) 'http-01': require('le-challenge-fs').create({ debug: config.debug })
, 'tls-sni-01': require('le-challenge-sni').create({ debug: config.debug }) , 'tls-sni-01': require('le-challenge-sni').create({ debug: config.debug })
// TODO dns-01 // TODO dns-01
//, 'dns-01': require('le-challenge-ddns').create() //, 'dns-01': require('le-challenge-ddns').create({ debug: config.debug })
} }
, challengeType: 'http-01'
, store: require('le-store-certbot').create({ webrootPath: '/tmp/acme-challenges' }) , store: require('le-store-certbot').create({ debug: config.debug })
, approveDomains: function (opts, certs, cb) { , approveDomains: function (opts, certs, cb) {
// This is where you check your database and associated // This is where you check your database and associated
@ -88,60 +94,87 @@ module.exports.create = function (deps, config, netHandler) {
if (certs) { if (certs) {
// TODO make sure the same options are used for renewal as for registration? // TODO make sure the same options are used for renewal as for registration?
opts.domains = certs.altnames; opts.domains = certs.altnames;
cb(null, { options: opts, certs: certs }); cb(null, { options: opts, certs: certs });
return; return;
} }
function complete(optsOverride) { function complete(optsOverride, domains) {
Object.keys(optsOverride).forEach(function (key) { if (!cb) {
opts[key] = optsOverride[key]; console.warn('tried to complete domain approval multiple times');
}); return;
cb(null, { options: opts, certs: certs });
} }
// We can't request certificates for wildcard domains, so filter any of those
// out of this list and put the domain that triggered this in the list if needed.
domains = (domains || []).filter(function (dom) { return dom[0] !== '*'; });
if (domains.indexOf(opts.domain) < 0) {
domains.push(opts.domain);
}
// TODO: allow user to specify options for challenges or storage.
// check config for domain name Object.assign(opts, optsOverride, { domains: domains, agreeTos: true });
if (-1 !== (config.tls.servernames || []).indexOf(opts.domain)) { cb(null, { options: opts, certs: certs });
// TODO how to handle SANs? cb = null;
// TODO fetch domain-specific email }
// TODO fetch domain-specific acmeDirectory
// NOTE: you can also change other options such as `challengeType` and `challenge` var handled = false;
// opts.challengeType = 'http-01'; if (Array.isArray(config.tls.domains)) {
// opts.challenge = require('le-challenge-fs').create({}); // TODO this doesn't actually work yet handled = config.tls.domains.some(function (dom) {
complete({ if (!nameMatchesDomains(opts.domain, dom.names)) {
return false;
}
return dom.modules.some(function (mod) {
if (mod.name !== 'acme') {
return false;
}
complete(mod, dom.names);
return true;
});
});
}
if (handled) {
return;
}
if (Array.isArray(config.tls.modules)) {
handled = config.tls.modules.some(function (mod) {
if (mod.name !== 'acme') {
return false;
}
if (!nameMatchesDomains(opts.domain, mod.domains)) {
return false;
}
complete(mod, mod.domains);
return true;
});
}
if (handled) {
return;
}
var defAcmeConf;
if (config.tls.acme) {
defAcmeConf = config.tls.acme;
} else {
defAcmeConf = {
email: config.tls.email email: config.tls.email
, agreeTos: true
, server: config.tls.acmeDirectoryUrl || le.server , server: config.tls.acmeDirectoryUrl || le.server
, challengeType: config.tls.challengeType || 'http-01' , challengeType: config.tls.challengeType || le.challengeType
}); , approvedDomains: config.tls.servernames
};
}
// Check config for domain name
// TODO: if `approvedDomains` isn't defined check all other modules to see if they can
// handle this domain (and what other domains it's grouped with).
if (-1 !== (defAcmeConf.approvedDomains || []).indexOf(opts.domain)) {
complete(defAcmeConf, defAcmeConf.approvedDomains);
return; return;
} }
// TODO ask http module (and potentially all other modules) about what domains it can
// handle. We can allow any domains that other modules will handle after we terminate TLS.
cb(new Error('domain is not allowed')); cb(new Error('domain is not allowed'));
// if (!modules.http) {
// modules.http = require('./modules/http.js').create(deps, config);
// }
// modules.http.checkServername(opts.domain).then(function (stuff) {
// if (!stuff || !stuff.domains) {
// // TODO once precheck is implemented we can just let it pass if it passes, yknow?
// cb(new Error('domain is not allowed'));
// return;
// }
// complete({
// domain: stuff.domain || stuff.domains[0]
// , domains: stuff.domains
// , email: stuff.email || program.email
// , server: stuff.acmeDirectoryUrl || program.acmeDirectoryUrl
// , challengeType: stuff.challengeType || program.challengeType
// , challenge: stuff.challenge
// });
// return;
// }, cb);
} }
}); });
le.tlsOptions = le.tlsOptions || le.httpsOptions; le.tlsOptions = le.tlsOptions || le.httpsOptions;
@ -257,10 +290,7 @@ module.exports.create = function (deps, config, netHandler) {
} }
var handled = (config.tls.domains || []).some(function (dom) { var handled = (config.tls.domains || []).some(function (dom) {
var relevant = dom.names.some(function (pattern) { if (!nameMatchesDomains(opts.servername, dom.names)) {
return domainMatches(pattern, opts.servername);
});
if (!relevant) {
return false; return false;
} }
@ -271,10 +301,7 @@ module.exports.create = function (deps, config, netHandler) {
} }
handled = (config.tls.modules || []).some(function (mod) { handled = (config.tls.modules || []).some(function (mod) {
var relevant = mod.domains.some(function (pattern) { if (!nameMatchesDomains(opts.servername, mod.domains)) {
return domainMatches(pattern, opts.servername);
});
if (!relevant) {
return false; return false;
} }
return checkModule(mod); return checkModule(mod);