A file-based certificate store for greenlock that supports wildcards.
A greenlock keypair and certificate storage strategy with wildcard support (simpler successor to le-store-certbot).
``js`
var greenlock = require('greenlock');
var gl = greenlock.create({
configDir: '~/.config/acme'
, store: require('le-store-fs')
, approveDomains: approveDomains
, ...
});
The default file system layout mirrors that of le-store-certbot in order to make transitioning effortless,
in most situations:
``
acme
├── accounts
│ └── acme-staging-v02.api.letsencrypt.org
│ └── directory
│ └── sites@example.com.json
└── live
├── example.com
│ ├── bundle.pem
│ ├── cert.pem
│ ├── chain.pem
│ ├── fullchain.pem
│ └── privkey.pem
└── www.example.com
├── bundle.pem
├── cert.pem
├── chain.pem
├── fullchain.pem
└── privkey.pem
Working with wildcards and multiple altnames requires greenlock >= v2.7.
To do so you must set opts.subject and opts.domains within the approvedomains() callback.
subject refers to "the subject of the ssl certificate" as opposed to domain which indicates "the domain servernamesubject
used in the current request". For single-domain certificates they're always the same, but for multiple-domain
certificates must be the name no matter what domain is receiving a request. subject is used as
part of the name of the file storage path where the certificate will be saved (or retrieved).
domains should be the list of "altnames" on the certificate, which should include the subject.
`js
function approveDomains(opts) {
// Allow only example.com and *.example.com (such as foo.example.com)
// foo.example.com => *.example.com
var wild = '*.' + opts.domain.split('.').slice(1).join('.');
if ('example.com' !== opts.domain && '*.example.com' !== wild) {
cb(new Error(opts.domain + " is not allowed"));
}
opts.subject = 'example.com';
opts.domains = [ 'example.com', '*.example.com' ];
return Promise.resolve(opts);
}
`
`js
function approveDomains(opts, certs, cb) {
var related = getRelated(opts.domain);
if (!related) { cb(new Error(opts.domain + " is not allowed")); };
opts.subject = related.subject;
opts.domains = related.domains;
cb({ options: opts, certs: certs });
}
`
`js`
function getRelated(domain) {
var related;
var wild = '*.' + domain.split('.').slice(1).join('.');
if (Object.keys(allAllowedDomains).some(function (k) {
return allAllowedDomains[k].some(function (name) {
if (domain === name || wild === name) {
related = { subject: k, altnames: allAllowedDomains[k] };
return true;
}
});
})) {
return related;
}
}
`js``
var allAllowedDomains = {
'example.com': ['example.com', '*.example.com']
, 'example.net': ['example.net', '*.example.net']
}