def prepare(dhparamConfig, dhparamState, statedir, dhparamSecName): if 'openssl' != dhparamConfig['handler'].split('/')[0]: return keysize = 2048 if 'keysize' in dhparamConfig: keysize = dhparamConfig['keysize'] path = os.path.realpath(os.path.join(statedir, 'dhparams', dhparamSecName)) makeDir(os.path.dirname(path)) dhparamState.setOpStateRunning() try: rv = check_output( ('openssl', 'dhparam', '-out', str(path), str(int(keysize)))) log.info(rv) except CalledProcessError as e: log.error(e.output) time.sleep(1) raise (e) resDict = {} resDict['tmpfile'] = path if 'running' == dhparamState.opstate: dhparamState.registerResult(resDict) dhparamState.setOpStateDone() return rv
def prepare(config, state): subState = state.getSubstate('cert') for certSecName, certConfig in config['cert'].items(): if 'DEFAULT' == certSecName: continue if 'handler' not in certConfig: continue certState = subState.getSubstate(certSecName) if certState.isDone(): continue domains = [ k for k, v in config['domain'].items() if 'cert' in v and certSecName in v['cert'] ] log.info('Create certificate for section \"{}\"'.format(certSecName)) log.info(' -> {}'.format(', '.join(domains))) log.debug(certConfig) domainAccessTable = createDomainAccessTable(config, domains) handlerNames = certConfig['handler'].split('/') handler = __import__('cryptdomainmgr.modules.cert.handler' + str(handlerNames[0]), fromlist=('cryptdomainmgr', 'modules', 'cert')) statedir = getStateDir(config, 'cert', certSecName) handler.prepare(certConfig, certState, statedir, domains, domainAccessTable)
def rollover(serviceConfig, serviceState, state): serviceState.setOpStateWaiting() if not isReady(serviceConfig, state, ['dhparam', 'cert']): return serviceState.setOpStateRunning() certNames = serviceConfig['cert'] if 'auto' == serviceConfig['dirext'] or 'auto' == serviceConfig['dirint']: client = docker.DockerClient(base_url=serviceConfig['dockersock']) containers = [c for c in client.containers.list() if c.attrs['Name'][1:] == serviceConfig['container']] if len(containers) == 0: log.error("container does not exist") container = containers[0] log.info("Updating traefik container: {}".format(container.attrs['Name'])) destinations = [e.replace(' ','').split('=')[1] for e in container.attrs['Args'] if e.replace(' ','').split('=')[0] == '--providers.file.directory'] if len(destinations) == 0: log.error("traefik container misses providers.file.directory argument") destination = destinations[0] traefikProvidersFileDirectory = dch.dockerPathMap('/'+serviceConfig['container'], destination)[0] #traefikProvidersFileDirectory = mount['Source'] if 'auto' == serviceConfig['dirext'] else serviceConfig['dirext'] #'./configuration' traefikConfigFilename = os.path.join(traefikProvidersFileDirectory,'files/configuration.toml') traefikCertDir = os.path.join(traefikProvidersFileDirectory,'certs') traefikProvidersFileDirectoryMount = os.path.normpath(destination) if 'auto' == serviceConfig['dirint'] else serviceConfig['dirint'] # '/configuration' traefikConfigFilenameMount = os.path.join(traefikProvidersFileDirectoryMount,'files/configuration.toml') traefikCertDirMount = os.path.join(traefikProvidersFileDirectoryMount,'certs') log.info(" -> Volume: {}:{}".format(traefikProvidersFileDirectory, traefikProvidersFileDirectoryMount)) tcfc = '' for certName in certNames: certState = state.getSubstate('cert').getSubstate(certName).result certPath = os.path.join(traefikCertDir,certName,'cert.pem') keyPath = os.path.join(traefikCertDir,certName,'key.pem') certPathMount = os.path.join(traefikCertDirMount,certName,'cert.pem') keyPathMount = os.path.join(traefikCertDirMount,certName,'key.pem') makeDir(os.path.dirname(certPath)) copyfile(certState['fullchainfile'], certPath) log.info(' {} -> {}'.format(certState['fullchainfile'], certPath)) copyfile(certState['keyfile'], keyPath) log.info(' {} -> {}'.format(certState['keyfile'], keyPath)) tcfc += '[[tls.certificates]]\n certFile = "{}"\n keyFile = "{}"\n\n'.format(certPathMount, keyPathMount) makeDir(os.path.dirname(traefikConfigFilename)) with open(traefikConfigFilename,'w') as f: f.write(tcfc) # traefik reloads config and certs only if a random file is written in # traefik's config root directory specified by: # --providers.file.directory = <config root directory> with open(os.path.join(traefikProvidersFileDirectory,'reloadtrigger'), 'w') as f: f.write('') log.info(' -> Traefik reload') serviceState.setOpStateDone()
def copyCert(certConfig, certState): src = os.path.dirname(certState.result['fullchainfile']) for name in certState.result['san']: dest = os.path.join(certConfig['destination'], name) log.info(' {} -> {}'.format(src, dest)) try: rv = check_output(('cp', '-rfLT', str(src), str(dest))) except CalledProcessError as e: log.error(e.output) raise (e)
def copyConf(dkimConfig, dkimState): src = dkimState.result['signingconftemporaryfile'] dest = dkimConfig['signingconfdestinationfile'] log.info(' {} -> {}'.format(src, dest)) try: rv = check_output(('cp', '-rfLT', str(src), str(dest))) rv = check_output(('chown', '_rspamd:_rspamd', str(dest))) except CalledProcessError as e: log.error(e.output) raise (e)
def runCmd(cmd, stderr=sp.STDOUT, env=None): log.info("RUN CMD: {}".format(cmd)) stdout = "" try: for stdoutLine in runCmdGen(cmd, stderr, env): log.relog(" "+stdoutLine[:-1]) stdout += stdoutLine except sp.CalledProcessError as e: raise sp.CalledProcessError(e.returncode, e.cmd, stdout) return stdout
def copyDKIM(dkimConfig, dkimState): src = dkimState.result['keyfile'] dest = os.path.join(dkimConfig['keylocation'], dkimConfig['keyname']) log.info(' {} -> {}'.format(src, dest)) try: rv = check_output(('cp', '-rfLT', str(src), str(dest))) rv = check_output(('chown', '_rspamd:_rspamd', str(dest))) except CalledProcessError as e: log.error(e.output) raise (e)
def interprete(self, sh): self.sections = getSections(self.cp) log.info('Interpreting config sections') for secName in self.sections: log.info(' - {}'.format(secName)) handler = __import__('cryptdomainmgr.modules.' + str(secName) + '.confighandler', fromlist=('cryptdomainmgr', 'modules')) handler.interpreteConfig(self, sh) log.debug(self.config)
def run(self, confFile=None, forcePhase='next', confContent=''): self.readConfig(confFile, confContent) self.sh.load() self.sh.delete() # --next starts with prepare, if failed self.sh.resetOpStateRecursive() currentPhase = getCurrentPhase(self.sh, forcePhase) log.info('Running phase: {}'.format(currentPhase)) runPhase(self.cr, self.sh, currentPhase) nextphase = getNextPhase(currentPhase) self.sh.registerResult({'nextphase': nextphase}) self.sh.save()
def cleanup(config, state): subState = state.getSubstate('service') for serviceSecName, serviceConfig in config['service'].items(): if 'DEFAULT' == serviceSecName: continue serviceState = subState.getSubstate(serviceSecName) if serviceState.isDone(): continue log.info('Cleanup service for section \"{}\"'.format(serviceSecName)) log.debug(serviceConfig) handler = __import__('cryptdomainmgr.modules.service.handler'+str(serviceSecName), fromlist=('cryptdomainmgr', 'modules','service')) handler.cleanup(serviceConfig, serviceState, state)
def rollover(serviceConfig, serviceState, state): serviceState.setOpStateWaiting() if not isReady(serviceConfig, state, 'dkim'): return serviceState.setOpStateRunning() log.info(' -> Rspamd reload') try: rv = check_output(('sudo', 'systemctl', 'reload', 'rspamd')) # this is now working with newer version of rspamd except CalledProcessError as e: log.error(e.output) raise(e) serviceState.setOpStateDone()
def cleanup(config, state): subState = state.getSubstate('dhparam') for dhparamSecName, dhparamConfig in config['dhparam'].items(): if 'DEFAULT' == dhparamSecName: continue if 'handler' not in dhparamConfig: continue dhparamState = subState.getSubstate(dhparamSecName) if dhparamState.isDone(): continue log.info('Cleanup dhparam for section \"{}\"'.format(dhparamSecName)) dhparamState.setOpStateDone()
def rollover(serviceConfig, serviceState, state): serviceState.setOpStateWaiting() if not isReady(serviceConfig, state, ['cert', 'dhparam']): return serviceState.setOpStateRunning() log.info(' -> Postfix reload') try: rv = check_output(('systemctl', 'start', 'postfix')) rv = check_output(('systemctl', 'reload', 'postfix')) except CalledProcessError as e: log.error(e.output) raise (e) serviceState.setOpStateDone()
def rollover(config, state): subState = state.getSubstate('dhparam') for dhparamSecName, dhparamConfig in config['dhparam'].items(): if 'DEFAULT' == dhparamSecName: continue if 'handler' not in dhparamConfig: continue dhparamState = subState.getSubstate(dhparamSecName) if dhparamState.isDone(): continue log.info('Apply dhparams for section \"{}\"'.format(dhparamSecName)) copyDH(dhparamConfig, dhparamState) dhparamState.setOpStateDone()
def cleanup(config, state): subState = state.getSubstate('cert') for certSecName, certConfig in config['cert'].items(): if 'DEFAULT' == certSecName: continue if 'handler' not in certConfig: continue certState = subState.getSubstate(certSecName) if certState.isDone(): continue log.info('Cleanup certificate for section \"{}\"'.format(certSecName)) delOldCert(certConfig, certState) certState.setOpStateDone()
def cleanup(config, state): subState = state.getSubstate('domain') for domainSecName, domainConfig in config['domain'].items(): if 'DEFAULT' == domainSecName: continue if 'handler' not in domainConfig: continue domainState = subState.getSubstate(domainSecName) if domainState.isDone(): continue log.info('Create resource records for section \"{}\"'.format(domainSecName)) handlerNames = domainConfig['handler'].split('/') handler = __import__('cryptdomainmgr.modules.domain.handler'+str(handlerNames[0]), fromlist=('cryptdomainmgr', 'modules','domain')) handler.cleanup(domainConfig, domainState, domainSecName, state)
def cleanup(config, state): subState = state.getSubstate('dkim') for dkimSecName, dkimConfig in config['dkim'].items(): if 'DEFAULT' == dkimSecName: continue if 'handler' not in dkimConfig: continue dkimState = subState.getSubstate(dkimSecName) if dkimState.isDone(): continue log.info("Cleanup DKIM key for dkim-section: \"{}\"".format(dkimSecName)) handlerNames = dkimConfig['handler'].split('/') handler = __import__('cryptdomainmgr.modules.dkim.handler'+str(handlerNames[0]), fromlist=('cryptdomainmgr','modules','dkim')) handler.cleanup(dkimConfig, dkimState)
def rollover(config, state): subState = state.getSubstate('service') for serviceSecName, serviceConfig in config['service'].items(): if 'DEFAULT' == serviceSecName: continue if 'handler' not in serviceConfig: continue serviceState = subState.getSubstate(serviceSecName) if serviceState.isDone(): continue log.info('Rollover service for section \"{}\"'.format(serviceSecName)) log.debug(serviceConfig) handlerNames = serviceConfig['handler'].split('/') handler = __import__('cryptdomainmgr.modules.service.handler'+str(handlerNames[0]), fromlist=('cryptdomainmgr', 'modules','service')) handler.rollover(serviceConfig, serviceState, state)
def copyDH(dhparamConfig, dhparamState): if 'tmpfile' not in dhparamState.result: return src = os.path.realpath(dhparamState.result['tmpfile']) dest = os.path.realpath(dhparamConfig['filename']) try: os.makedirs(os.path.dirname(dest)) except: pass log.info(' {} -> {}'.format(src, dest)) try: rv = check_output(('cp', '-rfLT', str(src), str(dest))) except CalledProcessError as e: log.error(e.output) raise(e)
def rollover(serviceConfig, serviceState, state): serviceState.setOpStateWaiting() if not isReady(serviceConfig, state, 'dkim'): return serviceState.setOpStateRunning() log.info(' -> Rspamd reload') try: rv = check_output(('systemctl', 'start', 'rspamd')) #rv = check_output(('systemctl', 'reload', 'rspamd')) # this is not working with rspamd #rv = check_output(('rspamadm', 'control', 'reload')) # only restart works - bug in rspamd rv = check_output(('systemctl', 'restart', 'rspamd')) except CalledProcessError as e: log.error(e.output) raise (e) serviceState.setOpStateDone()
def delOldCert(certConfig, certState): preserve = ['fullchainfile', 'certfile', 'keyfile', 'chainfile'] preserveFiles = set([certState.result[e] for e in preserve]) preserveFiles.update(set([os.path.realpath(e) for e in preserveFiles])) dirs = set([os.path.dirname(e) for e in preserveFiles]) dirs = [e for e in dirs if os.path.isdir(e)] allFiles = set([os.path.join(d, f) for d in dirs for f in os.listdir(d)]) allFiles = set([e for e in allFiles if os.path.isfile(e)]) removeFiles = allFiles - preserveFiles for e in removeFiles: log.info(' rm {}'.format(e)) try: rv = check_output(('rm', str(e))) except CalledProcessError as e: log.error(e.output) raise (e)
def prepare(config, state): subState = state.getSubstate('dhparam') for dhparamSecName, dhparamConfig in config['dhparam'].items(): if 'DEFAULT' == dhparamSecName: continue if 'handler' not in dhparamConfig: continue dhparamState = subState.getSubstate(dhparamSecName) if dhparamState.isDone(): continue log.info('Create dhparams for section \"{}\"'.format(dhparamSecName)) log.debug(dhparamConfig) handlerNames = dhparamConfig['handler'].split('/') handler = __import__('cryptdomainmgr.modules.dhparam.handler'+str(handlerNames[0]), fromlist=('cryptdomainmgr', 'modules','dhparam')) statedir = getStateDir(config, 'dhparam', dhparamSecName) handler.prepare(dhparamConfig, dhparamState, statedir, dhparamSecName)
def copyCert(certConfig, certState): src = os.path.dirname(certState.result['fullchainfile']) for name in certState.result['san']: dest = os.path.join(certConfig['destination'], name) log.info(' {} -> {}'.format(src, dest)) try: makeDir(str(dest)) for k in ['fullchainfile', 'chainfile', 'certfile', 'keyfile']: lnksrc = certState.result[k] lnkdst = os.path.join(os.path.dirname(lnksrc), os.readlink(lnksrc)) shutil.copy2(lnkdst, os.path.join(dest, os.path.basename(lnksrc))) except CalledProcessError as e: log.error(e.output) raise (e)
def copyDKIM(dkimConfig, dkimState): src = dkimState.result['keyfile'] dest = os.path.join(dkimConfig['keylocation'], dkimConfig['keyname']) log.info(' {} -> {}'.format(src, dest)) try: rv = check_output(('mkdir', '-p', os.path.dirname(str(dest)))) except CalledProcessError as e: log.error("Failed to create directory path for {}".format(str(dest))) try: rv = check_output(('cp', '-rfLT', str(src), str(dest))) except CalledProcessError as e: log.error("Failed to copy file") log.error(" {} -> {}".format(str(src), str(dest))) try: rv = check_output(('chown', '_rspamd:_rspamd', str(dest))) except CalledProcessError as e: log.error("Failed to change ownership of {}".format(str(dest)))
def copyConf(dkimConfig, dkimState): src = dkimState.result['signingconftemporaryfile'] dest = dkimConfig['signingconfdestinationfile'] log.info(' {} -> {}'.format(src, dest)) try: rv = check_output(('sudo', 'mkdir', '-p', os.path.dirname(str(dest)))) except CalledProcessError as e: log.error("Failed to create directory path for {}".format(str(dest))) try: rv = check_output(('sudo', 'cp', '-rfLT', str(src), str(dest))) except CalledProcessError as e: log.error("Failed to copy file") log.error(" {} -> {}".format(str(src), str(dest))) try: rv = check_output(('sudo', 'chown', '_rspamd:_rspamd', str(dest))) except CalledProcessError as e: log.error("Failed to change ownership of {}".format(str(dest)))
def postwait(config, state): #print(config) subState = state.getSubstate('cdm') if subState.isDone(): return for cdmSecName, cdmConfig in config['cdm'].items(): cdmState = subState.getSubstate(cdmSecName) if cdmState.isDone(): continue if not isReady(cdmConfig, state, ['dhparam', 'cert', 'domain', 'dkim', 'service']): #if not isReady(cdmConfig, state, ['cert']): return if 'postwait' in cdmConfig: T = cdmConfig['postwait'] log.info('Wait {} s after run!'.format(T)) time.sleep(int(T)) subState.setOpStateDone()
def infoRecord(recordDict, operation='add'): rrType = recordDict['type'] v = recordDict['content'].split('v=') if 1 < len(v): v = v[1].split(';')[0].split(' ')[0].split('1')[0] rrType = v.upper() if 5 < len(recordDict['name']): if '_adsp.' == recordDict['name'][:6]: rrType = 'ADSP' if 15 < len(recordDict['name']): if '_acme-challenge.' == recordDict['name'][:16]: rrType = 'ACME' if 'content' in recordDict: log.info('{} {} for {} : {}'.format(operation, rrType, recordDict['name'], recordDict['content'])) else: log.info('{} {} for {}'.format(operation, rrType, recordDict['name']))
def prepare(config, state): subState = state.getSubstate('domain') for domainSecName, domainConfig in config['domain'].items(): if 'DEFAULT' == domainSecName: continue if 'handler' not in domainConfig: continue domainState = subState.getSubstate(domainSecName) if domainState.isDone(): continue log.info( 'Create resource records for section \"{}\"'.format(domainSecName)) domainState.setOpStateWaiting() if not isReady(domainConfig, state, ['cert', 'dkim']): return domainState.setOpStateRunning() log.debug(domainConfig) handlerNames = domainConfig['handler'].split('/') handler = __import__('cryptdomainmgr.modules.domain.handler' + str(handlerNames[0]), fromlist=('cryptdomainmgr', 'modules', 'domain')) handler.prepare(domainConfig, domainState, domainSecName, state)
def setTLSA(domainConfig, domainState, domainSecName, dnsup, state, add=True, delete=True): rrState = domainState.getSubstate('settlsa') if rrState.isDone(): return True if 'cert' in domainConfig and ('tlsaAggrAdd' in domainConfig or 'tlsaAggrDel' in domainConfig): rrState.setOpStateWaiting() tlsaPres = [] for certSec in domainConfig['cert']: if not isCertReady(state, certSec): return False rrState.setOpStateRunning() cert = getFullchain(state, certSec) if cert is None: log.info( 'not deploying TLSA record for {} (no certificate)'.format( domainSecName)) else: log.info( 'deploying TLSA record for {} (certificate found)'.format( domainSecName)) sanList = getCertSAN(cert) log.info(' -> found certificate: {} for: {}'.format( cert, b', '.join(sanList))) if domainSecName.encode() not in sanList: log.error('{} not in certificate {}'.format( domainSecName, cert)) tlsaAdd = [ dict(e, filename=cert) for e in domainConfig['tlsaAggrAdd'] if 'op' in e if 'auto' == e['op'] ] tlsaPres.extend(tlsaAdd) if add is True: dnsup.addTLSA(domainSecName, tlsaAdd) log.info('removing old TLSA record for {}'.format(domainSecName)) if delete is True: tlsaDel = domainConfig['tlsaAggrDel'] dnsup.delTLSA(domainSecName, tlsaDel, tlsaPres) rrState.setOpStateDone() return True
def callAPI(self, method, params): for i in range(10): if i > 0: log.info('INWX api wait {} s before retry'.format(i**3)) time.sleep(i**3) rv = self.__conn.call_api(api_method=method, method_params=params) if 2400 == rv['code']: log.info('INWX api error code: {}'.format(rv['code'])) continue if 2500 == rv['code']: log.info('INWX api error code: {}'.format(rv['code'])) continue if 2502 == rv['code']: log.info('INWX api error code: {}'.format(rv['code'])) continue return rv log.error('INWX api retry limit exceeded!') log.error('INWX api error code was: {}'.format(rv['code'])) return rv