2019-11-02 18:38:12 +00:00
|
|
|
"use strict";
|
2019-04-01 07:56:41 +00:00
|
|
|
|
2019-11-02 18:38:12 +00:00
|
|
|
var os = require("os");
|
|
|
|
var path = require("path");
|
2019-04-08 06:14:28 +00:00
|
|
|
|
|
|
|
// How Storage Works in Greenlock: High-Level Call Stack
|
|
|
|
//
|
|
|
|
// nested === skipped if parent succeeds (or has cached result)
|
|
|
|
//
|
|
|
|
// tls.SNICallback() // TLS connection with SNI kicks of the request
|
|
|
|
//
|
|
|
|
// greenlock.approveDomains(opts) // Greenlokc does some housekeeping, checks for a cert in
|
2019-10-27 10:01:42 +00:00
|
|
|
// // an internal cache, and only asks you to approve new
|
2019-04-08 06:14:28 +00:00
|
|
|
// // certificate // registration if it doesn't find anything.
|
|
|
|
// // In `opts` you'll receive `domain` and a few other things.
|
|
|
|
// // You should return { subject: '...', altnames: ['...'] }
|
|
|
|
// // Anything returned by approveDomains() will be received
|
|
|
|
// // by all plugins at all stages
|
|
|
|
//
|
|
|
|
// greenlock.store.certificates.check() // Certificate checking happens after approval for several
|
|
|
|
// // reasons, including preventing duplicate registrations
|
|
|
|
// // but most importantly because you can dynamically swap the
|
|
|
|
// // storage plugin right from approveDomains().
|
|
|
|
// greenlock.store.certificates.checkKeypair() // Check for a keypair associated with the domain
|
|
|
|
//
|
|
|
|
// greenlock.store.accounts.check() // Optional. If you need it, look at other Greenlock docs
|
|
|
|
//
|
|
|
|
// greenlock.store.accounts.checkKeypair() // Check storage for registered account key
|
|
|
|
// (opts.generateKeypair||RSA.generateKeypair)() // Generates a new keypair
|
|
|
|
// greenlock.core.accounts.register() // Registers the keypair as an ACME account
|
|
|
|
// greenlock.store.accounts.setKeypair() // Saves the keypair of the registered account
|
|
|
|
// greenlock.store.accounts.set() // Optional. Saves superfluous ACME account metadata
|
|
|
|
//
|
|
|
|
// greenlock.core.certificates.register() // Begin certificate registration process & housekeeping
|
|
|
|
// (opts.generateKeypair||RSA.generateKeypair)() // Generates a new certificate keypair
|
|
|
|
// greenlock.acme.certificates.register() // Performs the ACME challenge processes
|
|
|
|
// greenlock.store.certificates.setKeypair() // Saves the keypair for the valid certificate
|
|
|
|
// greenlock.store.certificates.set() // Saves the valid certificate
|
|
|
|
|
|
|
|
////////////////////////////////////////////
|
|
|
|
// Recap of the high-level overview above //
|
|
|
|
////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// None of this ever gets called except if there's not a cert already cached.
|
|
|
|
// That only happens on service boot, and about every 75 days for each cert's renewal.
|
|
|
|
//
|
|
|
|
// Therefore, none of this needs to be fast, fancy, or clever
|
|
|
|
//
|
|
|
|
// For any type of customization, whatever is set in `approveDomains()` is available everywhere else.
|
2019-04-01 07:56:41 +00:00
|
|
|
|
2019-04-08 06:14:28 +00:00
|
|
|
// Either your user calls create with specific options, or greenlock calls it for you with a big options blob
|
2019-10-27 10:01:42 +00:00
|
|
|
module.exports.create = function(config) {
|
2019-11-02 18:38:12 +00:00
|
|
|
// 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.
|
2019-10-27 10:01:42 +00:00
|
|
|
|
2019-11-02 18:38:12 +00:00
|
|
|
// basic setup
|
|
|
|
var store = {
|
|
|
|
accounts: require("./accounts.js"),
|
|
|
|
certificates: require("./certificates.js")
|
|
|
|
};
|
2019-10-27 10:01:42 +00:00
|
|
|
|
2019-11-02 18:38:12 +00:00
|
|
|
// For you store.options should probably start empty and get a minimal set of options copied from `config` above.
|
|
|
|
// Example:
|
|
|
|
//store.options = {};
|
|
|
|
//store.options.databaseUrl = config.databaseUrl;
|
2019-10-27 10:01:42 +00:00
|
|
|
|
2019-11-02 18:38:12 +00:00
|
|
|
// In the case of greenlock-store-fs there's a bunch of legacy stuff that goes on, so we just clobber it all on.
|
|
|
|
// Don't be like greenlock-store-fs (see note above).
|
|
|
|
store.options = mergeOptions(config);
|
|
|
|
store.accounts.options = store.options;
|
|
|
|
store.certificates.options = store.options;
|
2019-10-27 10:01:42 +00:00
|
|
|
|
2019-11-02 18:38:12 +00:00
|
|
|
if (!config.basePath && !config.configDir) {
|
|
|
|
console.info("Greenlock Store FS Path:", store.options.configDir);
|
|
|
|
}
|
2019-10-27 10:01:42 +00:00
|
|
|
|
2019-11-02 18:38:12 +00:00
|
|
|
return store;
|
2019-04-01 07:56:41 +00:00
|
|
|
};
|
|
|
|
|
2019-04-08 06:14:28 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// Ignore //
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Everything below this line is just implementation specific
|
2019-04-01 07:56:41 +00:00
|
|
|
var defaults = {
|
2019-11-02 18:38:12 +00:00
|
|
|
basePath: path.join(os.homedir(), ".config", "greenlock"),
|
2019-10-27 10:01:42 +00:00
|
|
|
|
2019-11-02 18:38:12 +00:00
|
|
|
accountsDir: path.join(":basePath", "accounts", ":directoryUrl"),
|
|
|
|
serverDirGet: function(copy) {
|
|
|
|
return (copy.directoryUrl || copy.server || "")
|
|
|
|
.replace("https://", "")
|
|
|
|
.replace(/(\/)$/, "")
|
|
|
|
.replace(/\//g, path.sep);
|
|
|
|
},
|
|
|
|
privkeyPath: path.join(":basePath", ":env", ":subject", "privkey.pem"),
|
|
|
|
fullchainPath: path.join(":basePath", ":env", ":subject", "fullchain.pem"),
|
|
|
|
certPath: path.join(":basePath", ":env", ":subject", "cert.pem"),
|
|
|
|
chainPath: path.join(":basePath", ":env", ":subject", "chain.pem"),
|
|
|
|
bundlePath: path.join(":basePath", ":env", ":subject", "bundle.pem")
|
2019-04-01 07:56:41 +00:00
|
|
|
};
|
2019-10-27 10:01:42 +00:00
|
|
|
defaults.configDir = defaults.basePath;
|
2019-04-01 07:56:41 +00:00
|
|
|
|
|
|
|
function mergeOptions(configs) {
|
2019-11-02 18:38:12 +00:00
|
|
|
if (!configs.serverKeyPath) {
|
|
|
|
configs.serverKeyPath =
|
|
|
|
configs.domainKeyPath ||
|
|
|
|
configs.privkeyPath ||
|
|
|
|
defaults.privkeyPath;
|
|
|
|
}
|
2019-10-27 10:01:42 +00:00
|
|
|
|
2019-11-02 18:38:12 +00:00
|
|
|
Object.keys(defaults).forEach(function(key) {
|
|
|
|
if (!configs[key]) {
|
|
|
|
configs[key] = defaults[key];
|
|
|
|
}
|
|
|
|
});
|
2019-10-27 10:01:42 +00:00
|
|
|
|
2019-11-02 18:38:12 +00:00
|
|
|
return configs;
|
2019-04-08 06:14:28 +00:00
|
|
|
}
|