Browse Source

v3.0.0: Name.com DNS + Let's Encrypt for Node.js

master v3.0.0
AJ ONeal 5 years ago
parent
commit
18fcf2ce31
  1. 30
      README.md
  2. 4
      example.env
  3. 132
      lib/index.js
  4. 4
      package.json
  5. 6
      test.js
  6. 9
      token-test.sh

30
README.md

@ -8,7 +8,7 @@ Handles ACME dns-01 challenges. Compatible with ACME.js and Greenlock.js. Passes
- Compatible
- Let’s Encrypt v2.1 / ACME draft 18 (2019)
- Name.com API
- Name.com v4 API
- ACME.js, Greenlock.js, and others
- Quality
- node v6 compatible VanillaJS
@ -21,10 +21,22 @@ Handles ACME dns-01 challenges. Compatible with ACME.js and Greenlock.js. Passes
npm install --save acme-dns-01-namedotcom
```
Name.com Token:
## Name.com API Token
- Login to your account at: {{ Service URL }}
- {{ Instructions to generate token }}
- Login to your account at: <https://www.name.com/>
- Generate a token at <https://www.name.com/account/settings/api>
- **Important** Enable API Access, _again_ at <https://www.name.com/account/settings/security>
The following error is what you may get if you have Two-Factor Auth and don't _Enable API Access_ the second time:
```json
{
"message": "Permission Denied",
"details": "Authentication Error - Account Has Namesafe Enabled"
}
```
If you're using the Whitelist IPs feature, don't forget to add your test environment!
# Usage
@ -32,7 +44,8 @@ First you create an instance with your credentials:
```js
var dns01 = require('acme-dns-01-namedotcom').create({
baseUrl: '{{ api url }}', // default
baseUrl: 'http://api.name.com/v4/', // default
username: 'johndoe',
token: 'xxxx'
});
```
@ -51,7 +64,8 @@ var greenlock = Greenlock.create({
});
```
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.
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
@ -93,8 +107,8 @@ See acme-dns-01-test for more implementation details.
# Tests
```bash
# node ./test.js domain-zone api-token
node ./test.js example.com xxxxxx
# node ./test.js domain-zone username api-token
node ./test.js example.com me xxxxxx
```
# Authors

4
example.env

@ -1,2 +1,4 @@
ZONE=example.co.uk
TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# Used for HTTP Basic Auth
USERNAME=johndoe
TOKEN=xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

132
lib/index.js

@ -1,29 +1,137 @@
'use strict';
var request;
var defaults = {};
var defaults = {
baseUrl: 'https://api.name.com/v4/'
};
module.exports.create = function(config) {
return {
var baseUrl = (config.baseUrl || defaults.baseUrl).replace(/\/$/, '');
var token = config.token;
var username = config.username;
var plugin = {
init: function(opts) {
request = opts.request;
return null;
},
// We must list all zones (domains) in the account
zones: function(data) {
//console.info('List Zones', data);
throw Error('listing zones not implemented');
return api({
url: baseUrl + '/domains'
}).then(function(resp) {
return resp.body.domains.map(function(d) {
//#console.log("Domain Name:", d.domainName);
return d.domainName;
});
});
},
// We must set each record as required
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('Add TXT', data);
var ch = data.challenge;
if (!ch.dnsZone) {
throw new Error(
'[Name.com Plugin] Unknown domain: ',
data.domain || data.dnsHost
);
}
return api({
method: 'POST',
url: baseUrl + '/domains/' + ch.dnsZone + '/records',
json: {
host: ch.dnsPrefix,
type: 'TXT',
answer: ch.dnsAuthorization,
ttl: 300 // minimum allowed value
}
}).then(function(resp) {
if (!resp.body.id) {
throw Error('[Name.com API] [set] ' + resp.body);
}
return null;
});
},
// We must be able to confirm that the appropriate records were set
get: function(data) {
// console.info('List TXT', data);
throw Error('listing TXTs not implemented');
// console.log('List TXT', data);
var ch = data.challenge;
if (!ch.dnsZone) {
throw new Error(
'[Name.com Plugin] Unknown domain: ',
data.domain || data.dnsHost
);
}
return api({
url: baseUrl + '/domains/' + ch.dnsZone + '/records'
}).then(function(resp) {
var value = resp.body.records.filter(function(r) {
return (
r.host === ch.dnsPrefix &&
'TXT' === r.type &&
ch.dnsAuthorization === r.answer
);
})[0];
if (!value) {
return null;
}
// adding id to make re-usable for remove
return { id: value.id, dnsAuthorization: value.answer };
});
},
// We must delete junk records once we're done
remove: function(data) {
// console.log('Remove TXT', data);
var ch = data.challenge;
return plugin.get(data).then(function(r) {
if (!r.id) {
throw new Error(
'[Name.com Plugin] [del] Did not find TXT record for ' +
ch.dnsHost
);
}
return api({
method: 'DELETE',
url: baseUrl + '/domains/' + ch.dnsZone + '/records/' + r.id
}).then(function(resp) {
return null;
});
});
}
};
// Authentication and Error handling here
function api(opts) {
opts.auth = {
user: username,
pass: token,
sendImmediately: true
};
if (!opts.json) {
opts.json = true;
}
return request(opts).then(function(resp) {
if (!resp.body.message) {
return resp;
}
console.error(opts.method + ' ' + opts.url);
console.error(resp.headers);
console.error(resp.body);
throw new Error(
'[Name.com API] ' +
(opts.method || 'GET') +
' ' +
opts.url +
' : ' +
resp.body.message
);
});
}
return plugin;
};

4
package.json

@ -1,6 +1,6 @@
{
"name": "acme-dns-01-namedotcom",
"version": "0.0.1",
"version": "3.0.0",
"description": "Name.com + Let's Encrypt for Node.js - ACME dns-01 challenges w/ ACME.js and Greenlock.js",
"main": "index.js",
"files": [
@ -26,9 +26,9 @@
"author": "AJ ONeal <coolaj86@gmail.com> (https://coolaj86.com/)",
"license": "MPL-2.0",
"devDependencies": {
"acme-challenge-test": "^3.3.2",
"dotenv": "^8.0.0"
},
"dependencies": {
"acme-challenge-test": "^3.3.2"
}
}

6
test.js

@ -5,10 +5,12 @@
var tester = require('acme-challenge-test');
require('dotenv').config();
// Usage: node ./test.js example.com xxxxxxxxx
// Usage: node ./test.js example.com username xxxxxxxxx
var zone = process.argv[2] || process.env.ZONE;
var challenger = require('./index.js').create({
token: process.argv[3] || process.env.TOKEN
//baseUrl: 'http://api.dev.name.com/v4/', // sandbox url
username: process.argv[3] || process.env.USERNAME,
token: process.argv[4] || process.env.TOKEN
});
// The dry-run tests can pass on, literally, 'example.com'

9
token-test.sh

@ -0,0 +1,9 @@
#!/bin/bash
set -e
# Before assuming there's something wrong with this plugin,
# you should test that you can use your token with curl first.
echo "USERNAME: '$USERNAME'"
echo "TOKEN: '$TOKEN'"
curl -f -u "$USERNAME:$TOKEN" https://api.name.com/v4/domains
Loading…
Cancel
Save