diff --git a/clean.js b/clean.js index 8065381..5341ac5 100644 --- a/clean.js +++ b/clean.js @@ -1,12 +1,11 @@ -console.log('Cleaning the project.'); -console.log('Cleaning the project.'); +console.log('Emptying the bucket.'); require('dotenv').config(); var accessKeyId = process.env.AWS_ACCESS_KEY_ID - secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY - regionName = process.env.AWS_BUCKET_REGION - bucketName = process.env.AWS_BUCKET_NAME +secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY +regionName = process.env.AWS_BUCKET_REGION +bucketName = process.env.AWS_BUCKET_NAME var AWS = require('aws-sdk'); AWS.config.setPromisesDependency(Promise); @@ -20,7 +19,7 @@ AWS.config.update({ const s3 = new AWS.S3({ apiVersion: '2006-03-01' }); -s3.listObjects({ Bucket: bucketName }).promise().then(function(data){ +s3.listObjects({ Bucket: bucketName }).promise().then((data) => { if (data.Contents.length <= 0) { console.log('Your bucket is already empty :)'); @@ -29,19 +28,19 @@ s3.listObjects({ Bucket: bucketName }).promise().then(function(data){ var objectKeys = []; - for(let i = 0; i < data.Contents.length; i++){ + for (let i = 0; i < data.Contents.length; i++) { objectKeys.push({ Key: data.Contents[i].Key }) - } - - s3.deleteObjects({ Delete: { Objects: objectKeys }, Bucket: bucketName }).promise().then(function (data) { + } + + s3.deleteObjects({ Delete: { Objects: objectKeys }, Bucket: bucketName }).promise().then((data) => { console.log('Your bucket was emptied :)'); - }).catch( function(err) { + }).catch((err) => { console.error(err.message); throw err; }); -}).catch( function(err) { +}).catch((err) => { console.error(err.message); throw err; }); \ No newline at end of file diff --git a/index.js b/index.js new file mode 100644 index 0000000..c0e47df --- /dev/null +++ b/index.js @@ -0,0 +1,248 @@ + +//////////////////////////// +// File names for support // +//////////////////////////// + +const fileNames = { + privkey: { + pem: 'privkey.pem' + , jwk: 'privkey.jwk' + } + , cert: 'cert.pem' + , chain: 'chain.pem' + , fullchain: 'fullchain.pem' + , bundle: 'bundle.pem' +} + +/////////////////////////// +// Setup the environment // +/////////////////////////// + +var path = require('path'); +var Promise = require('bluebird') + +const AWS = require('aws-sdk'); +AWS.config.setPromisesDependency(Promise); + +const defaultOptions = { + accessKeyId: null + , secretAccessKey: null + , bucketName: null + , bucketRegion: null + , accountsDir: 'accounts/' + , configDir: 'acme/' +} + +const s3 = new AWS.S3({ apiVersion: '2006-03-01' }); + +module.exports.create = (createOptions) => { + + const options = Object.assign({}, defaultOptions, createOptions); + + AWS.config.update({ + region: options.bucketRegion + , credentials: new AWS.Credentials({ + accessKeyId: options.accessKeyId + , secretAccessKey: options.secretAccessKey + }) + }); + + const handlers = { + certificates: { + check: (opts) => { + + var id = opts.certificate && opts.certificate.id || opts.subject; + console.log('certificates.check for', opts.subject); + + var privkeyPath = certificatesPath(options, id, fileNames.privkey.pem); + var certPath = certificatesPath(options, id, fileNames.cert); + var chainPath = certificatesPath(options, id, fileNames.chain); + + return Promise.all([ + s3.getObject({ Key: privkeyPath, Bucket: options.bucketName }).promise().then((data) => { + console.log('Successfully retrieved certificate privkey.pem'); + return data.Body.toString(); + }).catch((err) => { + console.error('There was an error retrieving your certificate privkey.pem:', err.message); + throw err; + }), + s3.getObject({ Key: certPath, Bucket: options.bucketName }).promise().then((data) => { + console.log('Successfully retrieved certificate cert.pem'); + return data.Body.toString(); + }).catch((err) => { + console.error('There was an error retrieving your certificate cert.pem:', err.message); + throw err; + }), + s3.getObject({ Key: chainPath, Bucket: options.bucketName }).promise().then((data) => { + console.log('Successfully retrieved certificate chain.pem'); + return data.Body.toString(); + }).catch((err) => { + console.error('There was an error retrieving your certificate chain.pem:', err.message); + throw err; + }) + ]).then((values) => { + // console.log('Promise.all(values):', values); + + return { + privkey: values[0] + , cert: values[1] + , chain: values[2] + } + }).catch((err) => { + console.error('There was an error checking the ceritifcates:', err.message); + return null; + }); + }, + checkKeypair: (opts) => { + console.log('certificates.checkKeypair for', opts.subject); + + id = opts.certificate.kid || opts.certificate.id || opts.subject; + + pemKeyPath = certificatesPath(options, id, fileNames.privkey.pem); + jwkKeyPath = certificatesPath(options, id, fileNames.privkey.jwk); + + return s3.getObject({ Key: pemKeyPath, Bucket: options.bucketName }).promise().then((data) => { + console.log('Successfully retrieved certificate PEM keypair.'); + return { + privateKeyPem: data.Body.toString() + } + }).catch((err) => { + console.error('There was an error retrieving your certificate PEM keypair:', err.message); + return null + }); + + }, + setKeypair: (opts) => { + id = opts.certificate.kid || opts.certificate.id || opts.subject; + console.log('certificates.setKeypair for', id); + + pemKeyPath = certificatesPath(options, id, fileNames.privkey.pem); + jwkKeyPath = certificatesPath(options, id, fileNames.privkey.jwk); + + return s3.putObject({ Key: pemKeyPath, Body: opts.keypair.privateKeyPem, Bucket: options.bucketName }).promise().then((data) => { + console.log('Successfully set the PEM privateKey.'); + return null; + }).catch((err) => { + console.error('There was an error setting your PEM privateKey:', err.message); + throw err; + }); + }, + set: (opts) => { + console.log('certificates.set for ', opts.subject); + + var pems = { + cert: opts.pems.cert + , chain: opts.pems.chain + , privkey: opts.pems.privkey + } + + var certPath = certificatesPath(options, opts.subject, fileNames.cert); + var chainPath = certificatesPath(options, opts.subject, fileNames.chain); + var fullchainPath = certificatesPath(options, opts.subject, fileNames.fullchain); + var bundlePath = certificatesPath(options, opts.subject, fileNames.bundle); + + 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 Promise.all([ + s3.putObject({ Key: certPath, Body: pems.cert, Bucket: options.bucketName }).promise().then((data) => { + console.log('Successfully set', certPath); + }).catch((err) => { + console.error('There was an error setting cert.pem:', err.message); + throw err; + }), + s3.putObject({ Key: chainPath, Body: pems.chain, Bucket: options.bucketName }).promise().then((data) => { + console.log('Successfully set', chainPath); + }).catch((err) => { + console.error('There was an error setting chain.pem:', err.message); + throw err; + }), + s3.putObject({ Key: fullchainPath, Body: fullchainPem, Bucket: options.bucketName }).promise().then((data) => { + console.log('Successfully set', fullchainPath); + }).catch((err) => { + console.error('There was an error setting fullchain.pem:', err.message); + throw err; + }), + s3.putObject({ Key: bundlePath, Body: bundlePem, Bucket: options.bucketName }).promise().then((data) => { + console.log('Successfully set', bundlePath); + }).catch((err) => { + console.error('There was an error setting bundle.pem:', err.message); + throw err; + }) + ]).then((values) => { + return null; + }).catch((err) => { + console.error('There was an error setting the certificates:', err.message); + throw err; + }); + } + }, + accounts: { + check: (opts) => { + console.log("accounts.check", opts.account.id); + // console.log(opts); + }, + checkKeypair: (opts) => { + var id = opts.account.id || opts.email || 'single-user'; + console.log('accounts.checkKeypair for', id); + + key = accountsPath(options, id) + + return s3.getObject({ Key: key, Bucket: options.bucketName }).promise().then((data) => { + console.log('Successfully retrieved account keypair.'); + var keypair = JSON.parse(data.Body.toString()); + return { + privateKeyPem: keypair.privateKeyPem // string PEM private key + , privateKeyJwk: keypair.privateKeyJwk // object JWK private key + }; + }).catch((err) => { + console.error('There was an error retrieving your account keypair:', err.message); + return null; + }); + + }, + setKeypair: (opts) => { + console.log('accounts.setKeypair for', opts.account); + + var id = opts.account.id || opts.email || 'single-user'; + key = accountsPath(options, id) + + var body = JSON.stringify({ + privateKeyPem: opts.keypair.privateKeyPem // string PEM + , privateKeyJwk: opts.keypair.privateKeyJwk // object JWK + }); + + return s3.putObject({ Key: key, Body: body, Bucket: options.bucketName }).promise().then((data) => { + console.log('Successfully created account keypair.'); + return null; + }).catch((err) => { + console.error('There was an error creating account keypair:', err.message); + return null; + }); + + }, + set: (opts) => { + console.log("accounts.set"); + } + } + } + + return handlers; + +} + +const certificatesPath = (options, id, fileName) => { + var filePath = path.join(options.configDir, 'live', tameWild(id), fileName); + console.log('filePath:', filePath); + return filePath; +} + +const accountsPath = (options, id) => { + var filePath = path.join(options.configDir, options.accountsDir, tameWild(id) + '.json'); + console.log('filePath:', filePath); + return filePath; +} + +const tameWild = (wild) => { + return wild.replace(/\*/g, '_'); +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a8a5e88..43183a0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,6 +25,11 @@ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.0.tgz", "integrity": "sha512-ccav/yGvoa80BQDljCxsmmQ3Xvx60/UpBIij5QN21W3wBi/hhIC9OoO+KLpu9IJTS9j4DRVJ3aDDF9cMSoa2lw==" }, + "bluebird": { + "version": "3.5.4", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.5.4.tgz", + "integrity": "sha512-FG+nFEZChJrbQ9tIccIfZJBz3J7mLrAhxakAbnrJWn8d7aKOC+LWifa0G+p4ZqKp4y13T7juYvdhq9NzKdsrjw==" + }, "buffer": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.1.tgz", @@ -46,6 +51,12 @@ "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", "integrity": "sha1-nr23Y1rQmccNzEwqH1AEKI6L2SQ=" }, + "greenlock-store-test": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/greenlock-store-test/-/greenlock-store-test-3.0.2.tgz", + "integrity": "sha512-OckCJZT1rxG0o4dI/ZGH8BqudIFWwhuOpJkh7HquCJSGM3QMylWsMmfl55xtSuSWt6vYDSOc2EfFbR9mhEw4xw==", + "dev": true + }, "ieee754": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.8.tgz", diff --git a/package.json b/package.json index 16f4a61..73d1e66 100644 --- a/package.json +++ b/package.json @@ -27,9 +27,11 @@ }, "homepage": "https://github.com/cderche/greenlock-storage-s3#readme", "dependencies": { - "aws-sdk": "^2.451.0" + "aws-sdk": "^2.451.0", + "bluebird": "^3.5.4" }, "devDependencies": { - "dotenv": "^8.0.0" + "dotenv": "^8.0.0", + "greenlock-store-test": "^3.0.2" } } diff --git a/test.js b/test.js index 3b30854..efc8465 100644 --- a/test.js +++ b/test.js @@ -1 +1,27 @@ console.log('Testing the strategy.'); + +require('dotenv').config(); + +let accessKeyId = process.env.AWS_ACCESS_KEY_ID + secretAccessKey = process.env.AWS_SECRET_ACCESS_KEY + regionName = process.env.AWS_BUCKET_REGION + bucketName = process.env.AWS_BUCKET_NAME + +let tester = require('greenlock-store-test'); + +let store = require('./index').create({ + accessKeyId: accessKeyId + , secretAccessKey: secretAccessKey + , regionName: regionName + , bucketName: bucketName + , configDir: 'acme/' + , accountsDir: 'accounts/' +}); + +// All of these tests can pass locally, standalone without any ACME integration. +tester.test(store).then(() => { + console.info("Test successfully completed"); +}).catch((err) => { + console.error(err.message); + throw err; +}); \ No newline at end of file