forked from coolaj86/goldilocks.js
		
	implemented dns-01 ACME challenges
This commit is contained in:
		
							parent
							
								
									40bd1d9cc6
								
							
						
					
					
						commit
						11f2d37044
					
				| @ -271,6 +271,12 @@ tls: | |||||||
|       challenge_type: 'http-01' |       challenge_type: 'http-01' | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
|  | **NOTE:** If you specify `dns-01` as the challenge type there must also be a | ||||||
|  | [DDNS module](#ddns) defined for all of the relevant domains (though not all | ||||||
|  | domains handled by a single TLS module need to be handled by the same DDNS | ||||||
|  | module). The DDNS module provides all of the information needed to actually | ||||||
|  | set the DNS records needed to verify ownership. | ||||||
|  | 
 | ||||||
| ### tcp | ### tcp | ||||||
| 
 | 
 | ||||||
| The tcp system handles both *raw* and *tls-terminated* tcp network traffic | The tcp system handles both *raw* and *tls-terminated* tcp network traffic | ||||||
|  | |||||||
							
								
								
									
										122
									
								
								lib/ddns/challenge-responder.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										122
									
								
								lib/ddns/challenge-responder.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,122 @@ | |||||||
|  | 'use strict'; | ||||||
|  | 
 | ||||||
|  | // Much of this file was based on the `le-challenge-ddns` library (which we are not using
 | ||||||
|  | // here because it's method of setting records requires things we don't really want).
 | ||||||
|  | module.exports.create = function (deps, conf, utils) { | ||||||
|  | 
 | ||||||
|  |   function getReleventSessionId(domain) { | ||||||
|  |     var sessId; | ||||||
|  | 
 | ||||||
|  |     utils.iterateAllModules(function (mod, domainList) { | ||||||
|  |       // We return a truthy value in these cases because of the way the iterate function
 | ||||||
|  |       // handles modules grouped by domain. By returning true we are saying these domains
 | ||||||
|  |       // are "handled" and so if there are multiple modules we won't be given the rest.
 | ||||||
|  |       if (sessId) { return true; } | ||||||
|  |       if (domainList.indexOf(domain) < 0) { return true; } | ||||||
|  | 
 | ||||||
|  |       // But if the domains are relevant but we don't know how to handle the module we
 | ||||||
|  |       // return false to allow us to look at any other modules that might exist here.
 | ||||||
|  |       if (mod.type !== 'dns@oauth3.org')  { return false; } | ||||||
|  | 
 | ||||||
|  |       sessId = mod.tokenId || mod.token_id; | ||||||
|  |       return true; | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     return sessId; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function get(args, domain, challenge, done) { | ||||||
|  |     done(new Error("Challenge.get() does not need an implementation for dns-01. (did you mean Challenge.loopback?)")); | ||||||
|  |   } | ||||||
|  |   // same as get, but external
 | ||||||
|  |   function loopback(args, domain, challenge, done) { | ||||||
|  |     var challengeDomain = (args.test || '') + args.acmeChallengeDns + domain; | ||||||
|  |     require('dns').resolveTxt(challengeDomain, done); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   var activeChallenges = {}; | ||||||
|  |   async function removeAsync(args, domain) { | ||||||
|  |     var data = activeChallenges[domain]; | ||||||
|  |     if (!data) { | ||||||
|  |       console.warn(new Error('cannot remove DNS challenge for ' + domain + ': already removed')); | ||||||
|  |       return; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var session = await utils.getSession(data.sessId); | ||||||
|  |     var directives = await deps.OAUTH3.discover(session.token.aud); | ||||||
|  |     var apiOpts = { | ||||||
|  |       api: 'dns.unset' | ||||||
|  |     , session: session | ||||||
|  |     , type: 'TXT' | ||||||
|  |     , value: data.keyAuthDigest | ||||||
|  |     }; | ||||||
|  |     await deps.OAUTH3.api(directives.api, Object.assign({}, apiOpts, data.splitDomain)); | ||||||
|  | 
 | ||||||
|  |     delete activeChallenges[domain]; | ||||||
|  |   } | ||||||
|  |   async function setAsync(args, domain, challenge, keyAuth) { | ||||||
|  |     if (activeChallenges[domain]) { | ||||||
|  |       await removeAsync(args, domain, challenge); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     var sessId = getReleventSessionId(domain); | ||||||
|  |     if (!sessId) { | ||||||
|  |       throw new Error('no DDNS module handles the domain ' + domain); | ||||||
|  |     } | ||||||
|  |     var session = await utils.getSession(sessId); | ||||||
|  |     var directives = await deps.OAUTH3.discover(session.token.aud); | ||||||
|  | 
 | ||||||
|  |     // I'm not sure what role challenge is supposed to play since even in the library
 | ||||||
|  |     // this code is based on it was never used, but check for it anyway because ...
 | ||||||
|  |     if (!challenge || keyAuth) { | ||||||
|  |       console.warn(new Error('DDNS challenge missing challenge or keyAuth')); | ||||||
|  |     } | ||||||
|  |     var keyAuthDigest = require('crypto').createHash('sha256').update(keyAuth || '').digest('base64') | ||||||
|  |       .replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, ''); | ||||||
|  | 
 | ||||||
|  |     var challengeDomain = (args.test || '') + args.acmeChallengeDns + domain; | ||||||
|  |     var splitDomain = (await utils.splitDomains(directives.api, [challengeDomain]))[0]; | ||||||
|  | 
 | ||||||
|  |     var apiOpts = { | ||||||
|  |       api: 'dns.set' | ||||||
|  |     , session: session | ||||||
|  |     , type: 'TXT' | ||||||
|  |     , value: keyAuthDigest | ||||||
|  |     , ttl: args.ttl || 0 | ||||||
|  |     }; | ||||||
|  |     await deps.OAUTH3.api(directives.api, Object.assign({}, apiOpts, splitDomain)); | ||||||
|  | 
 | ||||||
|  |     activeChallenges[domain] = { | ||||||
|  |       sessId | ||||||
|  |     , keyAuthDigest | ||||||
|  |     , splitDomain | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return new Promise(res => setTimeout(res, 1000)); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   // It might be slightly easier to use arguments and apply, but the library that will use
 | ||||||
|  |   // this function counts the arguments we expect.
 | ||||||
|  |   function set(a, b, c, d, done) { | ||||||
|  |     setAsync(a, b, c, d).then(result => done(null, result), done); | ||||||
|  |   } | ||||||
|  |   function remove(a, b, c, done) { | ||||||
|  |     removeAsync(a, b, c).then(result => done(null, result), done); | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   function getOptions() { | ||||||
|  |     return { | ||||||
|  |       oauth3: 'oauth3.org' | ||||||
|  |     , debug: conf.debug | ||||||
|  |     , acmeChallengeDns: '_acme-challenge.' | ||||||
|  |     }; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   return { | ||||||
|  |     getOptions | ||||||
|  |   , set | ||||||
|  |   , get | ||||||
|  |   , remove | ||||||
|  |   , loopback | ||||||
|  |   }; | ||||||
|  | }; | ||||||
| @ -8,6 +8,7 @@ module.exports.create = function (deps, conf) { | |||||||
|   var utils = require('./utils').create(deps, conf); |   var utils = require('./utils').create(deps, conf); | ||||||
|   var loopback = require('./loopback').create(deps, conf, utils); |   var loopback = require('./loopback').create(deps, conf, utils); | ||||||
|   var dnsCtrl = require('./dns-ctrl').create(deps, conf, utils); |   var dnsCtrl = require('./dns-ctrl').create(deps, conf, utils); | ||||||
|  |   var challenge = require('./challenge-responder').create(deps, conf, utils); | ||||||
|   var tunnelClients = require('./tunnel-client-manager').create(deps, conf, utils); |   var tunnelClients = require('./tunnel-client-manager').create(deps, conf, utils); | ||||||
| 
 | 
 | ||||||
|   var loopbackDomain; |   var loopbackDomain; | ||||||
| @ -312,5 +313,6 @@ module.exports.create = function (deps, conf) { | |||||||
|   , getDeviceAddresses: dnsCtrl.getDeviceAddresses |   , getDeviceAddresses: dnsCtrl.getDeviceAddresses | ||||||
|   , recheckPubAddr:     recheckPubAddr |   , recheckPubAddr:     recheckPubAddr | ||||||
|   , updateConf:         updateConf |   , updateConf:         updateConf | ||||||
|  |   , challenge | ||||||
|   }; |   }; | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -159,11 +159,13 @@ module.exports.create = function (deps, config) { | |||||||
|     }); |     }); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   modules = {}; |   process.nextTick(function () { | ||||||
|   modules.tcpHandler = tcpHandler; |     modules = {}; | ||||||
|   modules.proxy = require('./proxy-conn').create(deps, config); |     modules.tcpHandler = tcpHandler; | ||||||
|   modules.tls   = require('./tls').create(deps, config, modules); |     modules.proxy = require('./proxy-conn').create(deps, config); | ||||||
|   modules.http  = require('./http').create(deps, config, modules); |     modules.tls   = require('./tls').create(deps, config, modules); | ||||||
|  |     modules.http  = require('./http').create(deps, config, modules); | ||||||
|  |   }); | ||||||
| 
 | 
 | ||||||
|   function updateListeners() { |   function updateListeners() { | ||||||
|     var current = listeners.list(); |     var current = listeners.list(); | ||||||
|  | |||||||
| @ -86,8 +86,7 @@ module.exports.create = function (deps, config, tcpMods) { | |||||||
|   , challenges: { |   , challenges: { | ||||||
|       'http-01': require('le-challenge-fs').create({ debug: config.debug }) |       'http-01': require('le-challenge-fs').create({ debug: config.debug }) | ||||||
|     , 'tls-sni-01': require('le-challenge-sni').create({ debug: config.debug }) |     , 'tls-sni-01': require('le-challenge-sni').create({ debug: config.debug }) | ||||||
|       // TODO dns-01
 |     , 'dns-01': deps.ddns.challenge | ||||||
|       //, 'dns-01': require('le-challenge-ddns').create({ debug: config.debug })
 |  | ||||||
|     } |     } | ||||||
|   , challengeType: 'http-01' |   , challengeType: 'http-01' | ||||||
| 
 | 
 | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user