From d07dffd46e054baf164e6eb6cd6a0b928759a410 Mon Sep 17 00:00:00 2001 From: AJ ONeal Date: Tue, 30 Jul 2019 23:37:03 -0600 Subject: [PATCH] v3.0.0: Webroot Authentication for Let's Encrypt --- README.md | 33 ++++++++++++--------- example.env | 4 +-- lib/index.js | 75 +++++++++++++++++++++++++++++++++++++++-------- package-lock.json | 34 +++++++++++++++++++++ package.json | 14 +++++---- test.js | 14 +++++++++ 6 files changed, 140 insertions(+), 34 deletions(-) create mode 100644 package-lock.json diff --git a/README.md b/README.md index db0eb8b..4b4bc70 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# [acme-http-01-{{servicename}}.js](https://git.rootprojects.org/root/acme-http-01-{{servicename}}.js) | a [Root](https://rootprojects.org/) project +# [acme-http-01-webroot.js](https://git.rootprojects.org/root/acme-http-01-webroot.js) | a [Root](https://rootprojects.org/) project -{{ Service Title }} Storage + Let's Encrypt for Node.js - ACME http-01 challenges w/ ACME.js and Greenlock.js +Webroot Authentication + Let's Encrypt for Node.js - ACME http-01 challenges w/ ACME.js and Greenlock.js Handles ACME http-01 challenges. Compatible with ACME.js and Greenlock.js. Passes acme-http-01-test. @@ -8,7 +8,10 @@ Handles ACME http-01 challenges. Compatible with ACME.js and Greenlock.js. Passe - Compatible - Let’s Encrypt v2.1 / ACME draft 18 (2019) - - {{ Service Title }} API + - Works with your web root + - Apache + - Nginx + - Lighttpd - ACME.js, Greenlock.js, and others - Quality - node v6 compatible VanillaJS @@ -18,22 +21,24 @@ Handles ACME http-01 challenges. Compatible with ACME.js and Greenlock.js. Passe # Install ```js -npm install --save acme-http-01-{{servicename}} +npm install --save acme-http-01-webroot ``` -{{ Service Title }} Token: - -- Login to your account at: {{ Service URL }} -- {{ Instructions to generate token }} - # Usage First you create an instance with your credentials: ```js -var http01 = require('acme-http-01-{{servicename}}').create({ - baseUrl: '{{ api url }}', // default - token: 'xxxx' +var http01 = require('acme-http-01-webroot').create({ + webroot: '~/.local/tmp/acme-challenge' // default +}); +``` + +Template example: + +```js +var http01 = require('acme-http-01-webroot').create({ + webroot: '/srv/www/{domain}/.well-known/acme-challenge' }); ``` @@ -73,7 +78,7 @@ There are only 4 methods: ```js http01 .set({ - altname: 'foo.example.co.uk', + identifier: { value: 'foo.example.co.uk' }, token: 'xxxx' keyAuthorization: 'xxxx.yyyy' }) @@ -103,7 +108,7 @@ See AUTHORS for contact info. # Legal -[acme-http-01-{{servicename}}.js](https://git.coolaj86.com/coolaj86/acme-http-01-{{servicename}}.js) | MPL-2.0 | [Terms of Use](https://therootcompany.com/legal/#terms) | [Privacy Policy](https://therootcompany.com/legal/#privacy) +[acme-http-01-webroot.js](https://git.coolaj86.com/coolaj86/acme-http-01-webroot.js) | MPL-2.0 | [Terms of Use](https://therootcompany.com/legal/#terms) | [Privacy Policy](https://therootcompany.com/legal/#privacy) Copyright 2019 AJ ONeal Copyright 2019 The Root Group LLC diff --git a/example.env b/example.env index 48ceb39..2b2242b 100644 --- a/example.env +++ b/example.env @@ -1,2 +1,2 @@ -ZONE=example.co.uk -TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +RECORD=example.co.uk +WEBROOT=/tmp/acme-challenge diff --git a/lib/index.js b/lib/index.js index ee756c9..3f2fe38 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,31 +1,82 @@ 'use strict'; -var request; -var defaults = {}; +//var request; +var promisify = require('util').promisify; +var os = require('os'); +var fs = require('fs'); +var writeFile = promisify(fs.writeFile); +var readFile = promisify(fs.readFile); +var unlink = promisify(fs.unlink); +var mkdirp = promisify(require('@root/mkdirp')); +var path = require('path'); + +var defaults = { + webroot: path.join(require('os').tmpdir(), 'acme-challenge') +}; module.exports.create = function(config) { + var webroot = config.webroot || config.webrootPath || defaults.webroot; + + function tpl(str, ch) { + return str + .replace(/\s*{+\s*domain\s*}+\s*/gi, ch.identifier.value) + .replace(/^~/, os.homedir()); + } + return { + // exposed to make testable + _tpl: tpl, + init: function(opts) { - request = opts.request; + //request = opts.request; return null; }, set: function(data) { - var ch = data.challenge; - // console.info('Add Key Auth URL', data); - throw Error('setting key authorization not implemented'); - }, + // console.log('Add Key Auth URL', data); - remove: function(data) { var ch = data.challenge; - // console.info('Remove Key Auth URL', data); - throw Error('removing key authorization not implemented'); + var pathname = tpl(webroot, ch); + + return mkdirp(pathname) + .then(function() { + return writeFile( + path.join(pathname, ch.token), + ch.keyAuthorization + ); + }) + .then(function() { + return null; + }); }, get: function(data) { + // console.log('List Key Auth URL', data); + var ch = data.challenge; - // console.info('List Key Auth URL', data); - throw Error('retrieving key authorization not implemented'); + var pathname = tpl(webroot, ch); + + return readFile(path.join(pathname, ch.token), 'utf8') + .then(function(keyAuth) { + return { keyAuthorization: keyAuth }; + }) + .catch(function(err) { + if ('ENOENT' !== err.code) { + throw err; + } + return null; + }); + }, + + remove: function(data) { + // console.log('Remove Key Auth URL', data); + + var ch = data.challenge; + var pathname = tpl(webroot, ch); + + return unlink(path.join(pathname, ch.token)).then(function() { + return null; + }); } }; }; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..0ce2ca1 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,34 @@ +{ + "name": "acme-http-01-webroot", + "version": "3.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@root/mkdirp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@root/mkdirp/-/mkdirp-1.0.0.tgz", + "integrity": "sha512-hxGAYUx5029VggfG+U9naAhQkoMSXtOeXtbql97m3Hi6/sQSRL/4khKZPyOF6w11glyCOU38WCNLu9nUcSjOfA==" + }, + "@root/request": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@root/request/-/request-1.3.11.tgz", + "integrity": "sha512-3a4Eeghcjsfe6zh7EJ+ni1l8OK9Fz2wL1OjP4UCa0YdvtH39kdXB9RGWuzyNv7dZi0+Ffkc83KfH0WbPMiuJFw==", + "dev": true + }, + "acme-challenge-test": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/acme-challenge-test/-/acme-challenge-test-3.3.2.tgz", + "integrity": "sha512-0AbMcaON20wpI5vzFDAqwcv2VerY4xIlNCqX0w1xEJUIu/EQtQNmkje+rKNuy2TUl2KBMdIaR6YBbJUdaEiC4w==", + "dev": true, + "requires": { + "@root/request": "^1.3.11" + } + }, + "dotenv": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.0.0.tgz", + "integrity": "sha512-30xVGqjLjiUOArT4+M5q9sYdvuR4riM6yK9wMcas9Vbp6zZa+ocC9dp6QoftuhTPhFAiLK/0C5Ni2nou/Bk8lg==", + "dev": true + } + } +} diff --git a/package.json b/package.json index 85054f4..1e9e742 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { - "name": "acme-http-01-{{servicename}}", - "version": "0.0.1", - "description": "{{ Service Title }} + Let's Encrypt for Node.js - ACME http-01 challenges w/ ACME.js and Greenlock.js", + "name": "acme-http-01-webroot", + "version": "3.0.0", + "description": "Webroot Authentication + Let's Encrypt for Node.js - ACME http-01 challenges w/ ACME.js and Greenlock.js", "main": "index.js", "files": [ "lib", @@ -12,10 +12,10 @@ }, "repository": { "type": "git", - "url": "https://git.coolaj86.com/coolaj86/acme-http-01-{{servicename}}.js.git" + "url": "https://git.coolaj86.com/coolaj86/acme-http-01-webroot.js.git" }, "keywords": [ - "{{servicename}}", + "webroot", "storage", "http-01", "letsencrypt", @@ -31,5 +31,7 @@ "acme-challenge-test": "^3.3.2", "dotenv": "^8.0.0" }, - "dependencies": {} + "dependencies": { + "@root/mkdirp": "^1.0.0" + } } diff --git a/test.js b/test.js index e40e367..b3f1291 100755 --- a/test.js +++ b/test.js @@ -24,3 +24,17 @@ tester console.error(e.message); console.error(e.stack); }); + +var ch = { identifier: { value: 'foo.example.co.uk' } }; +//var ch = { domain: 'foo.example.co.uk' }; +var homeish = challenger._tpl('~/.local/tmp/acme-challenge', ch); +console.log(homeish); +if ('/' !== homeish[0] || /~/.test(homeish)) { + throw new Error('Not the expected value for home tmp: ' + homeish); +} + +var srvish = challenger._tpl('/srv/{domain}/.well-known/acme-challenge', ch); +console.log(srvish); +if ('/' !== srvish[0] || /~/.test(srvish)) { + throw new Error('Not the expected value for srv template: ' + srvish); +}