acme-dns-01-cli.js/index.js

125 lines
3.8 KiB
JavaScript

'use strict';
var PromiseA = require('bluebird');
var dns = PromiseA.promisifyAll(require('dns'));
var Challenge = module.exports;
var leDnsResponse;
Challenge.create = function (defaults) {
return {
getOptions: function () {
return defaults || {};
}
, set: Challenge.set
, get: Challenge.get
, remove: Challenge.remove
, loopback: Challenge.loopback
, test: Challenge.test
};
};
// Show the user the token and key and wait for them to be ready to continue
Challenge.set = function (args, domain, challenge, keyAuthorization, cb) {
var keyAuthDigest = require('crypto').createHash('sha256').update(keyAuthorization||'').digest('base64')
.replace(/\+/g, '-')
.replace(/\//g, '_')
.replace(/=+$/g, '')
;
var challengeDomain = domain;
if (this.leDnsResponse) {
this.leDnsResponse(challenge, keyAuthorization, keyAuthDigest, challengeDomain, domain)
.then((successMessage) => {
console.log("Yay! " + successMessage);
cb(null);
});
} else {
console.info("");
console.info("Challenge for '" + domain + "'");
console.info("");
console.info("We now present (for you copy-and-paste pleasure) your ACME Challenge");
console.info("public Challenge and secret KeyAuthorization and Digest, in that order, respectively:");
console.info(challenge);
console.info(keyAuthorization);
console.info(keyAuthDigest);
console.info("");
console.info(challengeDomain + "\tTXT " + keyAuthDigest + "\tTTL 60");
console.info("");
console.info(JSON.stringify({
domain: domain
, challenge: challenge
, keyAuthorization: keyAuthorization
, keyAuthDigest: keyAuthDigest
}, null, ' ').replace(/^/gm, '\t'));
console.info("");
console.info("hit enter to continue...");
process.stdin.resume();
process.stdin.on('data', function () {
process.stdin.pause();
cb(null);
});
}
};
// nothing to do here, that's why it's manual
Challenge.get = function (defaults, domain, challenge, cb) {
cb(null);
};
// might as well tell the user that whatever they were setting up has been checked
Challenge.remove = function (args, domain, challenge, cb) {
console.info("Challenge for '" + domain + "' complete. You may remove it.");
console.info("");
//console.info("hit enter to continue...");
//process.stdin.resume();
//process.stdin.on('data', function () {
// process.stdin.pause();
cb(null);
//});
};
Challenge.loopback = function (defaults, domain, challenge, done) {
var challengeDomain = (defaults.test || '') + defaults.acmeChallengeDns + domain;
console.log("dig TXT +noall +answer @8.8.8.8 '" + challengeDomain + "' # " + challenge);
dns.resolveTxtAsync(challengeDomain).then(function (x) { done(null, x); }, done);
};
Challenge.test = function (args, domain, challenge, keyAuthorization, done) {
var me = this;
args.test = args.test || '_test.';
defaults.test = args.test;
me.set(args, domain, challenge, keyAuthorization || challenge, function (err, k) {
if (err) { done(err); return; }
me.loopback(defaults, domain, challenge, function (err, arr) {
if (err) { done(err); return; }
if (!arr.some(function (a) {
return a.some(function (keyAuthDigest) {
return keyAuthDigest === k;
});
})) {
err = new Error("txt record '" + challenge + "' doesn't match '" + k + "'");
}
me.remove(defaults, domain, challenge, function (_err) {
if (_err) { done(_err); return; }
// TODO needs to use native-dns so that specific nameservers can be used
// (otherwise the cache will still have the old answer)
done(err || null);
/*
me.loopback(defaults, domain, challenge, function (err) {
if (err) { done(err); return; }
done();
});
*/
});
});
});
}