2019-07-16 03:24:31 +00:00
|
|
|
'use strict';
|
|
|
|
|
2019-08-19 05:27:42 +00:00
|
|
|
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/'
|
|
|
|
};
|
2019-07-16 03:24:31 +00:00
|
|
|
|
|
|
|
module.exports.create = function(config) {
|
2019-08-19 05:27:42 +00:00
|
|
|
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 #########');
|
|
|
|
|
|
|
|
});
|
|
|
|
*/
|
|
|
|
|
2019-07-16 03:24:31 +00:00
|
|
|
return {
|
2019-08-19 05:27:42 +00:00
|
|
|
init: function(options) {
|
|
|
|
request = options.request;
|
2019-07-16 03:24:31 +00:00
|
|
|
return null;
|
|
|
|
},
|
2019-08-19 05:27:42 +00:00
|
|
|
zones: function(data) {
|
2019-07-16 03:24:31 +00:00
|
|
|
//console.info('List Zones', data);
|
2019-08-19 05:27:42 +00:00
|
|
|
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;
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2019-07-16 03:24:31 +00:00
|
|
|
},
|
|
|
|
set: function(data) {
|
|
|
|
// console.info('Add TXT', data);
|
2019-08-19 05:27:42 +00:00
|
|
|
// 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);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
2019-07-16 03:24:31 +00:00
|
|
|
},
|
|
|
|
get: function(data) {
|
|
|
|
// console.info('List TXT', data);
|
2019-08-19 05:27:42 +00:00
|
|
|
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);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
2019-07-16 03:24:31 +00:00
|
|
|
}
|
2019-08-19 05:27:42 +00:00
|
|
|
|
|
|
|
}
|
|
|
|
|
2019-07-16 03:24:31 +00:00
|
|
|
};
|