v0.9.2: add comments, tame wild domains (use '_')

This commit is contained in:
AJ ONeal 2019-04-01 22:27:39 -06:00
parent cfb335902c
commit 9f61e34a8a
2 changed files with 47 additions and 28 deletions

View File

@ -31,14 +31,24 @@ var os = require("os");
module.exports.create = function (config) { module.exports.create = function (config) {
// This file has been laid out in the order that options are used and calls are made // This file has been laid out in the order that options are used and calls are made
// greenlock.approveDomains) // SNICallback() // le-sni-auto has a cache
// greenlock.store.certificates.checkAsync() // greenlock.approveDomains()
// greenlock.store.accounts.checkAsync() // // you get opts.domain passed to you from SNI
// greenlock.store.accounts.setKeypairAsync() // // you should set opts.subject as the cert "id" domain
// greenlock.store.accounts.setAsync() // // you should set opts.domains as all domains on the cert
// greenlock.store.certificates.checkKeypairAsync() // // you should set opts.account.id, otherwise opts.email will be used
// greenlock.store.certificates.setKeypairAsync() // greenlock.store.certificates.checkAsync() // on success -> SNI cache, on fail \/
// greenlock.store.certificates.setAsync() // greenlock.store.accounts.checkAsync() // optional (you can always return null)
// greenlock.store.accounts.checkKeypairAsync()
// greenlock.core.RSA.generateKeypair() // TODO double check name
// greenlock.core.accounts.register() // TODO double check name
// greenlock.store.accounts.setKeypairAsync() // TODO make sure this only happens on generate
// greenlock.store.accounts.setAsync() // optional
// greenlock.store.certificates.checkKeypairAsync()
// greenlock.core.RSA.generateKeypair() // TODO double check name
// greenlock.core.certificates.register() // TODO double check name
// greenlock.store.certificates.setKeypairAsync()
// greenlock.store.certificates.setAsync()
// store // store
// Bear in mind that the only time any of this gets called is on first access after startup, new registration, // Bear in mind that the only time any of this gets called is on first access after startup, new registration,
@ -100,9 +110,9 @@ module.exports.create = function (config) {
var chainPath = opts.chainPath || path.join(liveDir, 'chain.pem'); var chainPath = opts.chainPath || path.join(liveDir, 'chain.pem');
return PromiseA.all([ return PromiseA.all([
readFileAsync(privkeyPath, 'ascii') // 0 readFileAsync(tameWild(privkeyPath, opts.subject), 'ascii') // 0
, readFileAsync(certPath, 'ascii') // 1 , readFileAsync(tameWild(certPath, opts.subject), 'ascii') // 1
, readFileAsync(chainPath, 'ascii') // 2 , readFileAsync(tameWild(chainPath, opts.subject), 'ascii') // 2
]).then(function (all) { ]).then(function (all) {
return { return {
privkey: all[0] privkey: all[0]
@ -154,7 +164,8 @@ module.exports.create = function (config) {
console.log('accounts.checkKeypairAsync for', id); console.log('accounts.checkKeypairAsync for', id);
if (!opts.account.id) { return Promise.reject(new Error("'account.id' should have been set in approveDomains()")); } if (!opts.account.id) { return Promise.reject(new Error("'account.id' should have been set in approveDomains()")); }
return readFileAsync(path.join(opts.accountsDir, sanitizeFilename(id) + '.json'), 'utf8').then(function (blob) { var pathname = path.join(tameWild(opts.accountsDir, opts.subject), sanitizeFilename(id) + '.json');
return readFileAsync(tameWild(pathname, opts.subject), 'utf8').then(function (blob) {
// keypair is an opaque object that should be treated as blob // keypair is an opaque object that should be treated as blob
return JSON.parse(blob); return JSON.parse(blob);
}).catch(function (err) { }).catch(function (err) {
@ -175,9 +186,10 @@ module.exports.create = function (config) {
console.log('accounts.setKeypairAsync for', id); console.log('accounts.setKeypairAsync for', id);
keypair = opts.keypair || keypair; keypair = opts.keypair || keypair;
if (!opts.account.id) { return Promise.reject(new Error("'account.id' should have been set in approveDomains()")); } if (!opts.account.id) { return Promise.reject(new Error("'account.id' should have been set in approveDomains()")); }
return mkdirpAsync(opts.accountsDir).then(function () { return mkdirpAsync(tameWild(opts.accountsDir, opts.subject)).then(function () {
// keypair is an opaque object that should be treated as blob // keypair is an opaque object that should be treated as blob
return writeFileAsync(path.join(opts.accountsDir, sanitizeFilename(id) + '.json'), JSON.stringify(keypair), 'utf8'); var pathname = tameWild(path.join(opts.accountsDir, sanitizeFilename(id) + '.json'), opts.subject);
return writeFileAsync(tameWild(pathname, opts.subject), JSON.stringify(keypair), 'utf8');
}); });
}; };
@ -200,7 +212,7 @@ module.exports.create = function (config) {
console.log('certificates.checkKeypairAsync:'); console.log('certificates.checkKeypairAsync:');
var liveDir = opts.liveDir || path.join(opts.configDir, 'live', opts.subject); var liveDir = opts.liveDir || path.join(opts.configDir, 'live', opts.subject);
var privkeyPath = opts.privkeyPath || opts.domainKeyPath || path.join(liveDir, 'privkey.pem'); var privkeyPath = opts.privkeyPath || opts.domainKeyPath || path.join(liveDir, 'privkey.pem');
return readFileAsync(privkeyPath, 'ascii').then(function (key) { 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 // keypair is normally an opaque object, but here it's a pem for the filesystem
return { privateKeyPem: key }; return { privateKeyPem: key };
}).catch(function (err) { }).catch(function (err) {
@ -217,8 +229,8 @@ module.exports.create = function (config) {
var liveDir = opts.liveDir || path.join(opts.configDir, 'live', opts.subject); var liveDir = opts.liveDir || path.join(opts.configDir, 'live', opts.subject);
var privkeyPath = opts.privkeyPath || opts.domainKeyPath || path.join(liveDir, 'privkey.pem'); 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 // keypair is normally an opaque object, but here it's a PEM for the FS
return mkdirpAsync(path.dirname(privkeyPath)).then(function () { return mkdirpAsync(tameWild(path.dirname(privkeyPath), opts.subject)).then(function () {
return writeFileAsync(privkeyPath, keypair.privateKeyPem, 'ascii').then(function () { return writeFileAsync(tameWild(privkeyPath, opts.subject), keypair.privateKeyPem, 'ascii').then(function () {
return null; return null;
}); });
}); });
@ -242,18 +254,19 @@ module.exports.create = function (config) {
//var privkeyPath = opts.privkeyPath || opts.domainKeyPath || path.join(liveDir, 'privkey.pem'); //var privkeyPath = opts.privkeyPath || opts.domainKeyPath || path.join(liveDir, 'privkey.pem');
var bundlePath = opts.bundlePath || path.join(liveDir, 'bundle.pem'); var bundlePath = opts.bundlePath || path.join(liveDir, 'bundle.pem');
return mkdirpAsync(path.dirname(certPath)).then(function () { return mkdirpAsync(path.dirname(tameWild(certPath, opts.subject))).then(function () {
return mkdirpAsync(path.dirname(chainPath)).then(function () { return mkdirpAsync(path.dirname(tameWild(chainPath, opts.subject))).then(function () {
return mkdirpAsync(path.dirname(fullchainPath)).then(function () { return mkdirpAsync(path.dirname(tameWild(fullchainPath, opts.subject))).then(function () {
return mkdirpAsync(path.dirname(bundlePath)).then(function () { return mkdirpAsync(path.dirname(tameWild(bundlePath, opts.subject))).then(function () {
var fullchainPem = [ pems.cert, pems.chain ].join('\n'); // for Apache, Nginx, etc
var bundlePem = [ pems.privkey, pems.cert, pems.chain ].join('\n'); // for HAProxy
return PromiseA.all([ return PromiseA.all([
sfs.writeFileAsync(certPath, pems.cert, 'ascii') sfs.writeFileAsync(tameWild(certPath, opts.subject), pems.cert, 'ascii')
, sfs.writeFileAsync(chainPath, pems.chain, 'ascii') , sfs.writeFileAsync(tameWild(chainPath, opts.subject), pems.chain, 'ascii')
// Most platforms need these two // Most web servers need these two
, sfs.writeFileAsync(fullchainPath, [ pems.cert, pems.chain ].join('\n'), 'ascii') , sfs.writeFileAsync(tameWild(fullchainPath, opts.subject), fullchainPem, 'ascii')
//, sfs.writeFileAsync(privkeyPath, pems.privkey, 'ascii')
// HAProxy needs "bundle.pem" aka "combined.pem" // HAProxy needs "bundle.pem" aka "combined.pem"
, sfs.writeFileAsync(bundlePath, [ pems.privkey, pems.cert, pems.chain ].join('\n'), 'ascii') , sfs.writeFileAsync(tameWild(bundlePath, opts.subject), bundlePem, 'ascii')
]); ]);
}); });
}); });
@ -297,3 +310,9 @@ function mergeOptions(configs) {
function sanitizeFilename(id) { function sanitizeFilename(id) {
return id.replace(/(\.\.)|\\|\//g, '_').replace(/[^!-~]/g, '_'); return id.replace(/(\.\.)|\\|\//g, '_').replace(/[^!-~]/g, '_');
} }
// because not all file systems like '*' in a name (and they're scary)
function tameWild(path, wild) {
var tame = wild.replace(/\*/g, '_');
return path.replace(wild, tame);
}

View File

@ -1,6 +1,6 @@
{ {
"name": "le-store-fs", "name": "le-store-fs",
"version": "0.9.1", "version": "0.9.2",
"description": "A file-based certificate store for greenlock that supports wildcards.", "description": "A file-based certificate store for greenlock that supports wildcards.",
"homepage": "https://git.coolaj86.com/coolaj86/le-store-fs.js", "homepage": "https://git.coolaj86.com/coolaj86/le-store-fs.js",
"main": "index.js", "main": "index.js",