diff --git a/index.js b/index.js index 6c04747..ac14445 100644 --- a/index.js +++ b/index.js @@ -31,14 +31,24 @@ var os = require("os"); module.exports.create = function (config) { // This file has been laid out in the order that options are used and calls are made - // greenlock.approveDomains) - // greenlock.store.certificates.checkAsync() - // greenlock.store.accounts.checkAsync() - // greenlock.store.accounts.setKeypairAsync() - // greenlock.store.accounts.setAsync() - // greenlock.store.certificates.checkKeypairAsync() - // greenlock.store.certificates.setKeypairAsync() - // greenlock.store.certificates.setAsync() + // SNICallback() // le-sni-auto has a cache + // greenlock.approveDomains() + // // you get opts.domain passed to you from SNI + // // 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.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 // 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'); return PromiseA.all([ - readFileAsync(privkeyPath, 'ascii') // 0 - , readFileAsync(certPath, 'ascii') // 1 - , readFileAsync(chainPath, 'ascii') // 2 + readFileAsync(tameWild(privkeyPath, opts.subject), 'ascii') // 0 + , readFileAsync(tameWild(certPath, opts.subject), 'ascii') // 1 + , readFileAsync(tameWild(chainPath, opts.subject), 'ascii') // 2 ]).then(function (all) { return { privkey: all[0] @@ -154,7 +164,8 @@ module.exports.create = function (config) { console.log('accounts.checkKeypairAsync for', id); 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 return JSON.parse(blob); }).catch(function (err) { @@ -175,9 +186,10 @@ module.exports.create = function (config) { console.log('accounts.setKeypairAsync for', id); keypair = opts.keypair || keypair; 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 - 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:'); var liveDir = opts.liveDir || path.join(opts.configDir, 'live', opts.subject); 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 return { privateKeyPem: key }; }).catch(function (err) { @@ -217,8 +229,8 @@ module.exports.create = function (config) { 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(path.dirname(privkeyPath)).then(function () { - return writeFileAsync(privkeyPath, keypair.privateKeyPem, 'ascii').then(function () { + return mkdirpAsync(tameWild(path.dirname(privkeyPath), opts.subject)).then(function () { + return writeFileAsync(tameWild(privkeyPath, opts.subject), keypair.privateKeyPem, 'ascii').then(function () { return null; }); }); @@ -242,18 +254,19 @@ module.exports.create = function (config) { //var privkeyPath = opts.privkeyPath || opts.domainKeyPath || path.join(liveDir, 'privkey.pem'); var bundlePath = opts.bundlePath || path.join(liveDir, 'bundle.pem'); - return mkdirpAsync(path.dirname(certPath)).then(function () { - return mkdirpAsync(path.dirname(chainPath)).then(function () { - return mkdirpAsync(path.dirname(fullchainPath)).then(function () { - return mkdirpAsync(path.dirname(bundlePath)).then(function () { + return mkdirpAsync(path.dirname(tameWild(certPath, opts.subject))).then(function () { + return mkdirpAsync(path.dirname(tameWild(chainPath, opts.subject))).then(function () { + return mkdirpAsync(path.dirname(tameWild(fullchainPath, opts.subject))).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([ - sfs.writeFileAsync(certPath, pems.cert, 'ascii') - , sfs.writeFileAsync(chainPath, pems.chain, 'ascii') - // Most platforms need these two - , sfs.writeFileAsync(fullchainPath, [ pems.cert, pems.chain ].join('\n'), 'ascii') - //, sfs.writeFileAsync(privkeyPath, pems.privkey, 'ascii') + sfs.writeFileAsync(tameWild(certPath, opts.subject), pems.cert, 'ascii') + , sfs.writeFileAsync(tameWild(chainPath, opts.subject), pems.chain, 'ascii') + // Most web servers need these two + , sfs.writeFileAsync(tameWild(fullchainPath, opts.subject), fullchainPem, 'ascii') // 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) { 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); +} diff --git a/package.json b/package.json index c162a32..48d7155 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "le-store-fs", - "version": "0.9.1", + "version": "0.9.2", "description": "A file-based certificate store for greenlock that supports wildcards.", "homepage": "https://git.coolaj86.com/coolaj86/le-store-fs.js", "main": "index.js",