publish #2
							
								
								
									
										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); | ||||
| 	 | ||||
| 						}); | ||||
| 
 | ||||
| 		} | ||||
| 	}; | ||||
| 		 | ||||
| 	} | ||||
| 	 | ||||
| }; | ||||
|  | ||||
							
								
								
									
										51
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								package-lock.json
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @ -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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user