mirror of
https://git.coolaj86.com/coolaj86/acme-dns-01-cli.js
synced 2025-04-21 21:30:38 +00:00
224 lines
7.1 KiB
JavaScript
224 lines
7.1 KiB
JavaScript
'use strict';
|
|
/*global Promise*/
|
|
var Challenge = module.exports;
|
|
|
|
// If your implementation needs config options, set them. Otherwise, don't bother (duh).
|
|
Challenge.create = function(config) {
|
|
var challenger = {};
|
|
|
|
// Note: normally you'd these right in the method body, but for the sake of
|
|
// "Table of Contents"-style documentation, I've pulled them out.
|
|
|
|
// Note: All of these methods can be synchronous, async, Promise, and callback-style
|
|
// (the calling functions check function.length and then Promisify accordingly)
|
|
|
|
// Fetches an array of zone name strings
|
|
challenger.zones = function(opts, cb) {
|
|
return Challenge._getZones(opts, cb);
|
|
};
|
|
|
|
// Called when it's time to set the challenge
|
|
challenger.set = function(opts, cb) {
|
|
return Challenge._setDns(opts, cb);
|
|
};
|
|
|
|
// Called when it's time to remove the challenge
|
|
challenger.remove = function(opts, cb) {
|
|
return Challenge._removeDns(opts, cb);
|
|
};
|
|
|
|
// Optional (only really useful for http and testing)
|
|
// Called when the challenge needs to be retrieved
|
|
challenger.get = function(opts) {
|
|
return Challenge._getDns(opts);
|
|
};
|
|
|
|
// Whatever you assign to 'options' will be merged into the incoming 'opts' beforehand
|
|
// (for convenience, so you don't have to do the if (!x) { x = y; } dance)
|
|
// (also, some defaults are layered, so it's good to set it any that you have)
|
|
challenger.options = { debug: config.debug };
|
|
|
|
return challenger;
|
|
};
|
|
|
|
// Show the user the token and key and wait for them to be ready to continue
|
|
Challenge._getZones = function(args, cb) {
|
|
// if you need per-run / per-domain options set them in approveDomains() and they'll be on 'args' here.
|
|
if (!Array.isArray(args.dnsHosts)) {
|
|
console.error(
|
|
'You must be using Greenlock v2.7+ to use acme-dns-01-cli v3+'
|
|
);
|
|
process.exit();
|
|
}
|
|
console.info();
|
|
console.info('############################');
|
|
console.info('# Step 1: Get Domain Zones #');
|
|
console.info('############################');
|
|
console.info();
|
|
console.info(
|
|
'Enter a comma or space delimited list of domain zones to which the following domain records belong.'
|
|
);
|
|
console.info();
|
|
console.info(
|
|
'Example:' +
|
|
'\n\tDOMAIN RECORD\t=>\tDOMAIN ZONE' +
|
|
'\n\texample.com\t=>\texample.com' +
|
|
'\n\tfoo.example.com\t=>\texample.com' +
|
|
'\n\tbar.example.com\t=>\texample.com' +
|
|
'\n\texample.co.uk\t=>\texample.co.uk' +
|
|
'\n\nYou would enter: example.com, example.co.uk'
|
|
);
|
|
console.info();
|
|
console.info('Domain RECORDS: ', args.dnsHosts.join(', '));
|
|
process.stdout.write('Domain ZONE list: ');
|
|
process.stdin.resume();
|
|
process.stdin.once('data', function(chunk) {
|
|
process.stdin.pause();
|
|
var zones = chunk
|
|
.toString('utf8')
|
|
.trim()
|
|
.split(/[,\s]+/);
|
|
console.info('Got Domain Zones:', zones);
|
|
setTimeout(function() {
|
|
cb(null, zones);
|
|
}, 1000);
|
|
});
|
|
};
|
|
|
|
// Show the user the token and key and wait for them to be ready to continue
|
|
Challenge._setDns = function(args, cb) {
|
|
// if you need per-run / per-domain options set them in approveDomains() and they'll be on 'args' here.
|
|
if (!args.challenge) {
|
|
console.error(
|
|
'You must be using Greenlock v2.7+ to use acme-dns-01-cli v3+'
|
|
);
|
|
process.exit();
|
|
}
|
|
var ch = args.challenge;
|
|
|
|
console.info('\n\n\n\n\n');
|
|
console.info('#################################');
|
|
console.info('# Step 2: Set Domain TXT Record #');
|
|
console.info('#################################');
|
|
console.info('');
|
|
console.info("[ACME dns-01 '" + ch.altname + "' CHALLENGE]");
|
|
console.info("You're about to receive the following DNS query:");
|
|
console.info('');
|
|
console.info(
|
|
'\tTXT\t' + ch.dnsHost + '\t' + ch.dnsAuthorization + '\tTTL 60'
|
|
);
|
|
console.info('');
|
|
if (ch.debug) {
|
|
console.info('Debug Info:');
|
|
console.info('');
|
|
console.info(
|
|
JSON.stringify(dnsChallengeToJson(ch), null, ' ').replace(/^/gm, '\t')
|
|
);
|
|
console.info('');
|
|
}
|
|
console.info(
|
|
'Go set that DNS record, wait a few seconds for it to propagate, and then continue when ready'
|
|
);
|
|
console.info('[Press the ANY key to continue...]');
|
|
process.stdin.resume();
|
|
process.stdin.once('data', function() {
|
|
process.stdin.pause();
|
|
setTimeout(function() {
|
|
cb(null, null);
|
|
}, 1000);
|
|
});
|
|
};
|
|
|
|
// might as well tell the user that whatever they were setting up has been checked
|
|
Challenge._removeDns = function(args, cb) {
|
|
var ch = args.challenge;
|
|
console.info('\n\n\n\n\n');
|
|
console.info('####################################');
|
|
console.info('# Step 4: Remove Domain TXT Record #');
|
|
console.info('####################################');
|
|
console.info('');
|
|
console.info("[ACME dns-01 '" + ch.altname + "' COMPLETE]: " + ch.status);
|
|
console.info(
|
|
'Challenge complete. You may now remove the DNS-01 challenge record:'
|
|
);
|
|
console.info('');
|
|
console.info('\tTXT\t' + ch.altname + '\t' + ch.dnsAuthorization);
|
|
console.info('');
|
|
console.info('NOTE: the next get should be EMPTY');
|
|
console.info('');
|
|
|
|
setTimeout(function() {
|
|
cb(null, null);
|
|
}, 1000);
|
|
};
|
|
|
|
// This is implemented here for completeness (and perhaps some possible use in testing),
|
|
// but it's not something you would implement because the Greenlock server isn't the NameServer.
|
|
Challenge._getDns = function(args) {
|
|
var ch = args.challenge;
|
|
// because the way to mock a DNS challenge is weird
|
|
var altname = ch.altname || ch.dnsHost || ch.identifier.value;
|
|
var dnsHost = ch.dnsHost || ch.identifier.value;
|
|
|
|
if (ch._test || !Challenge._getCache[ch.token]) {
|
|
console.info('\n\n\n\n\n');
|
|
console.info('#################################');
|
|
console.info('# Step 3: Get Domain TXT Record #');
|
|
console.info('#################################');
|
|
Challenge._getCache[ch.token] = true;
|
|
console.info('');
|
|
console.info(
|
|
'[ACME ' + ch.type + " '" + altname + "' REQUEST]: " + ch.status
|
|
);
|
|
console.info("The '" + ch.type + "' challenge request has arrived!");
|
|
console.info('dig TXT ' + dnsHost);
|
|
console.info(
|
|
'(paste in the "DNS Authorization" you received a moment ago to respond)'
|
|
);
|
|
process.stdout.write('> ');
|
|
}
|
|
|
|
return new Promise(function(resolve, reject) {
|
|
process.stdin.resume();
|
|
process.stdin.once('error', reject);
|
|
process.stdin.once('data', function(chunk) {
|
|
process.stdin.pause();
|
|
|
|
var result = chunk.toString('utf8').trim();
|
|
try {
|
|
result = JSON.parse(result);
|
|
} catch (e) {
|
|
args.challenge.dnsAuthorization = result;
|
|
result = args.challenge;
|
|
}
|
|
if (result.dnsAuthorization) {
|
|
setTimeout(function() {
|
|
resolve(result);
|
|
}, 1000);
|
|
return;
|
|
}
|
|
|
|
// The return value will checked. It must not be 'undefined'.
|
|
setTimeout(function() {
|
|
resolve(null);
|
|
}, 1000);
|
|
});
|
|
});
|
|
};
|
|
Challenge._getCache = {};
|
|
|
|
function dnsChallengeToJson(ch) {
|
|
return {
|
|
type: ch.type,
|
|
altname: ch.altname,
|
|
identifier: ch.identifier,
|
|
wildcard: ch.wildcard,
|
|
expires: ch.expires,
|
|
token: ch.token,
|
|
thumbprint: ch.thumbprint,
|
|
keyAuthorization: ch.keyAuthorization,
|
|
dnsHost: ch.dnsHost,
|
|
dnsAuthorization: ch.dnsAuthorization
|
|
};
|
|
}
|