added support for different ACME config for different domains
This commit is contained in:
parent
21a77ad10a
commit
3633c7570b
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in New Issue