From 9a54d2e3bc78e88f42eb8987228b0297bd20e8a3 Mon Sep 17 00:00:00 2001 From: Hitesh Date: Sun, 18 Aug 2019 22:27:42 -0700 Subject: [PATCH 1/2] all integration tests passed --- lib/index.js | 326 ++++++++++++++++++++++++++++++++++++++++++++-- package-lock.json | 51 ++++++++ package.json | 7 +- test.js | 6 +- 4 files changed, 374 insertions(+), 16 deletions(-) create mode 100644 package-lock.json diff --git a/lib/index.js b/lib/index.js index e25856a..56182d4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,29 +1,329 @@ 'use strict'; -var request; -var defaults = {}; +var v4 = require('aws-signature-v4'); +var xml2js = require('xml2js'); +var parseString = xml2js.parseString; +parseString = require('util').promisify(parseString); +var builder = new xml2js.Builder(); + +var request = require('@root/request'); +//request.debug = false; + +var defaults = { + baseUri: '/2013-04-01/' +}; module.exports.create = function(config) { + var baseUri = defaults.baseUri.replace(/\/$/, ''); + var key = config.key; + var secret = config.secret; + + function api(method, path, body, query) { + var body = body || ''; + var query = query || {}; + var options = { + key: key, + secret: secret, + query: query + }; + + if(method === 'POST') { + options.headers = { 'Content-Type': 'application/xml' } + } + + var url = v4.createPresignedURL(method, 'route53.amazonaws.com', path, 'route53', body, options); + //console.log('url: ' + url + '\n'); + + var parameters = { + method: method, + url: url + }; + + if(method === 'POST') { + parameters.headers = { 'Content-Type': 'application/xml' } + } + + if(body) { + parameters.body = body; + } + + // console.log(method, path, query); + + return request(parameters).then(function(response) { + if (response.statusCode < 200 || response.statusCode >= 300) { + console.error(response.statusCode, options.url); + console.error(); + console.error('Request:'); + console.error(options); + console.error(); + console.error('Response:'); + console.error(response.body); + console.error(); + throw new Error( + 'Error response. Check token, baseUri, domains, etc.' + ); + } + return response; + }); + } + + /* + api('GET', baseUri + '/' + 'hostedzone').then(function(response){ + console.log('######## RESPONSE #########'); + + }); + */ + return { - init: function(opts) { - request = opts.request; + init: function(options) { + request = options.request; return null; }, - zones: function(data) { + zones: function(data) { //console.info('List Zones', data); - throw Error('listing zones not implemented'); + return api('GET', baseUri + '/' + 'hostedzone').then(function(response){ + return parseString(response.body).then(function(body){ + var zones = body.ListHostedZonesResponse.HostedZones[0].HostedZone.map(function(element) { + return element.Name[0].replace(/\.$/, ''); // all route 53 domains terminate with a dot + }); + + return zones; + }); + }); + }, set: function(data) { // console.info('Add TXT', data); - throw Error('setting TXT not implemented'); - }, - remove: function(data) { - // console.info('Remove TXT', data); - throw Error('removing TXT not implemented'); + // console.log('data: ' + JSON.stringify(data, null, 2)); + var challenge = data.challenge; + + if (!challenge.dnsZone) { + throw new Error('No matching zone for ' + challenge.dnsHost); + } + + return api('GET', baseUri + '/' + 'hostedzonesbyname', null, 'dnsname=' + challenge.dnsZone + '&maxitems=1') + .then(function(response){ + return parseString(response.body).then(function(body){ + return body; + }); + }) + .then(function(body){ + var zoneID = body.ListHostedZonesByNameResponse.HostedZones[0].HostedZone[0].Id[0]; + var xmlns = body.ListHostedZonesByNameResponse.$.xmlns; + + var parameters = { + 'ChangeResourceRecordSetsRequest': { + '$': { + 'xmlns': xmlns + }, + 'ChangeBatch': [ + { + 'Changes': [ + { + 'Change': [ + { + 'Action': [ + 'CREATE' + ], + 'ResourceRecordSet': [ + { + 'Type': [ + 'TXT' + ], + 'Name': [ + challenge.dnsHost // AWS requires FQDN + ], + 'ResourceRecords': [ + { + 'ResourceRecord': [ + { + 'Value': [ + '"' + challenge.dnsAuthorization + '"' // value must be surrounded by double quotes + ] + } + ] + } + ], + 'TTL': [ + 300 + ] + } + ] + + } + ] + } + ] + } + ] + } + }; + + // By default AWS creates record sets with simple routing policy, so duplicates are not allowed. + // A workaround is put the record sets in different weighted policies. + if(challenge.identifier.value.startsWith('foo')) { + if(challenge.wildcard) { + + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].Weight = [ + 1 + ]; + + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].SetIdentifier = [ + 'wildcard' + ]; + } else { + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].Weight = [ + 1 + ]; + + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].SetIdentifier = [ + 'subdomain' + ]; + } + } + + var xml = builder.buildObject(parameters); + //console.log('xml: ' + xml); + + return api('POST', baseUri + zoneID + '/rrset/', xml); + + }); + + }, get: function(data) { // console.info('List TXT', data); - throw Error('listing TXTs not implemented'); + var challenge = data.challenge; + + return api('GET', baseUri + '/' + 'hostedzonesbyname', null, 'dnsname=' + challenge.dnsZone + '&maxitems=1') + .then(function(response){ + return parseString(response.body).then(function(body){ + return body; + }); + }) + .then(function(body){ + var zoneID = body.ListHostedZonesByNameResponse.HostedZones[0].HostedZone[0].Id[0]; + // console.log('zoneID: ' + zoneID); + return zoneID; + }) + .then(function(zoneID){ + // GET /2013-04-01/hostedzone/Id/rrset?identifier=StartRecordIdentifier&maxitems=MaxItems&name=StartRecordName&type=StartRecordType HTTP/1.1 + + return api('GET', baseUri + '/' + zoneID + '/rrset', null, 'name=' + challenge.dnsPrefix + '.' + challenge.dnsZone + '.' + '&type=TXT') + .then(function(response){ + return parseString(response.body).then(function(body){ + if (body.ListResourceRecordSetsResponse.ResourceRecordSets[0] === '') return null; + + var record = body.ListResourceRecordSetsResponse.ResourceRecordSets[0].ResourceRecordSet.filter(function(element){ + return ( + element.ResourceRecords[0].ResourceRecord[0].Value[0].replace(/\"/g, '') === challenge.dnsAuthorization && + element.Name[0].includes(challenge.dnsPrefix) + ); + })[0]; + + if (record) { + return { dnsAuthorization: record.ResourceRecords[0].ResourceRecord[0].Value[0].replace(/\"/g, '') }; + } + + return null; + }); + }); + + }); + + }, + remove: function(data) { + // console.info('Remove TXT', data); + + var challenge = data.challenge; + + return api('GET', baseUri + '/' + 'hostedzonesbyname', null, 'dnsname=' + challenge.dnsZone + '&maxitems=1') + .then(function(response){ + return parseString(response.body).then(function(body){ + return body; + }); + }) + .then(function(body){ + var zoneID = body.ListHostedZonesByNameResponse.HostedZones[0].HostedZone[0].Id[0]; + var xmlns = body.ListHostedZonesByNameResponse.$.xmlns; + + var parameters = { + 'ChangeResourceRecordSetsRequest': { + '$': { + 'xmlns': xmlns + }, + 'ChangeBatch': [ + { + 'Changes': [ + { + 'Change': [ + { + 'Action': [ + 'DELETE' + ], + 'ResourceRecordSet': [ + { + 'Type': [ + 'TXT' + ], + 'Name': [ + challenge.dnsHost // AWS requires FQDN + ], + 'ResourceRecords': [ + { + 'ResourceRecord': [ + { + 'Value': [ + '"' + challenge.dnsAuthorization + '"' // value must be surrounded by double quotes + ] + } + ] + } + ], + 'TTL': [ + 300 + ] + } + ] + + } + ] + } + ] + } + ] + } + }; + + if(challenge.identifier.value.startsWith('foo')) { + if(challenge.wildcard) { + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].Weight = [ + 1 + ]; + + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].SetIdentifier = [ + 'wildcard' + ]; + } else { + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].Weight = [ + 1 + ]; + + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].SetIdentifier = [ + 'subdomain' + ]; + } + } + + var xml = builder.buildObject(parameters); + // console.log('\n', xml, '\n'); + + return api('POST', baseUri + zoneID + '/rrset/', xml); + + }); + } - }; + + } + }; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..c465eba --- /dev/null +++ b/package-lock.json @@ -0,0 +1,51 @@ +{ + "name": "acme-dns-01-route53", + "version": "0.0.1", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@root/request": { + "version": "1.3.11", + "resolved": "https://registry.npmjs.org/@root/request/-/request-1.3.11.tgz", + "integrity": "sha512-3a4Eeghcjsfe6zh7EJ+ni1l8OK9Fz2wL1OjP4UCa0YdvtH39kdXB9RGWuzyNv7dZi0+Ffkc83KfH0WbPMiuJFw==" + }, + "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==", + "requires": { + "@root/request": "^1.3.11" + } + }, + "aws-signature-v4": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/aws-signature-v4/-/aws-signature-v4-1.4.0.tgz", + "integrity": "sha512-OpL4svs8b7ENRoC0DJzwXW7JTDSp+PkYD1sOWT47CsDxHFtmDaydK9zkLwl4LXNHKXnlzbNCvbOwP7lqgFEE2Q==" + }, + "dotenv": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-8.1.0.tgz", + "integrity": "sha512-GUE3gqcDCaMltj2++g6bRQ5rBJWtkWTmqmD0fo1RnnMuUqHNCt2oTPeDnS9n6fKYvlhn7AeBkb38lymBtWBQdA==", + "dev": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + }, + "xml2js": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.4.19.tgz", + "integrity": "sha512-esZnJZJOiJR9wWKMyuvSE1y6Dq5LCuJanqhxslH2bxM6duahNZ+HMpCLhBQGZkbX6xRf8x1Y2eJlgt2q3qo49Q==", + "requires": { + "sax": ">=0.6.0", + "xmlbuilder": "~9.0.1" + } + }, + "xmlbuilder": { + "version": "9.0.7", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", + "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" + } + } +} diff --git a/package.json b/package.json index 1e9b7e1..a92e017 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,11 @@ "author": "AJ ONeal (https://coolaj86.com/)", "license": "MPL-2.0", "devDependencies": { - "dotenv": "^8.0.0" + "dotenv": "^8.1.0" + }, + "dependencies": { + "acme-challenge-test": "^3.3.2", + "aws-signature-v4": "^1.4.0", + "xml2js": "^0.4.19" } } diff --git a/test.js b/test.js index b794bdf..5192918 100755 --- a/test.js +++ b/test.js @@ -3,11 +3,13 @@ // See https://git.coolaj86.com/coolaj86/acme-challenge-test.js var tester = require('acme-challenge-test'); +require('dotenv').config(); -// Usage: node ./test.js example.com xxxxxxxxx +// Usage: node ./test.js example.com key secret var zone = process.argv[2] || process.env.ZONE; var challenger = require('./index.js').create({ - token: process.argv[3] || process.env.TOKEN + key: process.argv[3] || process.env.KEY, + secret: process.argv[4] || process.env.SECRET }); // The dry-run tests can pass on, literally, 'example.com' -- 2.38.5 From 1ec51c635c027ef38b78b368743955f230d39193 Mon Sep 17 00:00:00 2001 From: Hitesh Date: Mon, 19 Aug 2019 14:40:43 -0700 Subject: [PATCH 2/2] refactor --- AUTHORS | 1 + README.md | 110 +++++++++++- lib/index.js | 468 ++++++++++++++++++++++++--------------------------- package.json | 5 +- 4 files changed, 330 insertions(+), 254 deletions(-) diff --git a/AUTHORS b/AUTHORS index f2496e6..c8a5c36 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1 +1,2 @@ AJ ONeal (https://coolaj86.com/) +Hitesh Walia diff --git a/README.md b/README.md index bfd0dcf..fd9bdfb 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,111 @@ # acme-dns-01-route53 -Amazon AWS Route53 DNS + Let's Encrypt for Node.js - ACME dns-01 challenges w/ ACME.js and Greenlock.js \ No newline at end of file +Amazon AWS Route53 DNS + Let's Encrypt for Node.js - ACME dns-01 challenges w/ ACME.js and Greenlock.js + + +# Features + +- Compatible + - Let’s Encrypt v2.1 / ACME draft 18 (2019) + - DNSimple v2 API + - ACME.js, Greenlock.js, and others +- Quality + - node v6 compatible VanillaJS + +# Install + +```js +npm install --save acme-dns-01-route53 +``` + +AWS API keys: + +- Login to your account at: https://console.aws.amazon.com +- Go to Services > IAM > Users, create a new user and assign AmazonRoute53FullAccess policy. +- Click *Securty credentials* tab and then click *Create access key*. + +# Usage + +First you create an instance with your credentials: + +```js +var dns01 = require('acme-dns-01-route53').create({ + key: 'your_key', + secret: 'your_secret' +}); +``` + +Then you can use it with any compatible ACME library, such as Greenlock.js or ACME.js. + +## Greenlock.js + +```js +var Greenlock = require('greenlock-express'); +var greenlock = Greenlock.create({ + challenges: { + 'dns-01': dns01 + // ... + } +}); +``` + +See [Greenlock Express](https://git.rootprojects.org/root/greenlock-express.js) and/or [Greenlock.js](https://git.rootprojects.org/root/greenlock.js) documentation for more details. + +## ACME.js + +```js +// TODO +``` + +See the [ACME.js](https://git.rootprojects.org/root/acme-v2.js) for more details. + +## Build your own + +There are only 5 methods: + +- `init(config)` +- `zones(opts)` +- `set(opts)` +- `get(opts)` +- `remove(opts)` + +```js +dns01 + .set({ + identifier: { value: 'foo.example.co.uk' }, + wildcard: false, + dnsZone: 'example.co.uk', + dnsPrefix: '_acme-challenge.foo', + dnsAuthorization: 'xxx_secret_xxx' + }) + .then(function() { + console.log('TXT record set'); + }) + .catch(function() { + console.log('Failed to set TXT record'); + }); +``` + +See acme-dns-01-test for more implementation details. + +# Tests + +```bash +# node ./test.js domain-zone key secret +node ./test.js example.com xxxxx yyyyy +``` + +# Authors + +- AJ ONeal +- Hitesh Walia + +See AUTHORS for contact info. + +# Legal + +[acme-dns-01-dnsimple.js](https://git.coolaj86.com/coolaj86/acme-dns-01-dnsimple.js) | MPL-2.0 | [Terms of Use](https://therootcompany.com/legal/#terms) | [Privacy Policy](https://therootcompany.com/legal/#privacy) + +Copyright 2019 Hitesh Walia +Copyright 2019 AJ ONeal +Copyright 2019 The Root Group LLC diff --git a/lib/index.js b/lib/index.js index 56182d4..e1f2802 100644 --- a/lib/index.js +++ b/lib/index.js @@ -5,14 +5,66 @@ var xml2js = require('xml2js'); var parseString = xml2js.parseString; parseString = require('util').promisify(parseString); var builder = new xml2js.Builder(); - -var request = require('@root/request'); -//request.debug = false; +var request; var defaults = { baseUri: '/2013-04-01/' }; +function setWeightedPolicy(XmlObject, isWildcard) { + if (isWildcard) { + XmlObject.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].SetIdentifier = [ + 'wildcard' + ]; + } else { + XmlObject.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].SetIdentifier = [ + 'subdomain' + ]; + } + + XmlObject.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].Weight = [ + 1 + ]; +} + +// used to build XML payload +var XmlParameters = { + ChangeResourceRecordSetsRequest: { + $: { + xmlns: '' + }, + ChangeBatch: [ + { + Changes: [ + { + Change: [ + { + Action: [''], + ResourceRecordSet: [ + { + Type: ['TXT'], + Name: [''], + ResourceRecords: [ + { + ResourceRecord: [ + { + Value: [''] + } + ] + } + ], + TTL: [300] + } + ] + } + ] + } + ] + } + ] + } +}; + module.exports.create = function(config) { var baseUri = defaults.baseUri.replace(/\/$/, ''); var key = config.key; @@ -21,33 +73,34 @@ module.exports.create = function(config) { function api(method, path, body, query) { var body = body || ''; var query = query || {}; - var options = { - key: key, - secret: secret, - query: query + var options = { + key: key, + secret: secret, + query: query }; - - if(method === 'POST') { - options.headers = { 'Content-Type': 'application/xml' } - } - - var url = v4.createPresignedURL(method, 'route53.amazonaws.com', path, 'route53', body, options); - //console.log('url: ' + url + '\n'); - var parameters = { - method: method, - url: url + method: method }; - if(method === 'POST') { - parameters.headers = { 'Content-Type': 'application/xml' } + if (method === 'POST') { + options.headers = { 'Content-Type': 'application/xml' }; + parameters.headers = { 'Content-Type': 'application/xml' }; } - if(body) { + if (body) { parameters.body = body; } - // console.log(method, path, query); + var url = v4.createPresignedURL( + method, + 'route53.amazonaws.com', + path, + 'route53', + body, + options + ); + + parameters.url = url; return request(parameters).then(function(response) { if (response.statusCode < 200 || response.statusCode >= 300) { @@ -67,263 +120,174 @@ module.exports.create = function(config) { }); } - /* - api('GET', baseUri + '/' + 'hostedzone').then(function(response){ - console.log('######## RESPONSE #########'); - - }); - */ - return { init: function(options) { request = options.request; return null; }, - zones: function(data) { - //console.info('List Zones', data); - return api('GET', baseUri + '/' + 'hostedzone').then(function(response){ - return parseString(response.body).then(function(body){ - var zones = body.ListHostedZonesResponse.HostedZones[0].HostedZone.map(function(element) { - return element.Name[0].replace(/\.$/, ''); // all route 53 domains terminate with a dot - }); + zones: function(data) { + return api('GET', baseUri + '/' + 'hostedzone').then(function( + response + ) { + return parseString(response.body).then(function(body) { + var zones = body.ListHostedZonesResponse.HostedZones[0].HostedZone.map( + function(element) { + return element.Name[0].replace(/\.$/, ''); // all route 53 domains terminate with a dot + } + ); return zones; }); }); - }, set: function(data) { - // console.info('Add TXT', data); - // console.log('data: ' + JSON.stringify(data, null, 2)); var challenge = data.challenge; if (!challenge.dnsZone) { throw new Error('No matching zone for ' + challenge.dnsHost); } - return api('GET', baseUri + '/' + 'hostedzonesbyname', null, 'dnsname=' + challenge.dnsZone + '&maxitems=1') - .then(function(response){ - return parseString(response.body).then(function(body){ - return body; - }); - }) - .then(function(body){ - var zoneID = body.ListHostedZonesByNameResponse.HostedZones[0].HostedZone[0].Id[0]; - var xmlns = body.ListHostedZonesByNameResponse.$.xmlns; - - var parameters = { - 'ChangeResourceRecordSetsRequest': { - '$': { - 'xmlns': xmlns - }, - 'ChangeBatch': [ - { - 'Changes': [ - { - 'Change': [ - { - 'Action': [ - 'CREATE' - ], - 'ResourceRecordSet': [ - { - 'Type': [ - 'TXT' - ], - 'Name': [ - challenge.dnsHost // AWS requires FQDN - ], - 'ResourceRecords': [ - { - 'ResourceRecord': [ - { - 'Value': [ - '"' + challenge.dnsAuthorization + '"' // value must be surrounded by double quotes - ] - } - ] - } - ], - 'TTL': [ - 300 - ] - } - ] - - } - ] - } - ] - } - ] - } - }; + return api( + 'GET', + baseUri + '/' + 'hostedzonesbyname', + null, + 'dnsname=' + challenge.dnsZone + '&maxitems=1' + ) + .then(function(response) { + return parseString(response.body).then(function(body) { + return body; + }); + }) + .then(function(body) { + var zoneID = + body.ListHostedZonesByNameResponse.HostedZones[0] + .HostedZone[0].Id[0]; + var xmlns = body.ListHostedZonesByNameResponse.$.xmlns; - // By default AWS creates record sets with simple routing policy, so duplicates are not allowed. - // A workaround is put the record sets in different weighted policies. - if(challenge.identifier.value.startsWith('foo')) { - if(challenge.wildcard) { - - parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].Weight = [ - 1 - ]; + var parameters = JSON.parse(JSON.stringify(XmlParameters)); + parameters.ChangeResourceRecordSetsRequest.$.xmlns = xmlns; + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].Action[0] = + 'CREATE'; + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].Name[0] = + challenge.dnsHost; // AWS requires FQDN + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].ResourceRecords[0].ResourceRecord[0].Value[0] = + '"' + challenge.dnsAuthorization + '"'; // value must be surrounded by double quotes - parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].SetIdentifier = [ - 'wildcard' - ]; - } else { - parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].Weight = [ - 1 - ]; + // By default AWS creates record sets with simple routing policy, so duplicates are not allowed. + // A workaround is put the record sets in different weighted policies. + if (challenge.identifier.value.startsWith('foo')) { + setWeightedPolicy(parameters, challenge.wildcard); + } - parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].SetIdentifier = [ - 'subdomain' - ]; - } - } - - var xml = builder.buildObject(parameters); - //console.log('xml: ' + xml); - - return api('POST', baseUri + zoneID + '/rrset/', xml); - - }); - - + var xml = builder.buildObject(parameters); + return api('POST', baseUri + zoneID + '/rrset/', xml); + }); }, get: function(data) { - // console.info('List TXT', data); var challenge = data.challenge; - return api('GET', baseUri + '/' + 'hostedzonesbyname', null, 'dnsname=' + challenge.dnsZone + '&maxitems=1') - .then(function(response){ - return parseString(response.body).then(function(body){ - return body; - }); - }) - .then(function(body){ - var zoneID = body.ListHostedZonesByNameResponse.HostedZones[0].HostedZone[0].Id[0]; - // console.log('zoneID: ' + zoneID); - return zoneID; - }) - .then(function(zoneID){ - // GET /2013-04-01/hostedzone/Id/rrset?identifier=StartRecordIdentifier&maxitems=MaxItems&name=StartRecordName&type=StartRecordType HTTP/1.1 - - return api('GET', baseUri + '/' + zoneID + '/rrset', null, 'name=' + challenge.dnsPrefix + '.' + challenge.dnsZone + '.' + '&type=TXT') - .then(function(response){ - return parseString(response.body).then(function(body){ - if (body.ListResourceRecordSetsResponse.ResourceRecordSets[0] === '') return null; - - var record = body.ListResourceRecordSetsResponse.ResourceRecordSets[0].ResourceRecordSet.filter(function(element){ - return ( - element.ResourceRecords[0].ResourceRecord[0].Value[0].replace(/\"/g, '') === challenge.dnsAuthorization && - element.Name[0].includes(challenge.dnsPrefix) - ); - })[0]; - - if (record) { - return { dnsAuthorization: record.ResourceRecords[0].ResourceRecord[0].Value[0].replace(/\"/g, '') }; - } - - return null; - }); - }); - - }); - - }, - remove: function(data) { - // console.info('Remove TXT', data); - - var challenge = data.challenge; - - return api('GET', baseUri + '/' + 'hostedzonesbyname', null, 'dnsname=' + challenge.dnsZone + '&maxitems=1') - .then(function(response){ - return parseString(response.body).then(function(body){ - return body; - }); - }) - .then(function(body){ - var zoneID = body.ListHostedZonesByNameResponse.HostedZones[0].HostedZone[0].Id[0]; - var xmlns = body.ListHostedZonesByNameResponse.$.xmlns; - - var parameters = { - 'ChangeResourceRecordSetsRequest': { - '$': { - 'xmlns': xmlns - }, - 'ChangeBatch': [ - { - 'Changes': [ - { - 'Change': [ - { - 'Action': [ - 'DELETE' - ], - 'ResourceRecordSet': [ - { - 'Type': [ - 'TXT' - ], - 'Name': [ - challenge.dnsHost // AWS requires FQDN - ], - 'ResourceRecords': [ - { - 'ResourceRecord': [ - { - 'Value': [ - '"' + challenge.dnsAuthorization + '"' // value must be surrounded by double quotes - ] - } - ] - } - ], - 'TTL': [ - 300 - ] - } - ] - - } - ] - } - ] - } - ] - } - }; - - if(challenge.identifier.value.startsWith('foo')) { - if(challenge.wildcard) { - parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].Weight = [ - 1 - ]; - - parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].SetIdentifier = [ - 'wildcard' - ]; - } else { - parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].Weight = [ - 1 - ]; - - parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].SetIdentifier = [ - 'subdomain' - ]; - } + return api( + 'GET', + baseUri + '/' + 'hostedzonesbyname', + null, + 'dnsname=' + challenge.dnsZone + '&maxitems=1' + ) + .then(function(response) { + return parseString(response.body).then(function(body) { + return body; + }); + }) + .then(function(body) { + var zoneID = + body.ListHostedZonesByNameResponse.HostedZones[0] + .HostedZone[0].Id[0]; + return zoneID; + }) + .then(function(zoneID) { + return api( + 'GET', + baseUri + '/' + zoneID + '/rrset', + null, + 'name=' + + challenge.dnsPrefix + + '.' + + challenge.dnsZone + + '.' + + '&type=TXT' + ).then(function(response) { + return parseString(response.body).then(function(body) { + if ( + body.ListResourceRecordSetsResponse + .ResourceRecordSets[0] === '' + ) { + return null; } - var xml = builder.buildObject(parameters); - // console.log('\n', xml, '\n'); - - return api('POST', baseUri + zoneID + '/rrset/', xml); - - }); + var record = body.ListResourceRecordSetsResponse.ResourceRecordSets[0].ResourceRecordSet.filter( + function(element) { + return ( + element.ResourceRecords[0].ResourceRecord[0].Value[0].replace( + /\"/g, + '' + ) === challenge.dnsAuthorization && + element.Name[0].includes( + challenge.dnsPrefix + ) + ); + } + )[0]; + if (record) { + return { + dnsAuthorization: record.ResourceRecords[0].ResourceRecord[0].Value[0].replace( + /\"/g, + '' + ) + }; + } + + return null; + }); + }); + }); + }, + remove: function(data) { + var challenge = data.challenge; + + return api( + 'GET', + baseUri + '/' + 'hostedzonesbyname', + null, + 'dnsname=' + challenge.dnsZone + '&maxitems=1' + ) + .then(function(response) { + return parseString(response.body).then(function(body) { + return body; + }); + }) + .then(function(body) { + var zoneID = + body.ListHostedZonesByNameResponse.HostedZones[0] + .HostedZone[0].Id[0]; + var xmlns = body.ListHostedZonesByNameResponse.$.xmlns; + + var parameters = JSON.parse(JSON.stringify(XmlParameters)); + parameters.ChangeResourceRecordSetsRequest.$.xmlns = xmlns; + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].Action[0] = + 'DELETE'; + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].Name[0] = + challenge.dnsHost; // AWS requires FQDN + parameters.ChangeResourceRecordSetsRequest.ChangeBatch[0].Changes[0].Change[0].ResourceRecordSet[0].ResourceRecords[0].ResourceRecord[0].Value[0] = + '"' + challenge.dnsAuthorization + '"'; // value must be surrounded by double quotes + + // need to add weight and setidentifier to delete record set + if (challenge.identifier.value.startsWith('foo')) { + setWeightedPolicy(parameters, challenge.wildcard); + } + + var xml = builder.buildObject(parameters); + return api('POST', baseUri + zoneID + '/rrset/', xml); + }); } - - } - + }; }; diff --git a/package.json b/package.json index a92e017..97c7965 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,10 @@ "acme", "greenlock" ], - "author": "AJ ONeal (https://coolaj86.com/)", + "author": "Hitesh Walia (https://coolaj86.com/)" + ], "license": "MPL-2.0", "devDependencies": { "dotenv": "^8.1.0" -- 2.38.5