294 lines
7.6 KiB
JavaScript
294 lines
7.6 KiB
JavaScript
'use strict';
|
|
|
|
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;
|
|
|
|
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;
|
|
var secret = config.secret;
|
|
|
|
function api(method, path, body, query) {
|
|
var body = body || '';
|
|
var query = query || {};
|
|
var options = {
|
|
key: key,
|
|
secret: secret,
|
|
query: query
|
|
};
|
|
var parameters = {
|
|
method: method
|
|
};
|
|
|
|
if (method === 'POST') {
|
|
options.headers = { 'Content-Type': 'application/xml' };
|
|
parameters.headers = { 'Content-Type': 'application/xml' };
|
|
}
|
|
|
|
if (body) {
|
|
parameters.body = body;
|
|
}
|
|
|
|
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) {
|
|
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;
|
|
});
|
|
}
|
|
|
|
return {
|
|
init: function(options) {
|
|
request = options.request;
|
|
return null;
|
|
},
|
|
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) {
|
|
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 = 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
|
|
|
|
// 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);
|
|
}
|
|
|
|
var xml = builder.buildObject(parameters);
|
|
return api('POST', baseUri + zoneID + '/rrset/', xml);
|
|
});
|
|
},
|
|
get: 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];
|
|
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 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);
|
|
});
|
|
}
|
|
};
|
|
};
|