def parseNestedEntry(key, value, default=[], keySplitPattern='.', valueSplitPattern=':'): if keySplitPattern == '': keyList = [key] else: keyList = key.split(keySplitPattern) if valueSplitPattern == '': valueList = [value] else: valueList = value.split(valueSplitPattern) addList = list(default) rv = {} subtractMode = False if '+' == keyList[-1][-1]: keyList[-1] = keyList[-1][:-1].strip() elif '-' == keyList[-1][-1]: keyList[-1] = keyList[-1][:-1].strip() subtractMode = True else: rv['delList'] = list(keyList) addList[:len(keyList)] = list(keyList) for i, e in enumerate(valueList): try: addList[-i] = e except IndexError: log.warn('Too many arguments in parameter!') if subtractMode is True: rv['delListWithContent'] = addList else: rv['addList'] = addList return rv
def formatSRVentry(name, srvDict): if type(srvDict) is list: return [formatSRVentry(name, e) for e in srvDict] srv = srvDict for k in ['service', 'proto', 'prio', 'weight', 'port', 'server']: if k not in srv: log.warn('Missing member \"{}\" in SRV entry!'.format(k)) return {} return { 'name': '_{x[service]}._{x[proto]}.{name}'.format(x=srv, name=str(name)), 'type': 'SRV', 'prio': srv['prio'], 'content': '{x[weight]} {x[port]} {x[server]}'.format(x=srv) }
def interpreteConfig(cr, sh): domainconfigOrig = cr.getRawConfigOf('domain', True) domainconfig = applyDefault( domainconfigOrig ) # must be here because following section depends on default values for domain, content in domainconfig.items(): for f in [ 'A', 'AAAA', 'DKIM', 'TLSA', 'MX', 'SRV', 'CAA', 'DMARC', 'SOA', 'SPF', 'Handler', 'Cert' ]: domainconfig[domain].update( (globals()['interprete{}'.format(f)](content))) certSections = set(cr.getRawConfigOf('cert').keys()) certEntry = set({}) certDoesNotExist = set({}) if 'cert' in domainconfig[domain]: certEntry = set(domainconfig[domain]['cert']) if 'auto' in certEntry: certEntry = certEntry - 'auto' + certSections certDoesNotExist = certEntry - certSections for missing in certDoesNotExist: log.warn( "Section cert:{} referenced in domain:{} does not exist!". format(missing, domain)) domainconfig[domain]['cert'] = list( set(domainconfig[domain]['cert']) - certDoesNotExist) addDKIMcont = set( [e['content'] for e in domainconfig[domain]['dkimAggrAdd']]) dkimSections = set(cr.getRawConfigOf('dkim').keys()) dkimDoesNotExist = addDKIMcont - dkimSections for missing in dkimDoesNotExist: log.warn("Section dkim:{} referenced in domain:{} does not exist!". format(missing, domain)) for e in domainconfig[domain]['dkimAggrAdd']: if e['content'] not in dkimDoesNotExist: del e if 'requires' not in domainconfig[domain]: domainconfig[domain]['requires'] = {} domainconfig[domain]['requires'][ 'dkim'] = addDKIMcont - dkimDoesNotExist domainconfig[domain]['requires']['cert'] = certEntry - certDoesNotExist log.debug(domainconfig) cr.updateConfig({'domain': domainconfig}) return domainconfig
def addCAA(self, name, caaDict): try: self.addList({'name': str(name), 'type': 'CAA'}, genCAA(caaDict)) except KeyError as e: log.warn('Not adding CAA record!')
def prepare(certConfig, certState, statedir, domainList, domainAccessTable): if 'dehydrated' != certConfig['handler'].split('/')[0]: return if 0 == len(domainList): return email = certConfig['email'] keysize = 4096 if 'keysize' in certConfig: keysize = certConfig['keysize'] if 'extraflags' in certConfig: extraFlags = certConfig['extraflags'] extraFlags = [e if '-' == e[0] else '--' + e for e in extraFlags] if '--staging' in extraFlags: ca = "https://acme-staging-v02.api.letsencrypt.org/directory" extraFlags.remove('--staging') if 'staging' in extraFlags: extraFlags.remove('staging') elif 'ca' in certConfig: ca = certConfig['ca'] else: ca = "https://acme-v02.api.letsencrypt.org/directory" makeDir(os.path.realpath(statedir)) confFilename = os.path.normpath(os.path.join(statedir, 'dehydrated.conf')) confFile = open(confFilename, 'w') confFile.write('CA={}\n'.format(str(ca))) confFile.write('CONTACT_EMAIL={}\n'.format(str(email))) confFile.write('KEYSIZE={}\n'.format(int(keysize))) confFile.write('CERTDIR={}\n'.format(os.path.join(statedir, 'certs'))) confFile.write('\n') confFile.close() here = os.path.dirname(os.path.realpath(__file__)) args = [ os.path.join(here, 'dehydrated/dehydrated'), '-f', confFilename, '--accept-terms', '-c', '-t', 'dns-01', '-k', os.path.join(here, 'hook.sh') ] log.debug(extraFlags) args.extend(extraFlags) for d in domainList: args.extend(['-d', str(d)]) log.debug(args) certState.setOpStateRunning() i = 2 while True: try: log.info('Starting DNS-01 authentication') rv = runCmd(' '.join(args), stderr=STDOUT, env=dict(os.environ, DOMAINACCESSTABLE=domainAccessTable, STATEDIR=statedir, WAITSEC="{}".format(3**i))) break except CalledProcessError as e: if 'ERROR: Lock file' in e.output: lockFilename = e.output.split('ERROR: Lock file \'')[-1].split( '\' present, aborting')[0] log.warn('Lock file from imcomplete run found: {}'.format( lockFilename)) log.warn(' -> Removing') os.remove(lockFilename) elif 'Incorrect TXT record' in e.output: log.info( ' -> Invalid DNS-01 challenge, maybe due to DNS caching interval. Trying to wait longer!' ) i += 1 log.info( ' -> Will wait {} s to give challenge time to propagate DNS cache.' .format(3**i)) elif 'NXDOMAIN' in e.output: log.info( ' -> Missing DNS-01 challenge, maybe due to DNS caching interval. Trying to wait longer!' ) i += 1 log.info( ' -> Will wait {} s to give challenge time to propagate DNS cache.' .format(3**i)) else: raise (e) if 9 == i: raise (e) res = [] rv = rv.splitlines() for s, e in enumerate(rv): if '---- DEPLOYMENTRESULT ----' == e[:len('---- DEPLOYMENTRESULT ----' )]: break for i, e in enumerate(rv[s + 1:]): if '---- END DEPLOYMENTRESULT ----' == e[:len( '---- END DEPLOYMENTRESULT ----')]: break res.append(e) resDict = {e.split('=')[0].lower(): e.split('=')[1] for e in res} resDict['san'] = list(domainList) if 'running' == certState.opstate: certState.registerResult(resDict) certState.setOpStateDone() return rv