|
|
@ -1,11 +1,9 @@ |
|
|
|
'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) { |
|
|
|
|
|
|
|
Challenge.create = function(config) { |
|
|
|
var challenger = {}; |
|
|
|
|
|
|
|
// Note: normally you'd these right in the method body, but for the sake of
|
|
|
@ -14,19 +12,24 @@ Challenge.create = function (config) { |
|
|
|
// Note: All of these methods can be synchronous, async, Promise, and callback-style
|
|
|
|
// (the calling functions check function.length and then Promisify accordingly)
|
|
|
|
|
|
|
|
// Called when it's tiem to set the challenge
|
|
|
|
challenger.set = function (opts, cb) { |
|
|
|
// 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) { |
|
|
|
return Challenge._removeDns(opts); |
|
|
|
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) { |
|
|
|
challenger.get = function(opts) { |
|
|
|
return Challenge._getDns(opts); |
|
|
|
}; |
|
|
|
|
|
|
@ -38,88 +41,167 @@ Challenge.create = function (config) { |
|
|
|
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) { |
|
|
|
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 greenlock-challenge-dns v3+"); |
|
|
|
console.error( |
|
|
|
'You must be using Greenlock v2.7+ to use acme-dns-01-cli v3+' |
|
|
|
); |
|
|
|
process.exit(); |
|
|
|
} |
|
|
|
var ch = args.challenge; |
|
|
|
|
|
|
|
console.info(""); |
|
|
|
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(""); |
|
|
|
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('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...]"); |
|
|
|
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.once('data', function() { |
|
|
|
process.stdin.pause(); |
|
|
|
cb(null, null); |
|
|
|
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) { |
|
|
|
Challenge._removeDns = function(args, cb) { |
|
|
|
var ch = args.challenge; |
|
|
|
console.info(""); |
|
|
|
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(""); |
|
|
|
|
|
|
|
return null; |
|
|
|
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) { |
|
|
|
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); |
|
|
|
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(''); |
|
|
|
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("> "); |
|
|
|
console.info( |
|
|
|
'(paste in the "DNS Authorization" you received a moment ago to respond)' |
|
|
|
); |
|
|
|
process.stdout.write('> '); |
|
|
|
} |
|
|
|
|
|
|
|
return new Promise(function (resolve, reject) { |
|
|
|
return new Promise(function(resolve, reject) { |
|
|
|
process.stdin.resume(); |
|
|
|
process.stdin.once('error', reject); |
|
|
|
process.stdin.once('data', function (chunk) { |
|
|
|
process.stdin.once('data', function(chunk) { |
|
|
|
process.stdin.pause(); |
|
|
|
|
|
|
|
var result = chunk.toString('utf8').trim(); |
|
|
|
try { |
|
|
|
result = JSON.parse(result); |
|
|
|
} catch(e) { |
|
|
|
} catch (e) { |
|
|
|
args.challenge.dnsAuthorization = result; |
|
|
|
result = args.challenge; |
|
|
|
} |
|
|
|
if (result.dnsAuthorization) { |
|
|
|
resolve(result); |
|
|
|
setTimeout(function() { |
|
|
|
resolve(result); |
|
|
|
}, 1000); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// The return value will checked. It must not be 'undefined'.
|
|
|
|
resolve(null); |
|
|
|
setTimeout(function() { |
|
|
|
resolve(null); |
|
|
|
}, 1000); |
|
|
|
}); |
|
|
|
}); |
|
|
|
}; |
|
|
@ -127,15 +209,15 @@ 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 |
|
|
|
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 |
|
|
|
}; |
|
|
|
} |
|
|
|