mirror of
				https://git.coolaj86.com/coolaj86/greenlock-store-fs.js.git
				synced 2025-11-04 02:52:47 +00:00 
			
		
		
		
	updates for Greenlock v3
This commit is contained in:
		
							parent
							
								
									a4aae8647d
								
							
						
					
					
						commit
						6d398d36c4
					
				
							
								
								
									
										113
									
								
								accounts.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										113
									
								
								accounts.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,113 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var accounts = module.exports;
 | 
				
			||||||
 | 
					var store = accounts;
 | 
				
			||||||
 | 
					var U = require('./utils.js');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var fs = require('fs');
 | 
				
			||||||
 | 
					var path = require('path');
 | 
				
			||||||
 | 
					var PromiseA = require('./promise.js');
 | 
				
			||||||
 | 
					var readFileAsync = PromiseA.promisify(fs.readFile);
 | 
				
			||||||
 | 
					var writeFileAsync = PromiseA.promisify(fs.writeFile);
 | 
				
			||||||
 | 
					var mkdirpAsync = PromiseA.promisify(require('@root/mkdirp'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Implement if you need the ACME account metadata elsewhere in the chain of events
 | 
				
			||||||
 | 
					//store.accounts.check = function (opts) {
 | 
				
			||||||
 | 
					//  console.log('accounts.check for', opts.account, opts.email);
 | 
				
			||||||
 | 
					//  return PromiseA.resolve(null);
 | 
				
			||||||
 | 
					//};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Accounts.checkKeypair
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Use account.id, or email, if id hasn't been set, to find an account keypair.
 | 
				
			||||||
 | 
					// Return an object with string privateKeyPem and/or object privateKeyJwk (or null, not undefined)
 | 
				
			||||||
 | 
					accounts.checkKeypair = function(opts) {
 | 
				
			||||||
 | 
						var id =
 | 
				
			||||||
 | 
							(opts.account && opts.account.id) ||
 | 
				
			||||||
 | 
							(opts.subscriberEmail || opts.email) ||
 | 
				
			||||||
 | 
							'single-user';
 | 
				
			||||||
 | 
						//console.log('accounts.checkKeypair for', id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var pathname = path.join(
 | 
				
			||||||
 | 
							accountsDir(store, opts),
 | 
				
			||||||
 | 
							sanitizeFilename(id) + '.json'
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						return readFileAsync(U._tameWild(pathname, opts.subject), 'utf8')
 | 
				
			||||||
 | 
							.then(function(blob) {
 | 
				
			||||||
 | 
								// keypair can treated as an opaque object and just passed along,
 | 
				
			||||||
 | 
								// but just to show you what it is...
 | 
				
			||||||
 | 
								var keypair = JSON.parse(blob);
 | 
				
			||||||
 | 
								return keypair;
 | 
				
			||||||
 | 
								/*
 | 
				
			||||||
 | 
					      {
 | 
				
			||||||
 | 
									privateKeyPem: keypair.privateKeyPem, // string PEM private key
 | 
				
			||||||
 | 
									privateKeyJwk: keypair.privateKeyJwk, // object JWK private key
 | 
				
			||||||
 | 
									private: keypair.private,
 | 
				
			||||||
 | 
									public: keypair.public
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
					      */
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							.catch(function(err) {
 | 
				
			||||||
 | 
								if ('ENOENT' === err.code) {
 | 
				
			||||||
 | 
									return null;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								throw err;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Accounts.setKeypair({ account, email, keypair, ... }):
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Use account.id (or email if no id is present) to save an account keypair
 | 
				
			||||||
 | 
					// Return null (not undefined) on success, or throw on error
 | 
				
			||||||
 | 
					accounts.setKeypair = function(opts) {
 | 
				
			||||||
 | 
						//console.log('accounts.setKeypair for', opts.account, opts.email, opts.keypair);
 | 
				
			||||||
 | 
						var id = opts.account.id || opts.email || 'single-user';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// you can just treat the keypair as opaque and save and retrieve it as JSON
 | 
				
			||||||
 | 
						var keyblob = JSON.stringify(opts.keypair);
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						var keyblob = JSON.stringify({
 | 
				
			||||||
 | 
							privateKeyPem: opts.keypair.privateKeyPem, // string PEM
 | 
				
			||||||
 | 
							privateKeyJwk: opts.keypair.privateKeyJwk, // object JWK
 | 
				
			||||||
 | 
					    private: opts.keypair.private
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ignore.
 | 
				
			||||||
 | 
						// Just implementation specific details here.
 | 
				
			||||||
 | 
						return mkdirpAsync(accountsDir(store, opts))
 | 
				
			||||||
 | 
							.then(function() {
 | 
				
			||||||
 | 
								var pathname = path.join(
 | 
				
			||||||
 | 
									accountsDir(store, opts),
 | 
				
			||||||
 | 
									sanitizeFilename(id) + '.json'
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								return writeFileAsync(
 | 
				
			||||||
 | 
									U._tameWild(pathname, opts.subject),
 | 
				
			||||||
 | 
									keyblob,
 | 
				
			||||||
 | 
									'utf8'
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							.then(function() {
 | 
				
			||||||
 | 
								// This is your job: return null, not undefined
 | 
				
			||||||
 | 
								return null;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Implement if you need the ACME account metadata elsewhere in the chain of events
 | 
				
			||||||
 | 
					//accounts.set = function (opts) {
 | 
				
			||||||
 | 
					//  console.log('account.set:', opts.account, opts.email, opts.receipt);
 | 
				
			||||||
 | 
					//  return PromiseA.resolve(null);
 | 
				
			||||||
 | 
					//};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function sanitizeFilename(id) {
 | 
				
			||||||
 | 
						return id.replace(/(\.\.)|\\|\//g, '_').replace(/[^!-~]/g, '_');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function accountsDir(store, opts) {
 | 
				
			||||||
 | 
						var dir = U._tpl(
 | 
				
			||||||
 | 
							store,
 | 
				
			||||||
 | 
							opts,
 | 
				
			||||||
 | 
							opts.accountsDir || store.options.accountsDir
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						return U._tameWild(dir, opts.subject || '');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										267
									
								
								certificates.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										267
									
								
								certificates.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,267 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var certificates = module.exports;
 | 
				
			||||||
 | 
					var store = certificates;
 | 
				
			||||||
 | 
					var U = require('./utils.js');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var fs = require('fs');
 | 
				
			||||||
 | 
					var path = require('path');
 | 
				
			||||||
 | 
					var PromiseA = require('./promise.js');
 | 
				
			||||||
 | 
					var sfs = require('safe-replace');
 | 
				
			||||||
 | 
					var readFileAsync = PromiseA.promisify(fs.readFile);
 | 
				
			||||||
 | 
					var writeFileAsync = PromiseA.promisify(fs.writeFile);
 | 
				
			||||||
 | 
					var mkdirpAsync = PromiseA.promisify(require('@root/mkdirp'));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Certificates.check
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Use certificate.id, or subject, if id hasn't been set, to find a certificate.
 | 
				
			||||||
 | 
					// Return an object with string PEMs for cert and chain (or null, not undefined)
 | 
				
			||||||
 | 
					certificates.check = function(opts) {
 | 
				
			||||||
 | 
						// { certificate.id, subject, ... }
 | 
				
			||||||
 | 
						var id = (opts.certificate && opts.certificate.id) || opts.subject;
 | 
				
			||||||
 | 
						//console.log('certificates.check for', opts.certificate, opts.subject);
 | 
				
			||||||
 | 
						//console.log(opts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// For advanced use cases:
 | 
				
			||||||
 | 
						// This just goes 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);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (opts.exampleReturnCerts) {
 | 
				
			||||||
 | 
							return Promise.resolve(opts.exampleReturnCerts);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return Promise.all([
 | 
				
			||||||
 | 
							readFileAsync(U._tameWild(privkeyPath(store, opts), id), 'ascii'), // 0 // all other PEM types are just
 | 
				
			||||||
 | 
							readFileAsync(U._tameWild(certPath(store, opts), id), 'ascii'), // 1 // some arrangement of these 3
 | 
				
			||||||
 | 
							readFileAsync(U._tameWild(chainPath(store, opts), id), 'ascii') // 2 // (bundle, combined, fullchain, etc)
 | 
				
			||||||
 | 
						])
 | 
				
			||||||
 | 
							.then(function(all) {
 | 
				
			||||||
 | 
								////////////////////////
 | 
				
			||||||
 | 
								// PAY ATTENTION HERE //
 | 
				
			||||||
 | 
								////////////////////////
 | 
				
			||||||
 | 
								// This is all you have to return: cert, chain
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									cert: all[1], // string PEM. the bare cert, half of the concatonated fullchain.pem you need
 | 
				
			||||||
 | 
									chain: all[2], // string PEM. the bare chain, the second half of the fullchain.pem
 | 
				
			||||||
 | 
									privkey: all[0] // string PEM. optional, allows checkKeypair to be skipped
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
									// These can be useful to store in your database,
 | 
				
			||||||
 | 
									// but otherwise they're easy to derive from the cert.
 | 
				
			||||||
 | 
									// (when not available they'll be generated from cert-info)
 | 
				
			||||||
 | 
									//, subject: certinfo.subject     // string domain name
 | 
				
			||||||
 | 
									//, altnames: certinfo.altnames   // array of domain name strings
 | 
				
			||||||
 | 
									//, issuedAt: certinfo.issuedAt   // number in ms (a.k.a. NotBefore)
 | 
				
			||||||
 | 
									//, expiresAt: certinfo.expiresAt // number in ms (a.k.a. NotAfter)
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							.catch(function(err) {
 | 
				
			||||||
 | 
								// Treat non-exceptional failures as null returns (not undefined)
 | 
				
			||||||
 | 
								if ('ENOENT' === err.code) {
 | 
				
			||||||
 | 
									return null;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								throw err; // True exceptions should be thrown
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Certificates.checkKeypair
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Use certificate.kid, certificate.id, or subject to find a certificate keypair
 | 
				
			||||||
 | 
					// Return an object with string privateKeyPem and/or object privateKeyJwk (or null, not undefined)
 | 
				
			||||||
 | 
					certificates.checkKeypair = function(opts) {
 | 
				
			||||||
 | 
						//console.log('certificates.checkKeypair:', opts.certificate, opts.subject);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return readFileAsync(
 | 
				
			||||||
 | 
							U._tameWild(privkeyPath(store, opts), opts.subject),
 | 
				
			||||||
 | 
							'ascii'
 | 
				
			||||||
 | 
						)
 | 
				
			||||||
 | 
							.then(function(key) {
 | 
				
			||||||
 | 
								////////////////////////
 | 
				
			||||||
 | 
								// PAY ATTENTION HERE //
 | 
				
			||||||
 | 
								////////////////////////
 | 
				
			||||||
 | 
								return {
 | 
				
			||||||
 | 
									privateKeyPem: key // In this case we only saved privateKeyPem, so we only return it
 | 
				
			||||||
 | 
									//privateKeyJwk: null     // (but it's fine, just different encodings of the same thing)
 | 
				
			||||||
 | 
								};
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							.catch(function(err) {
 | 
				
			||||||
 | 
								if ('ENOENT' === err.code) {
 | 
				
			||||||
 | 
									return null;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								throw err;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Certificates.setKeypair({ certificate, subject, keypair, ... }):
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Use certificate.kid (or certificate.id or subject if no kid is present) to find a certificate keypair
 | 
				
			||||||
 | 
					// Return null (not undefined) on success, or throw on error
 | 
				
			||||||
 | 
					certificates.setKeypair = function(opts) {
 | 
				
			||||||
 | 
						var keypair = opts.keypair || keypair;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ignore.
 | 
				
			||||||
 | 
						// Just specific implementation details.
 | 
				
			||||||
 | 
						return mkdirpAsync(
 | 
				
			||||||
 | 
							U._tameWild(path.dirname(privkeyPath(store, opts)), opts.subject)
 | 
				
			||||||
 | 
						).then(function() {
 | 
				
			||||||
 | 
							// keypair is normally an opaque object, but here it's a PEM for the FS (for things like Apache and Nginx)
 | 
				
			||||||
 | 
							return writeFileAsync(
 | 
				
			||||||
 | 
								U._tameWild(privkeyPath(store, opts), opts.subject),
 | 
				
			||||||
 | 
								keypair.privateKeyPem,
 | 
				
			||||||
 | 
								'ascii'
 | 
				
			||||||
 | 
							).then(function() {
 | 
				
			||||||
 | 
								return null;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Certificates.set({ subject, pems, ... }):
 | 
				
			||||||
 | 
					//
 | 
				
			||||||
 | 
					// Use certificate.id (or subject if no ki is present) to save a certificate
 | 
				
			||||||
 | 
					// Return null (not undefined) on success, or throw on error
 | 
				
			||||||
 | 
					certificates.set = function(opts) {
 | 
				
			||||||
 | 
						//console.log('certificates.set:', opts.subject, opts.pems);
 | 
				
			||||||
 | 
						var pems = {
 | 
				
			||||||
 | 
							cert: opts.pems.cert, // string PEM the first half of the concatonated fullchain.pem cert
 | 
				
			||||||
 | 
							chain: opts.pems.chain, // string PEM the second half (yes, you need this too)
 | 
				
			||||||
 | 
							privkey: opts.pems.privkey // Ignore. string PEM, useful if you have to create bundle.pem
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Ignore
 | 
				
			||||||
 | 
						// Just implementation specific details (writing lots of combinatons of files)
 | 
				
			||||||
 | 
						return mkdirpAsync(path.dirname(certPath(store, opts)))
 | 
				
			||||||
 | 
							.then(function() {
 | 
				
			||||||
 | 
								return mkdirpAsync(
 | 
				
			||||||
 | 
									path.dirname(U._tameWild(chainPath(store, opts), opts.subject))
 | 
				
			||||||
 | 
								).then(function() {
 | 
				
			||||||
 | 
									return mkdirpAsync(
 | 
				
			||||||
 | 
										path.dirname(
 | 
				
			||||||
 | 
											U._tameWild(fullchainPath(store, opts), opts.subject)
 | 
				
			||||||
 | 
										)
 | 
				
			||||||
 | 
									).then(function() {
 | 
				
			||||||
 | 
										return mkdirpAsync(
 | 
				
			||||||
 | 
											path.dirname(
 | 
				
			||||||
 | 
												U._tameWild(bundlePath(store, opts), opts.subject)
 | 
				
			||||||
 | 
											)
 | 
				
			||||||
 | 
										).then(function() {
 | 
				
			||||||
 | 
											var fullchainPem = [
 | 
				
			||||||
 | 
												pems.cert.trim() + '\n',
 | 
				
			||||||
 | 
												pems.chain.trim() + '\n'
 | 
				
			||||||
 | 
											].join('\n'); // for Apache, Nginx, etc
 | 
				
			||||||
 | 
											var bundlePem = [
 | 
				
			||||||
 | 
												pems.privkey,
 | 
				
			||||||
 | 
												pems.cert,
 | 
				
			||||||
 | 
												pems.chain
 | 
				
			||||||
 | 
											].join('\n'); // for HAProxy
 | 
				
			||||||
 | 
											return PromiseA.all([
 | 
				
			||||||
 | 
												sfs.writeFileAsync(
 | 
				
			||||||
 | 
													U._tameWild(
 | 
				
			||||||
 | 
														certPath(store, opts),
 | 
				
			||||||
 | 
														opts.subject
 | 
				
			||||||
 | 
													),
 | 
				
			||||||
 | 
													pems.cert,
 | 
				
			||||||
 | 
													'ascii'
 | 
				
			||||||
 | 
												),
 | 
				
			||||||
 | 
												sfs.writeFileAsync(
 | 
				
			||||||
 | 
													U._tameWild(
 | 
				
			||||||
 | 
														chainPath(store, opts),
 | 
				
			||||||
 | 
														opts.subject
 | 
				
			||||||
 | 
													),
 | 
				
			||||||
 | 
													pems.chain,
 | 
				
			||||||
 | 
													'ascii'
 | 
				
			||||||
 | 
												),
 | 
				
			||||||
 | 
												// Most web servers need these two
 | 
				
			||||||
 | 
												sfs.writeFileAsync(
 | 
				
			||||||
 | 
													U._tameWild(
 | 
				
			||||||
 | 
														fullchainPath(store, opts),
 | 
				
			||||||
 | 
														opts.subject
 | 
				
			||||||
 | 
													),
 | 
				
			||||||
 | 
													fullchainPem,
 | 
				
			||||||
 | 
													'ascii'
 | 
				
			||||||
 | 
												),
 | 
				
			||||||
 | 
												// HAProxy needs "bundle.pem" aka "combined.pem"
 | 
				
			||||||
 | 
												sfs.writeFileAsync(
 | 
				
			||||||
 | 
													U._tameWild(
 | 
				
			||||||
 | 
														bundlePath(store, opts),
 | 
				
			||||||
 | 
														opts.subject
 | 
				
			||||||
 | 
													),
 | 
				
			||||||
 | 
													bundlePem,
 | 
				
			||||||
 | 
													'ascii'
 | 
				
			||||||
 | 
												)
 | 
				
			||||||
 | 
											]);
 | 
				
			||||||
 | 
										});
 | 
				
			||||||
 | 
									});
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							})
 | 
				
			||||||
 | 
							.then(function() {
 | 
				
			||||||
 | 
								// That's your job: return null
 | 
				
			||||||
 | 
								return null;
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function liveDir(store, opts) {
 | 
				
			||||||
 | 
						return opts.liveDir || path.join(opts.configDir, 'live', opts.subject);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function privkeyPath(store, opts) {
 | 
				
			||||||
 | 
						var dir = U._tpl(
 | 
				
			||||||
 | 
							store,
 | 
				
			||||||
 | 
							opts,
 | 
				
			||||||
 | 
							opts.serverKeyPath ||
 | 
				
			||||||
 | 
								opts.privkeyPath ||
 | 
				
			||||||
 | 
								opts.domainKeyPath ||
 | 
				
			||||||
 | 
								store.options.serverKeyPath ||
 | 
				
			||||||
 | 
								store.options.privkeyPath ||
 | 
				
			||||||
 | 
								store.options.domainKeyPath ||
 | 
				
			||||||
 | 
								path.join(liveDir(), 'privkey.pem')
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						return U._tameWild(dir, opts.subject || '');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function certPath(store, opts) {
 | 
				
			||||||
 | 
						var dir = U._tpl(
 | 
				
			||||||
 | 
							store,
 | 
				
			||||||
 | 
							opts,
 | 
				
			||||||
 | 
							opts.certPath ||
 | 
				
			||||||
 | 
								store.options.certPath ||
 | 
				
			||||||
 | 
								path.join(liveDir(), 'cert.pem')
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						return U._tameWild(dir, opts.subject || '');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function fullchainPath(store, opts) {
 | 
				
			||||||
 | 
						var dir = U._tpl(
 | 
				
			||||||
 | 
							store,
 | 
				
			||||||
 | 
							opts,
 | 
				
			||||||
 | 
							opts.fullchainPath ||
 | 
				
			||||||
 | 
								store.options.fullchainPath ||
 | 
				
			||||||
 | 
								path.join(liveDir(), 'fullchain.pem')
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						return U._tameWild(dir, opts.subject || '');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function chainPath(store, opts) {
 | 
				
			||||||
 | 
						var dir = U._tpl(
 | 
				
			||||||
 | 
							store,
 | 
				
			||||||
 | 
							opts,
 | 
				
			||||||
 | 
							opts.chainPath ||
 | 
				
			||||||
 | 
								store.options.chainPath ||
 | 
				
			||||||
 | 
								path.join(liveDir(), 'chain.pem')
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						return U._tameWild(dir, opts.subject || '');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function bundlePath(store, opts) {
 | 
				
			||||||
 | 
						var dir = U._tpl(
 | 
				
			||||||
 | 
							store,
 | 
				
			||||||
 | 
							opts,
 | 
				
			||||||
 | 
							opts.bundlePath ||
 | 
				
			||||||
 | 
								store.options.bundlePath ||
 | 
				
			||||||
 | 
								path.join(liveDir(), 'bundle.pem')
 | 
				
			||||||
 | 
						);
 | 
				
			||||||
 | 
						return U._tameWild(dir, opts.subject || '');
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										341
									
								
								index.js
									
									
									
									
									
								
							
							
						
						
									
										341
									
								
								index.js
									
									
									
									
									
								
							@ -1,15 +1,7 @@
 | 
				
			|||||||
'use strict';
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var os = require("os");
 | 
					var os = require('os');
 | 
				
			||||||
var fs = require('fs');
 | 
					 | 
				
			||||||
var path = require('path');
 | 
					var path = require('path');
 | 
				
			||||||
var sfs = require('safe-replace');
 | 
					 | 
				
			||||||
var PromiseA = getPromise();
 | 
					 | 
				
			||||||
var readFileAsync = PromiseA.promisify(fs.readFile);
 | 
					 | 
				
			||||||
var writeFileAsync = PromiseA.promisify(fs.writeFile);
 | 
					 | 
				
			||||||
// TODO replace with zero-depenency version
 | 
					 | 
				
			||||||
var mkdirpAsync = PromiseA.promisify(require('@root/mkdirp'));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// How Storage Works in Greenlock: High-Level Call Stack
 | 
					// How Storage Works in Greenlock: High-Level Call Stack
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
@ -18,7 +10,7 @@ var mkdirpAsync = PromiseA.promisify(require('@root/mkdirp'));
 | 
				
			|||||||
// tls.SNICallback()                                      // TLS connection with SNI kicks of the request
 | 
					// tls.SNICallback()                                      // TLS connection with SNI kicks of the request
 | 
				
			||||||
//
 | 
					//
 | 
				
			||||||
//   greenlock.approveDomains(opts)                       // Greenlokc does some housekeeping, checks for a cert in
 | 
					//   greenlock.approveDomains(opts)                       // Greenlokc does some housekeeping, checks for a cert in
 | 
				
			||||||
//                                                        // an internal cash, and only asks you to approve new
 | 
					//                                                        // an internal cache, and only asks you to approve new
 | 
				
			||||||
//                                                        // certificate // registration if it doesn't find anything.
 | 
					//                                                        // certificate // registration if it doesn't find anything.
 | 
				
			||||||
//                                                        // In `opts` you'll receive `domain` and a few other things.
 | 
					//                                                        // In `opts` you'll receive `domain` and a few other things.
 | 
				
			||||||
//                                                        // You should return { subject: '...', altnames: ['...'] }
 | 
					//                                                        // You should return { subject: '...', altnames: ['...'] }
 | 
				
			||||||
@ -45,7 +37,6 @@ var mkdirpAsync = PromiseA.promisify(require('@root/mkdirp'));
 | 
				
			|||||||
//       greenlock.store.certificates.setKeypair()        // Saves the keypair for the valid certificate
 | 
					//       greenlock.store.certificates.setKeypair()        // Saves the keypair for the valid certificate
 | 
				
			||||||
//       greenlock.store.certificates.set()               // Saves the valid certificate
 | 
					//       greenlock.store.certificates.set()               // Saves the valid certificate
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
////////////////////////////////////////////
 | 
					////////////////////////////////////////////
 | 
				
			||||||
// Recap of the high-level overview above //
 | 
					// Recap of the high-level overview above //
 | 
				
			||||||
////////////////////////////////////////////
 | 
					////////////////////////////////////////////
 | 
				
			||||||
@ -57,246 +48,34 @@ var mkdirpAsync = PromiseA.promisify(require('@root/mkdirp'));
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
//  For any type of customization, whatever is set in `approveDomains()` is available everywhere else.
 | 
					//  For any type of customization, whatever is set in `approveDomains()` is available everywhere else.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// Either your user calls create with specific options, or greenlock calls it for you with a big options blob
 | 
					// Either your user calls create with specific options, or greenlock calls it for you with a big options blob
 | 
				
			||||||
module.exports.create = function (config) {
 | 
					module.exports.create = function(config) {
 | 
				
			||||||
  // Bear in mind that the only time any of this gets called is on first access after startup, new registration, and
 | 
						// 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
 | 
						// 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.
 | 
						// more than 10,000 domains, for example.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // basic setup
 | 
						// basic setup
 | 
				
			||||||
  var store = { accounts: {}, certificates: {} };
 | 
						var store = {
 | 
				
			||||||
 | 
							accounts: require('./accounts.js'),
 | 
				
			||||||
 | 
							certificates: require('./certificates.js')
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // For you store.options should probably start empty and get a minimal set of options copied from `config` above.
 | 
						// For you store.options should probably start empty and get a minimal set of options copied from `config` above.
 | 
				
			||||||
  // Example:
 | 
						// Example:
 | 
				
			||||||
  //store.options = {};
 | 
						//store.options = {};
 | 
				
			||||||
  //store.options.databaseUrl = config.databaseUrl;
 | 
						//store.options.databaseUrl = config.databaseUrl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // In the case of greenlock-store-fs there's a bunch of legacy stuff that goes on, so we just clobber it all on.
 | 
						// 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).
 | 
						// Don't be like greenlock-store-fs (see note above).
 | 
				
			||||||
  store.options = mergeOptions(config);
 | 
						store.options = mergeOptions(config);
 | 
				
			||||||
 | 
						store.accounts.options = store.options;
 | 
				
			||||||
 | 
						store.certificates.options = store.options;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!config.basePath && !config.configDir) {
 | 
				
			||||||
 | 
							console.info('Greenlock Store FS Path:', store.options.configDir);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return store;
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Certificates.check
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // Use certificate.id, or subject, if id hasn't been set, to find a certificate.
 | 
					 | 
				
			||||||
  // Return an object with string PEMs for cert and chain (or null, not undefined)
 | 
					 | 
				
			||||||
  store.certificates.check = function (opts) {
 | 
					 | 
				
			||||||
    // { certificate.id, subject, ... }
 | 
					 | 
				
			||||||
    var id = opts.certificate && opts.certificate.id || opts.subject;
 | 
					 | 
				
			||||||
    //console.log('certificates.check for', opts.certificate, opts.subject);
 | 
					 | 
				
			||||||
    //console.log(opts);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // For advanced use cases:
 | 
					 | 
				
			||||||
    // This just goes 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 PromiseA.reject(new Error("You want an error? You got it!")); }
 | 
					 | 
				
			||||||
    if (opts.exampleReturnNull) { return PromiseA.resolve(null); }
 | 
					 | 
				
			||||||
    if (opts.exampleReturnCerts) { return PromiseA.resolve(opts.exampleReturnCerts); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Ignore this first bit, it's just file system template / compatibility stuff
 | 
					 | 
				
			||||||
    var liveDir = opts.liveDir || path.join(opts.configDir, 'live', opts.subject);
 | 
					 | 
				
			||||||
    var privkeyPath = opts.privkeyPath || opts.domainKeyPath || path.join(liveDir, 'privkey.pem');
 | 
					 | 
				
			||||||
    var certPath = opts.certPath || path.join(liveDir, 'cert.pem');
 | 
					 | 
				
			||||||
    var chainPath = opts.chainPath || path.join(liveDir, 'chain.pem');
 | 
					 | 
				
			||||||
    return PromiseA.all([
 | 
					 | 
				
			||||||
      readFileAsync(tameWild(privkeyPath, id), 'ascii')   // 0 // all other PEM types are just
 | 
					 | 
				
			||||||
    , readFileAsync(tameWild(certPath, id), 'ascii')      // 1 // some arrangement of these 3
 | 
					 | 
				
			||||||
    , readFileAsync(tameWild(chainPath, id), 'ascii')     // 2 // (bundle, combined, fullchain, etc)
 | 
					 | 
				
			||||||
    ]).then(function (all) {
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      ////////////////////////
 | 
					 | 
				
			||||||
      // PAY ATTENTION HERE //
 | 
					 | 
				
			||||||
      ////////////////////////
 | 
					 | 
				
			||||||
      // This is all you have to return: cert, chain
 | 
					 | 
				
			||||||
      return {
 | 
					 | 
				
			||||||
        cert: all[1]      // string PEM. the bare cert, half of the concatonated fullchain.pem you need
 | 
					 | 
				
			||||||
      , chain: all[2]     // string PEM. the bare chain, the second half of the fullchain.pem
 | 
					 | 
				
			||||||
      , privkey: all[0]   // string PEM. optional, allows checkKeypair to be skipped
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // These can be useful to store in your database,
 | 
					 | 
				
			||||||
      // but otherwise they're easy to derive from the cert.
 | 
					 | 
				
			||||||
      // (when not available they'll be generated from cert-info)
 | 
					 | 
				
			||||||
      //, subject: certinfo.subject     // string domain name
 | 
					 | 
				
			||||||
      //, altnames: certinfo.altnames   // array of domain name strings
 | 
					 | 
				
			||||||
      //, issuedAt: certinfo.issuedAt   // number in ms (a.k.a. NotBefore)
 | 
					 | 
				
			||||||
      //, expiresAt: certinfo.expiresAt // number in ms (a.k.a. NotAfter)
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    }).catch(function (err) {
 | 
					 | 
				
			||||||
      // Treat non-exceptional failures as null returns (not undefined)
 | 
					 | 
				
			||||||
      if ('ENOENT' === err.code) { return null; }
 | 
					 | 
				
			||||||
      throw err; // True exceptions should be thrown
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Implement if you need the ACME account metadata elsewhere in the chain of events
 | 
					 | 
				
			||||||
  //store.accounts.check = function (opts) {
 | 
					 | 
				
			||||||
  //  console.log('accounts.check for', opts.account, opts.email);
 | 
					 | 
				
			||||||
  //  return PromiseA.resolve(null);
 | 
					 | 
				
			||||||
  //};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Accounts.checkKeypair
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // Use account.id, or email, if id hasn't been set, to find an account keypair.
 | 
					 | 
				
			||||||
  // Return an object with string privateKeyPem and/or object privateKeyJwk (or null, not undefined)
 | 
					 | 
				
			||||||
  store.accounts.checkKeypair = function (opts) {
 | 
					 | 
				
			||||||
    var id = opts.account.id || opts.email || 'single-user';
 | 
					 | 
				
			||||||
    //console.log('accounts.checkKeypair for', id);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    var pathname = path.join(tameWild(opts.accountsDir, opts.subject), sanitizeFilename(id) + '.json');
 | 
					 | 
				
			||||||
    return readFileAsync(tameWild(pathname, opts.subject), 'utf8').then(function (blob) {
 | 
					 | 
				
			||||||
      // keypair can treated as an opaque object and just passed along,
 | 
					 | 
				
			||||||
      // but just to show you what it is...
 | 
					 | 
				
			||||||
      var keypair = JSON.parse(blob);
 | 
					 | 
				
			||||||
      return {
 | 
					 | 
				
			||||||
        privateKeyPem: keypair.privateKeyPem // string PEM private key
 | 
					 | 
				
			||||||
      , privateKeyJwk: keypair.privateKeyJwk // object JWK private key
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    }).catch(function (err) {
 | 
					 | 
				
			||||||
      if ('ENOENT' === err.code) { return null; }
 | 
					 | 
				
			||||||
      throw err;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Accounts.setKeypair({ account, email, keypair, ... }):
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // Use account.id (or email if no id is present) to save an account keypair
 | 
					 | 
				
			||||||
  // Return null (not undefined) on success, or throw on error
 | 
					 | 
				
			||||||
  store.accounts.setKeypair = function (opts) {
 | 
					 | 
				
			||||||
    //console.log('accounts.setKeypair for', opts.account, opts.email, opts.keypair);
 | 
					 | 
				
			||||||
    var id = opts.account.id || opts.email || 'single-user';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // you can just treat the keypair as opaque and save and retrieve it as JSON
 | 
					 | 
				
			||||||
    var keyblob = JSON.stringify({
 | 
					 | 
				
			||||||
      privateKeyPem: opts.keypair.privateKeyPem // string PEM
 | 
					 | 
				
			||||||
    , privateKeyJwk: opts.keypair.privateKeyJwk // object JWK
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Ignore.
 | 
					 | 
				
			||||||
    // Just implementation specific details here.
 | 
					 | 
				
			||||||
    return mkdirpAsync(tameWild(opts.accountsDir, opts.subject)).then(function () {
 | 
					 | 
				
			||||||
      var pathname = tameWild(path.join(opts.accountsDir, sanitizeFilename(id) + '.json'), opts.subject);
 | 
					 | 
				
			||||||
      return writeFileAsync(tameWild(pathname, opts.subject), keyblob, 'utf8');
 | 
					 | 
				
			||||||
    }).then(function () {
 | 
					 | 
				
			||||||
      // This is your job: return null, not undefined
 | 
					 | 
				
			||||||
      return null;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Implement if you need the ACME account metadata elsewhere in the chain of events
 | 
					 | 
				
			||||||
  //store.accounts.set = function (opts) {
 | 
					 | 
				
			||||||
  //  console.log('account.set:', opts.account, opts.email, opts.receipt);
 | 
					 | 
				
			||||||
  //  return PromiseA.resolve(null);
 | 
					 | 
				
			||||||
  //};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Certificates.checkKeypair
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // Use certificate.kid, certificate.id, or subject to find a certificate keypair
 | 
					 | 
				
			||||||
  // Return an object with string privateKeyPem and/or object privateKeyJwk (or null, not undefined)
 | 
					 | 
				
			||||||
  store.certificates.checkKeypair = function (opts) {
 | 
					 | 
				
			||||||
    //console.log('certificates.checkKeypair:', opts.certificate, opts.subject);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Ignore this. It's just special stuff for file system compat with the old le-store-certbot
 | 
					 | 
				
			||||||
    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) {
 | 
					 | 
				
			||||||
      ////////////////////////
 | 
					 | 
				
			||||||
      // PAY ATTENTION HERE //
 | 
					 | 
				
			||||||
      ////////////////////////
 | 
					 | 
				
			||||||
      return {
 | 
					 | 
				
			||||||
        privateKeyPem: key      // In this case we only saved privateKeyPem, so we only return it
 | 
					 | 
				
			||||||
      //privateKeyJwk: null     // (but it's fine, just different encodings of the same thing)
 | 
					 | 
				
			||||||
      };
 | 
					 | 
				
			||||||
    }).catch(function (err) {
 | 
					 | 
				
			||||||
      if ('ENOENT' === err.code) { return null; }
 | 
					 | 
				
			||||||
      throw err;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Certificates.setKeypair({ certificate, subject, keypair, ... }):
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // Use certificate.kid (or certificate.id or subject if no kid is present) to find a certificate keypair
 | 
					 | 
				
			||||||
  // Return null (not undefined) on success, or throw on error
 | 
					 | 
				
			||||||
  store.certificates.setKeypair = function (opts) {
 | 
					 | 
				
			||||||
    var keypair = opts.keypair || keypair;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Ignore.
 | 
					 | 
				
			||||||
    // Just specific implementation details.
 | 
					 | 
				
			||||||
    var liveDir = opts.liveDir || path.join(opts.configDir, 'live', opts.subject);
 | 
					 | 
				
			||||||
    var privkeyPath = opts.privkeyPath || opts.domainKeyPath || path.join(liveDir, 'privkey.pem');
 | 
					 | 
				
			||||||
    return mkdirpAsync(tameWild(path.dirname(privkeyPath), opts.subject)).then(function () {
 | 
					 | 
				
			||||||
      // keypair is normally an opaque object, but here it's a PEM for the FS (for things like Apache and Nginx)
 | 
					 | 
				
			||||||
      return writeFileAsync(tameWild(privkeyPath, opts.subject), keypair.privateKeyPem, 'ascii').then(function () {
 | 
					 | 
				
			||||||
        return null;
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // Certificates.set({ subject, pems, ... }):
 | 
					 | 
				
			||||||
  //
 | 
					 | 
				
			||||||
  // Use certificate.id (or subject if no ki is present) to save a certificate
 | 
					 | 
				
			||||||
  // Return null (not undefined) on success, or throw on error
 | 
					 | 
				
			||||||
  store.certificates.set = function (opts) {
 | 
					 | 
				
			||||||
    //console.log('certificates.set:', opts.subject, opts.pems);
 | 
					 | 
				
			||||||
    var pems = {
 | 
					 | 
				
			||||||
      cert: opts.pems.cert        // string PEM the first half of the concatonated fullchain.pem cert
 | 
					 | 
				
			||||||
    , chain: opts.pems.chain      // string PEM the second half (yes, you need this too)
 | 
					 | 
				
			||||||
    , privkey: opts.pems.privkey  // Ignore. string PEM, useful if you have to create bundle.pem
 | 
					 | 
				
			||||||
    };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // Ignore
 | 
					 | 
				
			||||||
    // Just implementation specific details (writing lots of combinatons of files)
 | 
					 | 
				
			||||||
    var liveDir = opts.liveDir || path.join(opts.configDir, 'live', opts.subject);
 | 
					 | 
				
			||||||
    var certPath = opts.certPath || path.join(liveDir, 'cert.pem');
 | 
					 | 
				
			||||||
    var fullchainPath = opts.fullchainPath || path.join(liveDir, 'fullchain.pem');
 | 
					 | 
				
			||||||
    var chainPath = opts.chainPath || path.join(liveDir, 'chain.pem');
 | 
					 | 
				
			||||||
    var bundlePath = opts.bundlePath || path.join(liveDir, 'bundle.pem');
 | 
					 | 
				
			||||||
    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(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(tameWild(bundlePath, opts.subject), bundlePem, 'ascii')
 | 
					 | 
				
			||||||
            ]);
 | 
					 | 
				
			||||||
          });
 | 
					 | 
				
			||||||
        });
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    }).then(function () {
 | 
					 | 
				
			||||||
      // That's your job: return null
 | 
					 | 
				
			||||||
      return null;
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return store;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
///////////////////////////////////////////////////////////////////////////////
 | 
					///////////////////////////////////////////////////////////////////////////////
 | 
				
			||||||
@ -305,56 +84,36 @@ module.exports.create = function (config) {
 | 
				
			|||||||
//
 | 
					//
 | 
				
			||||||
// Everything below this line is just implementation specific
 | 
					// Everything below this line is just implementation specific
 | 
				
			||||||
var defaults = {
 | 
					var defaults = {
 | 
				
			||||||
  configDir: path.join(os.homedir(), 'acme', 'etc')
 | 
						basePath: path.join(os.homedir(), '.config', 'greenlock'),
 | 
				
			||||||
 | 
					
 | 
				
			||||||
, accountsDir: path.join(':configDir', 'accounts', ':serverDir')
 | 
						accountsDir: path.join(':basePath', 'accounts', ':directoryUrl'),
 | 
				
			||||||
, serverDirGet: function (copy) {
 | 
						serverDirGet: function(copy) {
 | 
				
			||||||
    return (copy.server || '').replace('https://', '').replace(/(\/)$/, '').replace(/\//g, path.sep);
 | 
							return (copy.directoryUrl || copy.server || '')
 | 
				
			||||||
  }
 | 
								.replace('https://', '')
 | 
				
			||||||
, privkeyPath: path.join(':configDir', 'live', ':hostname', 'privkey.pem')
 | 
								.replace(/(\/)$/, '')
 | 
				
			||||||
, fullchainPath: path.join(':configDir', 'live', ':hostname', 'fullchain.pem')
 | 
								.replace(/\//g, path.sep);
 | 
				
			||||||
, certPath: path.join(':configDir', 'live', ':hostname', 'cert.pem')
 | 
						},
 | 
				
			||||||
, chainPath: path.join(':configDir', 'live', ':hostname', 'chain.pem')
 | 
					  privkeyPath: path.join(':basePath', ':env', ':subject', 'privkey.pem'),
 | 
				
			||||||
, bundlePath: path.join(':configDir', 'live', ':hostname', 'bundle.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')
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					defaults.configDir = defaults.basePath;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
function mergeOptions(configs) {
 | 
					function mergeOptions(configs) {
 | 
				
			||||||
  if (!configs.domainKeyPath) {
 | 
						if (!configs.serverKeyPath) {
 | 
				
			||||||
    configs.domainKeyPath = configs.privkeyPath || defaults.privkeyPath;
 | 
							configs.serverKeyPath =
 | 
				
			||||||
  }
 | 
								configs.domainKeyPath ||
 | 
				
			||||||
 | 
								configs.privkeyPath ||
 | 
				
			||||||
 | 
								defaults.privkeyPath;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Object.keys(defaults).forEach(function (key) {
 | 
						Object.keys(defaults).forEach(function(key) {
 | 
				
			||||||
    if (!configs[key]) {
 | 
							if (!configs[key]) {
 | 
				
			||||||
      configs[key] = defaults[key];
 | 
								configs[key] = defaults[key];
 | 
				
			||||||
    }
 | 
							}
 | 
				
			||||||
  });
 | 
						});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return configs;
 | 
						return 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);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
function getPromise() {
 | 
					 | 
				
			||||||
  var util = require('util');
 | 
					 | 
				
			||||||
  var PromiseA;
 | 
					 | 
				
			||||||
  if (util.promisify && global.Promise) {
 | 
					 | 
				
			||||||
    PromiseA = global.Promise;
 | 
					 | 
				
			||||||
    PromiseA.promisify = util.promisify;
 | 
					 | 
				
			||||||
  } else {
 | 
					 | 
				
			||||||
    try {
 | 
					 | 
				
			||||||
      PromiseA = require('bluebird');
 | 
					 | 
				
			||||||
    } catch(e) {
 | 
					 | 
				
			||||||
      console.error("Your version of node is missing Promise. Please run `npm install --save bluebird` in your project to fix");
 | 
					 | 
				
			||||||
      process.exit(10);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return PromiseA;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										22
									
								
								promise.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								promise.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function getPromise() {
 | 
				
			||||||
 | 
						var util = require('util');
 | 
				
			||||||
 | 
						var PromiseA;
 | 
				
			||||||
 | 
						if (util.promisify && global.Promise) {
 | 
				
			||||||
 | 
							PromiseA = global.Promise;
 | 
				
			||||||
 | 
							PromiseA.promisify = util.promisify;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							try {
 | 
				
			||||||
 | 
								PromiseA = require('bluebird');
 | 
				
			||||||
 | 
							} catch (e) {
 | 
				
			||||||
 | 
								console.error(
 | 
				
			||||||
 | 
									'Your version of node is missing Promise. Please run `npm install --save bluebird` in your project to fix'
 | 
				
			||||||
 | 
								);
 | 
				
			||||||
 | 
								process.exit(10);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return PromiseA;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = getPromise();
 | 
				
			||||||
							
								
								
									
										46
									
								
								utils.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								utils.js
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
				
			|||||||
 | 
					'use strict';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var U = module.exports;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// because not all file systems like '*' in a name (and they're scary)
 | 
				
			||||||
 | 
					U._tameWild = function tameWild(pathname, wild) {
 | 
				
			||||||
 | 
						if (!wild) {
 | 
				
			||||||
 | 
							return pathname;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var tame = wild.replace(/\*/g, '_');
 | 
				
			||||||
 | 
						return pathname.replace(wild, tame);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					U._tpl = function tpl(store, opts, str) {
 | 
				
			||||||
 | 
						var server = ['directoryUrl', 'serverDir', 'server'];
 | 
				
			||||||
 | 
						var env = ['env', 'directoryUrl'];
 | 
				
			||||||
 | 
						[
 | 
				
			||||||
 | 
							['basePath', 'configDir'],
 | 
				
			||||||
 | 
							server,
 | 
				
			||||||
 | 
							['subject', 'hostname', 'domain'],
 | 
				
			||||||
 | 
							env
 | 
				
			||||||
 | 
						].forEach(function(group) {
 | 
				
			||||||
 | 
							group.forEach(function(tmpl) {
 | 
				
			||||||
 | 
								group.forEach(function(key) {
 | 
				
			||||||
 | 
									var item = store.options[key] || opts[key] || '';
 | 
				
			||||||
 | 
									if ('directoryUrl' === key) {
 | 
				
			||||||
 | 
										item = item.replace(/^https?:\/\//i, '');
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if ('env' === tmpl) {
 | 
				
			||||||
 | 
										if (/staging/.test(item)) {
 | 
				
			||||||
 | 
											item = 'staging';
 | 
				
			||||||
 | 
										} else if (/acme-v02/.test(item)) {
 | 
				
			||||||
 | 
											item = 'live';
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											// item = item;
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if (-1 === str.indexOf(':' + tmpl)) {
 | 
				
			||||||
 | 
										return;
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									str = str.replace(':' + tmpl, item);
 | 
				
			||||||
 | 
								});
 | 
				
			||||||
 | 
							});
 | 
				
			||||||
 | 
						});
 | 
				
			||||||
 | 
						return str;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user