From 6dabaebda727582e3e8ada14386d6a74fa2f2f36 Mon Sep 17 00:00:00 2001 From: nyaundi brian Date: Mon, 10 Jun 2019 16:54:39 +0300 Subject: [PATCH] initial commit --- README.md | 10 ++++ index.js | 3 ++ lib/index.js | 150 +++++++++++++++++++++++++++++++++++++++++++++++++++ package.json | 32 +++++++++++ test.js | 42 +++++++++++++++ 5 files changed, 237 insertions(+) create mode 100644 README.md create mode 100644 index.js create mode 100644 lib/index.js create mode 100644 package.json create mode 100644 test.js diff --git a/README.md b/README.md new file mode 100644 index 0000000..916d7c0 --- /dev/null +++ b/README.md @@ -0,0 +1,10 @@ +# [acme-dns-01-vultr](https://git.rootprojects.org/root/acme-dns-01-vultr) | a [Root](https://rootrpojects.org) project + +Vultr DNS for Let's Encrypt / ACME dns-01 challenges with ACME.js and Greenlock.js (Node.js). + +# Tests + +``` +# node ./test.js domain-zone api-token +node ./test.js example.com xxxxxx +``` diff --git a/index.js b/index.js new file mode 100644 index 0000000..647221a --- /dev/null +++ b/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./lib/index.js'); diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..2b6de4f --- /dev/null +++ b/lib/index.js @@ -0,0 +1,150 @@ +'use strict'; + +var request = require('@root/request'); +request = require('util').promisify(request); + +var defaults = { + baseUrl: 'https://api.vultr.com/' +}; + +module.exports.create = function(config) { + var baseUrl = config.baseUrl || defaults.baseUrl; + var apiKey = config.apiKey; + return { + set: function(data) { + var ch = data.challenge; + var domainname = ch.identifier.value; + var zone = domainname; + var dnsPrefix = ch.dnsHost.replace(new RegExp('.' + zone + '$'), ''); + var txt = ch.dnsAuthorization; + // If the domain to be verified is + var url = baseUrl + 'v1/dns/create_record'; + + console.log('adding txt', data); + return request({ + method: 'POST', + url: url, + headers: { + 'API-Key': apiKey + }, + form: { + type: 'TXT', + name: dnsPrefix, + data: '"'+txt+'"', // vultr requires the TXT record wraped in quotes + domain: config.domain, + ttl: 300 + } + }).then(function(resp) { + if(resp.statusCode==200){ + return true; + }else{ + console.log(resp.statusCode); + console.log(resp.body); + throw new Error('record did not set. check subdomain, api key, etc'); + } + }); + }, + remove: function(data) { + var ch = data.challenge; + var domainname = data.challenge.altname; + var zone = domainname; + + var url = baseUrl + 'v1/dns/records'; + + return request({ + method: 'GET', + url: url+'?domain='+config.domain, + // PROBLEM (fixed): Remember to set json: true (not need to JSON.parse) + json: true, + headers: { + 'API-Key': apiKey + } + }).then(function(resp) { + if(resp.statusCode==200){ + resp = resp.body; + console.log(resp); + var entries = + resp.filter(function(x) { + return x.type === 'TXT'; + }); + + var entry = entries.filter(function(x) { + // vultr wraps the TXT record in double quotes + return x.data.substring(1, x.data.length - 1) === ch.dnsAuthorization; + })[0]; + + if (entry) { + return entry['RECORDID']; + } else { + throw new Error( + "Couldn't remove record. check subdomain, api key, etc" + ); + } + }else{ + throw new Error("record did not set. check subdomain, api key, etc"); + } + + }) + .then(function(recordId) { + var domainname = data.challenge.altname; + var zone = domainname; + var url = baseUrl + 'v1/dns/delete_record'; + + return request({ + method: 'POST', + url: url, + headers: { + 'API-Key': apiKey + }, + form: { + domain: config.domain, + 'RECORDID': recordId + } + }).then(function(resp) { + if(resp.statusCode==200){ + return true; + }else{ + console.log(resp.statusCode); + console.log(resp.body); + throw new Error("record did not remove. check subdomain, api key, etc"); + } + }); + }); + }, + get: function(data) { + var ch = data.challenge; + var domainname = data.challenge.altname; + var url = baseUrl + 'v1/dns/records'; + console.log('getting txt', data); + + // Digital ocean provides the api to fetch records by ID. Since we do not have id, we fetch all the records, + // filter the required TXT record + + return request({ + method: 'GET', + url: url+'?domain='+config.domain, + json: true, + headers: { + 'API-Key': apiKey + } + }).then(function(resp) { + resp = resp.body; + + var entries = resp.filter(function(x) { + return x.type === 'TXT'; + }); + + var entry = entries.filter(function(x) { + // vultr wraps the TXT record in double quotes + return x.data.substring(1, x.data.length - 1) === ch.dnsAuthorization; + })[0]; + + if (entry) { + return { dnsAuthorization: entry.data.substring(1, entry.data.length - 1)}; + } else { + return null; + } + }); + } + }; +}; diff --git a/package.json b/package.json new file mode 100644 index 0000000..4c34f75 --- /dev/null +++ b/package.json @@ -0,0 +1,32 @@ +{ + "name": "acme-dns-01-vultr", + "version": "3.0.0", + "description": "Vultr DNS for Let's Encrypt / ACME dns-01 challenges with ACME.js and Greenlock.js", + "main": "index.js", + "scripts": { + "test": "node ./test.js" + }, + "repository": { + "type": "git", + "url": "https://git.rootprojects.org/root/acme-dns-01-vultr.git" + }, + "keywords": [ + "vultr", + "dns", + "dns-01", + "letsencrypt", + "acme", + "greenlock" + ], + "author": "AJ ONeal (https://coolaj86.com/)", + "contributors": [ + "Nyaundi Brian " + ], + "license": "MPL-2.0", + "dependencies": { + "@root/request": "^1.3.11" + }, + "devDependencies": { + "acme-challenge-test": "^3.0.4" + } +} diff --git a/test.js b/test.js new file mode 100644 index 0000000..fe9b55d --- /dev/null +++ b/test.js @@ -0,0 +1,42 @@ +#!/usr/bin/env node +'use strict'; + +// https://git.coolaj86.com/coolaj86/acme-challenge-test.js +var tester = require('acme-challenge-test'); + +// Usage: node ./test.js example.com xxxxxxxxx +var zone = process.argv[2]; +var challenger = require('./index.js').create({ + apiKey: process.argv[3], + domain:zone +}); + +// The dry-run tests can pass on, literally, 'example.com' +// but the integration tests require that you have control over the domain +var domain = zone; + +tester + .test('dns-01', domain, challenger) + .then(function() { + console.info('PASS', domain); + ///* + domain = 'foo.' + zone; + + return tester + .test('dns-01', domain, challenger) + .then(function() { + console.info('PASS', domain); + }) + .then(function() { + domain = '*.foo.' + zone; + + return tester.test('dns-01', domain, challenger).then(function() { + console.info('PASS', domain); + }); + }); + //*/ + }) + .catch(function(e) { + console.error(e.message); + console.error(e.stack); + });