publish #2
1
AUTHORS
1
AUTHORS
|
@ -1 +1,2 @@
|
||||||
AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)
|
AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)
|
||||||
|
Hitesh Walia <hiteshwar.walia@gmail.com>
|
||||||
|
|
110
README.md
110
README.md
|
@ -1,3 +1,111 @@
|
||||||
# acme-dns-01-route53
|
# acme-dns-01-route53
|
||||||
|
|
||||||
Amazon AWS Route53 DNS + Let's Encrypt for Node.js - ACME dns-01 challenges w/ ACME.js and Greenlock.js
|
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
|
||||||
|
|
290
lib/index.js
290
lib/index.js
|
@ -1,29 +1,293 @@
|
||||||
'use strict';
|
'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 request;
|
||||||
var defaults = {};
|
|
||||||
|
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) {
|
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 {
|
return {
|
||||||
init: function(opts) {
|
init: function(options) {
|
||||||
request = opts.request;
|
request = options.request;
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
zones: function(data) {
|
zones: function(data) {
|
||||||
//console.info('List Zones', data);
|
return api('GET', baseUri + '/' + 'hostedzone').then(function(
|
||||||
throw Error('listing zones not implemented');
|
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) {
|
set: function(data) {
|
||||||
// console.info('Add TXT', data);
|
var challenge = data.challenge;
|
||||||
throw Error('setting TXT not implemented');
|
|
||||||
},
|
if (!challenge.dnsZone) {
|
||||||
remove: function(data) {
|
throw new Error('No matching zone for ' + challenge.dnsHost);
|
||||||
// console.info('Remove TXT', data);
|
}
|
||||||
throw Error('removing TXT not implemented');
|
|
||||||
|
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) {
|
get: function(data) {
|
||||||
// console.info('List TXT', data);
|
var challenge = data.challenge;
|
||||||
throw Error('listing TXTs not implemented');
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -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="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
12
package.json
12
package.json
|
@ -19,9 +19,17 @@
|
||||||
"acme",
|
"acme",
|
||||||
"greenlock"
|
"greenlock"
|
||||||
],
|
],
|
||||||
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
|
"author": "Hitesh Walia <hiteshwar.walia@gmail.com",
|
||||||
|
"contributors": [
|
||||||
|
"AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)"
|
||||||
|
],
|
||||||
"license": "MPL-2.0",
|
"license": "MPL-2.0",
|
||||||
"devDependencies": {
|
"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
|
// See https://git.coolaj86.com/coolaj86/acme-challenge-test.js
|
||||||
var tester = require('acme-challenge-test');
|
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 zone = process.argv[2] || process.env.ZONE;
|
||||||
var challenger = require('./index.js').create({
|
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'
|
// The dry-run tests can pass on, literally, 'example.com'
|
||||||
|
|
Loading…
Reference in New Issue