v1.0.0: update docs, burn the logs, cleanup comments, etc
This commit is contained in:
parent
9f61e34a8a
commit
533cb5395d
14
README.md
14
README.md
|
@ -56,15 +56,19 @@ part of the name of the file storage path where the certificate will be saved (o
|
|||
## Simple Example
|
||||
|
||||
```js
|
||||
function approveDomains(opts, certs, cb) {
|
||||
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' !== wild) { cb(new Error(opts.domain + " is not allowed")); }
|
||||
if ('example.com' !== opts.domain && '*.example.com' !== wild) {
|
||||
cb(new Error(opts.domain + " is not allowed"));
|
||||
}
|
||||
|
||||
opts.subject = '*.example.com';
|
||||
opts.domains = ['*.example.com'];
|
||||
opts.subject = 'example.com';
|
||||
opts.domains = [ 'example.com', '*.example.com' ];
|
||||
|
||||
cb({ options: opts, certs: certs });
|
||||
return Promise.resolve(opts);
|
||||
}
|
||||
```
|
||||
|
||||
|
|
93
index.js
93
index.js
|
@ -37,7 +37,7 @@ module.exports.create = function (config) {
|
|||
// // you should set opts.subject as the cert "id" domain
|
||||
// // you should set opts.domains as all domains on the cert
|
||||
// // you should set opts.account.id, otherwise opts.email will be used
|
||||
// greenlock.store.certificates.checkAsync() // on success -> SNI cache, on fail \/
|
||||
// greenlock.store.certificates.checkAsync() // on success -> SNI cache, on fail -> checkAccount
|
||||
// greenlock.store.accounts.checkAsync() // optional (you can always return null)
|
||||
// greenlock.store.accounts.checkKeypairAsync()
|
||||
// greenlock.core.RSA.generateKeypair() // TODO double check name
|
||||
|
@ -51,9 +51,9 @@ module.exports.create = function (config) {
|
|||
// greenlock.store.certificates.setAsync()
|
||||
|
||||
// store
|
||||
// Bear in mind that the only time any of this gets called is on first access after startup, new registration,
|
||||
// and renewal - so none of this needs to be particularly fast. It may need to be memory efficient, however
|
||||
// (if you have more than 10,000 domains, for example).
|
||||
// Bear in mind that the only time any of this gets called is on first access after startup, new registration, and
|
||||
// renewal - so none of this needs to be particularly fast. It may need to be memory efficient, however - if you have
|
||||
// more than 10,000 domains, for example.
|
||||
var store = {};
|
||||
|
||||
// options:
|
||||
|
@ -66,10 +66,6 @@ module.exports.create = function (config) {
|
|||
// See the note on create() above.
|
||||
store.options = mergeOptions(config);
|
||||
|
||||
// getOptions():
|
||||
// This must be implemented for backwards compatibility. That is all.
|
||||
store.getOptions = function () { return store.options; };
|
||||
|
||||
// set and check account keypairs and account data
|
||||
store.accounts = {};
|
||||
// set and check domain keypairs and domain certificates
|
||||
|
@ -77,27 +73,17 @@ module.exports.create = function (config) {
|
|||
|
||||
// certificates.checkAsync({ subject, ... }):
|
||||
//
|
||||
// The first check is that a certificate looked for by domain name.
|
||||
// The first check is that a certificate looked for by its subject (primary domain name).
|
||||
// If that lookup succeeds, then nothing else needs to happen. Otherwise accounts.checkAsync will happen next.
|
||||
// What should happen here is a lookup in a database (or filesystem). Generally the pattern will be to see if the
|
||||
// domain is an exact match for a single-subject (single domain) or multi-subject (many domains via SANS/AltName)
|
||||
// and then stripping the first part of the domain to see if there's a wildcard match. If you're clever you could
|
||||
// also do these checks in parallel, but this only happens at startup and before renewal, so you don't have to get
|
||||
// unless you want to for fun.
|
||||
// The only input you need to be concerned with is opts.subject (which falls back to opts.domains[0] if not set).
|
||||
// However, this is called after `approveDomains)`, so any options that you set there will be available here too,
|
||||
// as well as any other config you might need to access from other modules, if you're doing something special.
|
||||
//
|
||||
// On Success: Promise.resolve({ ... }) - the pem or jwk for the certificate
|
||||
// On Failure: Promise.resolve(null) - do not return undefined, do not throw, do not reject
|
||||
// On Error: Promise.reject(new Error("something descriptive for the user"))
|
||||
// And since this is called after `approveDomains()`, any options that you set there will be available here too.
|
||||
store.certificates.checkAsync = function (opts) {
|
||||
// { domain, ... }
|
||||
console.log('certificates.checkAsync for', opts.domain, opts.subject, opts.domains);
|
||||
console.log(opts);
|
||||
console.log(new Error("just for the stack trace:").stack);
|
||||
// { certificate.id, subject, domains, ... }
|
||||
var id = opts.certificate && opts.certificate.id || opts.subject;
|
||||
//console.log('certificates.checkAsync for', opts.domain, opts.subject, opts.domains);
|
||||
//console.log(opts);
|
||||
|
||||
// Just to show that any options set in approveDomains will be available here
|
||||
// Just to show that any options set in approveDomains() will be available here
|
||||
// (the same is true for all of the hooks in this file)
|
||||
if (opts.exampleThrowError) { return Promise.reject(new Error("You want an error? You got it!")); }
|
||||
if (opts.exampleReturnNull) { return Promise.resolve(null); }
|
||||
|
@ -110,45 +96,46 @@ module.exports.create = function (config) {
|
|||
var chainPath = opts.chainPath || path.join(liveDir, 'chain.pem');
|
||||
|
||||
return PromiseA.all([
|
||||
readFileAsync(tameWild(privkeyPath, opts.subject), 'ascii') // 0
|
||||
, readFileAsync(tameWild(certPath, opts.subject), 'ascii') // 1
|
||||
, readFileAsync(tameWild(chainPath, opts.subject), 'ascii') // 2
|
||||
// all other PEM files are arrangements of these three
|
||||
readFileAsync(tameWild(privkeyPath, id), 'ascii') // 0
|
||||
, readFileAsync(tameWild(certPath, id), 'ascii') // 1
|
||||
, readFileAsync(tameWild(chainPath, id), 'ascii') // 2
|
||||
]).then(function (all) {
|
||||
// Success
|
||||
return {
|
||||
privkey: all[0]
|
||||
, cert: all[1]
|
||||
, chain: all[2]
|
||||
// When using a database, these should be retrieved
|
||||
// (as is they'll be read via cert-info)
|
||||
// When using a database, these should be retrieved too
|
||||
// (when not available they'll be generated from cert-info)
|
||||
//, subject: certinfo.subject
|
||||
//, altnames: certinfo.altnames
|
||||
//, issuedAt: certinfo.issuedAt // a.k.a. NotBefore
|
||||
//, expiresAt: certinfo.expiresAt // a.k.a. NotAfter
|
||||
};
|
||||
}).catch(function (err) {
|
||||
// Non-success
|
||||
if ('ENOENT' === err.code) { return null; }
|
||||
// Failure
|
||||
throw err;
|
||||
});
|
||||
};
|
||||
|
||||
// accounts.checkAsync({ accountId, email, [...] }): // Optional
|
||||
//
|
||||
// This is where you promise an account corresponding to the given the email and ID. All instance options
|
||||
// (i.e. 'options' above, merged with other "override" or per-use options, such as from 'approveDomains)')
|
||||
// are also available. You can ignore them unless your implementation is using them in some way.
|
||||
// You should error if the account cannot be found (otherwise an unexpected error will be thrown)
|
||||
// Although you can supply a 'check' thunk (node-style callback) here, it's going to be converted to a proper
|
||||
// promise, so just go ahead and use that from the get-go.
|
||||
// This is where you promise an account corresponding to the given the email and ID. All options set in
|
||||
// approveDomains() are also available. You can ignore them unless your implementation is using them in some way.
|
||||
//
|
||||
// Since accounts are based on public key, the act of creating a new account or returning an existing account
|
||||
// are the same in regards to the API and so we don't really need to store the account id or retrieve it.
|
||||
// This method only needs to be implemented if you need it for your own purposes
|
||||
//
|
||||
// On Success: Promise.resolve({ id, keypair, ... }) - an id and, for backwards compatibility, the abstract keypair
|
||||
// On Failure: Promise.resolve(null) - do not return undefined, do not throw, do not reject
|
||||
// On Error: Promise.reject(new Error("something descriptive for the user"))
|
||||
store.accounts.checkAsync = function (opts) {
|
||||
var id = opts.account.id || 'single-user';
|
||||
console.log('accounts.checkAsync for', id);
|
||||
// Since accounts are based on public key, the act of creating a new account or returning an existing account
|
||||
// are the same in regards to the API and so we don't really need to store the account id or retrieve it.
|
||||
// This method only needs to be implemented if you need it for your own purposes
|
||||
store.accounts.checkAsync = function (/*opts*/) {
|
||||
//var id = opts.account.id || 'single-user';
|
||||
//console.log('accounts.checkAsync for', id);
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
|
||||
|
@ -177,7 +164,7 @@ module.exports.create = function (config) {
|
|||
// accounts.setKeypairAsync({ keypair, email, ... }):
|
||||
//
|
||||
// The keypair details (RSA, ECDSA, etc) are chosen either by the greenlock defaults, global user defaults,
|
||||
// or whatever you set in approveDomains)
|
||||
// or whatever you set in approveDomains(). This is called *after* the account is successfully created.
|
||||
//
|
||||
// On Success: Promise.resolve(null) - just knowing the operation is successful will do
|
||||
// On Error: Promise.reject(new Error("something descriptive for the user"))
|
||||
|
@ -195,23 +182,26 @@ module.exports.create = function (config) {
|
|||
|
||||
// accounts.setAsync({ account, keypair, email, ... }):
|
||||
//
|
||||
// The account details, from ACME, if everything is successful.
|
||||
// The account details, from ACME, if everything is successful. Unless you need to do something with those account
|
||||
// details, this implementation can remain empty.
|
||||
//
|
||||
// On Success: Promise.resolve(null||{ id }) - do not return undefined, do not throw, do not reject
|
||||
// On Error: Promise.reject(new Error("something descriptive for the user"))
|
||||
store.accounts.setAsync = function (opts, receipt) {
|
||||
receipt = opts.receipt || receipt;
|
||||
console.log('account.setAsync:', receipt);
|
||||
store.accounts.setAsync = function (/*opts, receipt*/) {
|
||||
//receipt = opts.receipt || receipt;
|
||||
//console.log('account.setAsync:', receipt);
|
||||
return Promise.resolve(null);
|
||||
};
|
||||
|
||||
// certificates.checkKeypairAsync({ subject, ... }):
|
||||
//
|
||||
// Same rules as above apply, except for the private key of the certificate, not the public certificate itself.
|
||||
// Same rules as certificates.checkAsync apply, except for the private key of the certificate, not the public
|
||||
// certificate itself (similar to accounts.checkKeyPairAsync, but for certs).
|
||||
store.certificates.checkKeypairAsync = function (opts) {
|
||||
console.log('certificates.checkKeypairAsync:');
|
||||
//console.log('certificates.checkKeypairAsync:');
|
||||
var liveDir = opts.liveDir || path.join(opts.configDir, 'live', opts.subject);
|
||||
var privkeyPath = opts.privkeyPath || opts.domainKeyPath || path.join(liveDir, 'privkey.pem');
|
||||
|
||||
return readFileAsync(tameWild(privkeyPath, opts.subject), 'ascii').then(function (key) {
|
||||
// keypair is normally an opaque object, but here it's a pem for the filesystem
|
||||
return { privateKeyPem: key };
|
||||
|
@ -228,6 +218,7 @@ module.exports.create = function (config) {
|
|||
keypair = opts.keypair || keypair;
|
||||
var liveDir = opts.liveDir || path.join(opts.configDir, 'live', opts.subject);
|
||||
var privkeyPath = opts.privkeyPath || opts.domainKeyPath || path.join(liveDir, 'privkey.pem');
|
||||
|
||||
// keypair is normally an opaque object, but here it's a PEM for the FS
|
||||
return mkdirpAsync(tameWild(path.dirname(privkeyPath), opts.subject)).then(function () {
|
||||
return writeFileAsync(tameWild(privkeyPath, opts.subject), keypair.privateKeyPem, 'ascii').then(function () {
|
||||
|
@ -240,8 +231,8 @@ module.exports.create = function (config) {
|
|||
//
|
||||
// This is where certificates are set, as well as certinfo
|
||||
store.certificates.setAsync = function (opts) {
|
||||
console.log('certificates.setAsync:');
|
||||
console.log(opts.domain, '<=', opts.subject);
|
||||
//console.log('certificates.setAsync:');
|
||||
//console.log(opts.domain, '<=', opts.subject);
|
||||
var pems = {
|
||||
privkey: opts.pems.privkey
|
||||
, cert: opts.pems.cert
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "le-store-fs",
|
||||
"version": "0.9.2",
|
||||
"version": "1.0.0",
|
||||
"description": "A file-based certificate store for greenlock that supports wildcards.",
|
||||
"homepage": "https://git.coolaj86.com/coolaj86/le-store-fs.js",
|
||||
"main": "index.js",
|
||||
|
|
Loading…
Reference in New Issue