all integration tests passed
This commit is contained in:
parent
c7336ae7da
commit
9a54d2e3bc
326
lib/index.js
326
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);
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
|
|
@ -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="
|
||||
}
|
||||
}
|
||||
}
|
|
@ -22,6 +22,11 @@
|
|||
"author": "AJ ONeal <coolaj86@gmail.com> (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"
|
||||
}
|
||||
}
|
||||
|
|
6
test.js
6
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'
|
||||
|
|
Loading…
Reference in New Issue