188 lines
6.0 KiB
JavaScript
188 lines
6.0 KiB
JavaScript
'use strict';
|
|
|
|
// IMPORTANT
|
|
// IMPORTANT
|
|
// IMPORTANT
|
|
//
|
|
// Ready? DON'T OVERTHINK IT!!! (Seriously, this is a huge problem)
|
|
//
|
|
// If you get confused, you're probably smart and thinking too deep.
|
|
//
|
|
// Want an explanation of how and why? Okay...
|
|
// https://coolaj86.com/articles/lets-encrypt-v2-step-by-step/
|
|
//
|
|
// But really, you probably don't want to know how and why (because then you'd be implementing your own from scratch)
|
|
//
|
|
// IMPORTANT
|
|
// IMPORTANT
|
|
// IMPORTANT
|
|
//
|
|
// If you want to create a storage strategy quick-and-easy, treat everything as either dumb strings or JSON blobs
|
|
// (just as is done here), don't try to do clever optimizations, 5th normal form, etc (you ain't gonna need it),
|
|
// but DO use the simple test provided by `greenlock-store-test`.
|
|
//
|
|
// IMPORTANT
|
|
// IMPORTANT
|
|
// IMPORTANT
|
|
//
|
|
// Don't get fancy. Don't overthink it.
|
|
// If you want to be fancy and clever, do that after you can pass `greenlock-store-test` the dumb way shown here.
|
|
//
|
|
// Also: please do contribute clarifying comments.
|
|
|
|
|
|
module.exports.create = function (opts) {
|
|
// pass in database url, connection string, filepath,
|
|
// or whatever it is you need to get your job done well
|
|
|
|
|
|
|
|
// This is our dummy in-memory storage.
|
|
// (we optionally receive it as an option so that it can be defined outside to make testing easier)
|
|
var cache = opts.cache || {};
|
|
if (!cache.accounts) { cache.accounts = {}; }
|
|
if (!cache.certificates) { cache.certificates = {}; }
|
|
// Although we could have two collections of keypairs,
|
|
// it's also fine to store both types together (their ids will be distinct).
|
|
if (!cache.keypairs) { cache.keypairs = {}; }
|
|
// This is an in-memory store, hence we don't actually save it.
|
|
function saveCertificate(id, blob) { cache.certificates[id] = blob; return null; }
|
|
function getCertificate(id) { return cache.certificates[id]; }
|
|
function saveKeypair(id, blob) { cache.keypairs[id] = blob; return null; }
|
|
function getKeypair(id) { return cache.keypairs[id]; }
|
|
|
|
|
|
|
|
var store = {};
|
|
// any options you need per instance
|
|
// (probably okay to leave empty)
|
|
store.options = {};
|
|
store.accounts = {};
|
|
store.certificates = {};
|
|
|
|
|
|
|
|
// Whenever a new keypair is used to successfully create an account, we need to save its keypair
|
|
store.accounts.setKeypair = function (opts) {
|
|
console.log('accounts.setKeypair:', opts.account, opts.email, opts.keypair);
|
|
|
|
var id = opts.account.id || opts.email || 'default';
|
|
var keypair = opts.keypair;
|
|
|
|
saveKeypair(id, JSON.stringify({
|
|
privateKeyPem: keypair.privateKeyPem
|
|
, privateKeyJwk: keypair.privateKeyJwk
|
|
}));
|
|
|
|
return null; // or Promise.resolve(null);
|
|
};
|
|
|
|
|
|
|
|
// We need a way to retrieve a prior account's keypair for renewals and additional ACME certificate "orders"
|
|
store.accounts.checkKeypair = function (opts) {
|
|
console.log('accounts.checkKeypair:', opts.account, opts.email);
|
|
|
|
var id = opts.account.id || opts.email || 'default';
|
|
var keyblob = getKeypair(id);
|
|
|
|
if (!keyblob) { return null; }
|
|
|
|
return JSON.parse(keyblob);
|
|
};
|
|
|
|
|
|
|
|
// We can optionally implement ACME account storage and retrieval
|
|
// (to reduce API calls), but it's really not necessary.
|
|
/*
|
|
store.accounts.set = function (opts) {
|
|
console.log('accounts.set:', opts);
|
|
return null;
|
|
};
|
|
store.accounts.check = function (opts) {
|
|
var id = opts.account.id || opts.email || 'default';
|
|
console.log('accounts.check:', opts);
|
|
return null;
|
|
};
|
|
*/
|
|
|
|
|
|
|
|
// The certificate keypairs (properly named privkey.pem, though sometimes sutpidly called cert.key)
|
|
// https://community.letsencrypt.org/t/what-are-those-pem-files/18402
|
|
// Certificate Keypairs must not be used for Accounts and vice-versamust not be the same as any account keypair
|
|
//
|
|
store.certificates.setKeypair = function (opts) {
|
|
console.log('certificates.setKeypair:', opts.certificate, opts.subject, opts.keypair);
|
|
|
|
// The ID is a string that doesn't clash between accounts and certificates.
|
|
// That's all you need to know... unless you're doing something special (in which case you're on your own).
|
|
var id = opts.certificate.kid || opts.certificate.id || opts.subject;
|
|
var keypair = opts.keypair;
|
|
|
|
saveKeypair(id, JSON.stringify({
|
|
privateKeyPem: keypair.privateKeyPem
|
|
, privateKeyJwk: keypair.privateKeyJwk
|
|
}));
|
|
// Note: you can use the "keypairs" package to convert between
|
|
// public and private for jwk and pem, as well as convert JWK <-> PEM
|
|
|
|
return null;
|
|
};
|
|
|
|
|
|
|
|
// You won't be able to use a certificate without it's private key, gotta save it
|
|
store.certificates.checkKeypair = function (opts) {
|
|
console.log('certificates.checkKeypair:', opts.certificate, opts.subject);
|
|
|
|
var id = opts.certificate.kid || opts.certificate.id || opts.subject;
|
|
var keyblob = getKeypair(id);
|
|
|
|
if (!keyblob) { return null; }
|
|
|
|
return JSON.parse(keyblob);
|
|
};
|
|
|
|
|
|
|
|
// And you'll also need to save certificates. You may find the metadata useful to save
|
|
// (perhaps to delete expired keys), but the same information can also be redireved from
|
|
// the key using the "cert-info" package.
|
|
store.certificates.set = function (opts) {
|
|
console.log('certificates.set:', opts.certificate, opts.subject);
|
|
|
|
var id = opts.certificate.id || opts.subject;
|
|
var pems = opts.pems;
|
|
saveCertificate(id, JSON.stringify({
|
|
cert: pems.cert
|
|
, chain: pems.chain
|
|
, subject: pems.subject
|
|
, altnames: pems.altnames
|
|
, issuedAt: pems.issuedAt // a.k.a. NotBefore
|
|
, expiresAt: pems.expiresAt // a.k.a. NotAfter
|
|
}));
|
|
|
|
return null;
|
|
};
|
|
|
|
|
|
|
|
// This is actually the first thing to be called after approveDomins(),
|
|
// but it's easiest to implement last since it's not useful until there
|
|
// are certs that can actually be loaded from storage.
|
|
store.certificates.check = function (opts) {
|
|
console.log('certificates.check:', opts.certificate, opts.subject);
|
|
|
|
var id = opts.certificate.id || opts.subject;
|
|
var certblob = getCertificate(id);
|
|
|
|
if (!certblob) { return null; }
|
|
|
|
return JSON.parse(certblob);
|
|
};
|
|
|
|
return store;
|
|
};
|