def challenge_factory(self, name, challenges, path): sni_todo = [] # Since a single invocation of SNI challenge can satsify multiple # challenges. We must keep track of all the challenges it satisfies sni_satisfies = [] challenge_objs = [] challenge_obj_indicies = [] for c in path: if challenges[c]["type"] == "dvsni": logger.info("\tDVSNI challenge for name %s." % name) sni_satisfies.append(c) sni_todo.append((str(name), str(challenges[c]["r"]), str(challenges[c]["nonce"]))) elif challenges[c]["type"] == "recoveryToken": logger.info("\tRecovery Token Challenge for name: %s." % name) challenge_objs_indicies.append(c) challenge_objs.append(RecoveryToken()) else: logger.fatal("Challenge not currently supported") sys.exit(82) if sni_todo: # SNI_Challenge can satisfy many sni challenges at once so only # one "challenge object" is issued for all sni_challenges challenge_objs.append( SNI_Challenge(sni_todo, os.path.abspath(self.key_file), self.config)) challenge_obj_indicies.append(sni_satisfies) logger.debug(sni_todo) return challenge_objs, challenge_obj_indicies
def verify_identity(self, c): path = self.gen_challenge_path(c["challenges"], c.get("combinations", None)) logger.info("Peforming the following challenges:") # Every indicies element is a list of integers referring to which # challenges in the master list the challenge object satisfies # Single Challenge objects that can satisfy multiple server challenges # mess up the order of the challenges, thus requiring the indicies challenge_objs, indicies = self.challenge_factory( self.names[0], c["challenges"], path) responses = [None] * len(c["challenges"]) # Perform challenges and populate responses for i, c_obj in enumerate(challenge_objs): if not c_obj.perform(): logger.fatal("Challenge Failed") sys.exit(1) for index in indicies[i]: responses[index] = c_obj.generate_response() logger.info("Configured Apache for challenges; \ waiting for verification...") return responses, challenge_objs
def is_expected_msg(self, msg_dict, expected, delay=3, rounds = 20): for i in range(rounds): if msg_dict["type"] == expected: return msg_dict elif msg_dict["type"] == "error": logger.error("%s: %s - More Info: %s" % (msg_dict["error"], msg_dict.get("message", ""), msg_dict.get("moreInfo", ""))) raise Exception(msg_dict["error"]) elif msg_dict["type"] == "defer": logger.info("Waiting for %d seconds..." % delay) time.sleep(delay) msg_dict = self.send(self.status_request(msg_dict["token"])) else: logger.fatal("Received unexpected message") logger.fatal("Expected: %s" % expected) logger.fatal("Received: " + msg_dict) sys.exit(33) logger.error("Server has deferred past the max of %d seconds" % (rounds * delay)) return None
def verify_identity(self, c): path = self.gen_challenge_path( c["challenges"], c.get("combinations", None)) logger.info("Performing the following challenges:") # Every indicies element is a list of integers referring to which # challenges in the master list the challenge object satisfies # Single Challenge objects that can satisfy multiple server challenges # mess up the order of the challenges, thus requiring the indicies challenge_objs, indicies = self.challenge_factory( self.names[0], c["challenges"], path) responses = [None] * len(c["challenges"]) # Perform challenges and populate responses for i, c_obj in enumerate(challenge_objs): if not c_obj.perform(): logger.fatal("Challenge Failed") sys.exit(1) for index in indicies[i]: responses[index] = c_obj.generate_response() logger.info("Configured Apache for challenges; " + "waiting for verification...") return responses, challenge_objs
def challenge_factory(self, name, challenges, path): sni_todo = [] # Since a single invocation of SNI challenge can satisfy multiple # challenges. We must keep track of all the challenges it satisfies sni_satisfies = [] challenge_objs = [] challenge_obj_indicies = [] for c in path: if challenges[c]["type"] == "dvsni": logger.info(" DVSNI challenge for name %s." % name) sni_satisfies.append(c) sni_todo.append( (str(name), str(challenges[c]["r"]), str(challenges[c]["nonce"])) ) elif challenges[c]["type"] == "recoveryToken": logger.info("\tRecovery Token Challenge for name: %s." % name) challenge_objs_indicies.append(c) challenge_objs.append(RecoveryToken()) else: logger.fatal("Challenge not currently supported") sys.exit(82) if sni_todo: # SNI_Challenge can satisfy many sni challenges at once so only # one "challenge object" is issued for all sni_challenges challenge_objs.append(SNI_Challenge( sni_todo, os.path.abspath(self.key_file), self.config)) challenge_obj_indicies.append(sni_satisfies) logger.debug(sni_todo) return challenge_objs, challenge_obj_indicies
def certificate_request(self, csr_der, key): logger.info("Preparing and sending CSR..") return { "type": "certificateRequest", "csr": jose.b64encode_url(csr_der), "signature": crypto_util.create_sig(csr_der, self.key_file) }
def verify_identity(self, c): path = self.gen_challenge_path( c["challenges"], c.get("combinations", None)) logger.info("Performing the following challenges:") # Every indices element is a list of integers referring to which # challenges in the master list the challenge object satisfies # Single Challenge objects that can satisfy multiple server challenges # mess up the order of the challenges, thus requiring the indices challenge_objs, indices = self.challenge_factory( self.names[0], c["challenges"], path) responses = ["null"] * len(c["challenges"]) # Perform challenges for i, c_obj in enumerate(challenge_objs): response = "null" if c_obj["type"] in CONFIG.CONFIG_CHALLENGES: response = self.config.perform(c_obj) else: # Handle RecoveryToken type challenges pass for index in indices[i]: responses[index] = response logger.info("Configured Apache for challenges; " + "waiting for verification...") return responses, challenge_objs
def list_certs_keys(self): list_file = CERT_KEY_BACKUP + "LIST" certs = [] if not os.path.isfile(CERT_KEY_BACKUP + "LIST"): logger.info( "You don't have any certificates saved from letsencrypt") return with open(list_file, 'rb') as csvfile: csvreader = csv.reader(csvfile) for row in csvreader: c = crypto_util.get_cert_info(row[1]) b_k = CERT_KEY_BACKUP + os.path.basename(row[2]) + "_" + row[0] b_c = CERT_KEY_BACKUP + os.path.basename(row[1]) + "_" + row[0] c["orig_key_file"] = row[2] c["orig_cert_file"] = row[1] c["idx"] = int(row[0]) c["backup_key_file"] = b_k c["backup_cert_file"] = b_c certs.append(c) if certs: self.choose_certs(certs) else: display.generic_notification("There are not any trusted \ Let's Encrypt certificates for this server.")
def cleanup_challenges(self, challenge_objs): logger.info("Cleaning up challenges...") for c in challenge_objs: if c["type"] in CONFIG.CONFIG_CHALLENGES: self.config.cleanup() else: #Handle other cleanup if needed pass
def cleanup_challenges(self, challenges): logger.info("Cleaning up challenges...") for chall in challenges: if chall["type"] in CONFIG.CONFIG_CHALLENGES: self.config.cleanup() else: # Handle other cleanup if needed pass
def redirect_to_ssl(self, vhost): for ssl_vh in vhost: success, redirect_vhost = self.config.enable_redirect(ssl_vh) logger.info("\nRedirect vhost: " + redirect_vhost.file + " - " + str(success)) # If successful, make sure redirect site is enabled if success: self.config.enable_site(redirect_vhost)
def challenge_factory(self, name, challenges, path): """ :param name: TODO :type name: TODO :param challanges: A list of challenges from ACME "challenge" server message to be fulfilled by the client in order to prove possession of the identifier. :type challenges: list :param path: List of indices from `challenges`. :type path: list :returns: A pair of TODO :rtype: tuple """ sni_todo = [] # Since a single invocation of SNI challenge can satisfy multiple # challenges. We must keep track of all the challenges it satisfies sni_satisfies = [] challenge_objs = [] challenge_obj_indices = [] for index in path: chall = challenges[index] if chall["type"] == "dvsni": logger.info(" DVSNI challenge for name %s." % name) sni_satisfies.append(index) sni_todo.append( (str(name), str(chall["r"]), str(chall["nonce"]))) elif chall["type"] == "recoveryToken": logger.info("\tRecovery Token Challenge for name: %s." % name) challenge_obj_indices.append(index) challenge_objs.append({ type: "recoveryToken", }) else: logger.fatal("Challenge not currently supported") sys.exit(82) if sni_todo: # SNI_Challenge can satisfy many sni challenges at once so only # one "challenge object" is issued for all sni_challenges challenge_objs.append({ "type": "dvsni", "listSNITuple": sni_todo, "dvsni_key": os.path.abspath(self.key_file), }) challenge_obj_indices.append(sni_satisfies) logger.debug(sni_todo) return challenge_objs, challenge_obj_indices
def get_key_csr_pem(self, csr_return_format='der'): """Return key and CSR, generate if necessary. Returns key and CSR using provided files or generating new files if necessary. Both will be saved in PEM format on the filesystem. The CSR can optionally be returned in DER format as the CSR cannot be loaded back into M2Crypto. :param csr_return_format: If "der" returned CSR is in DER format, PEM otherwise. :param csr_return_format: str :returns: A pair of `(key, csr)`, where `key` is PEM encoded `str` and `csr` is PEM/DER (depedning on `csr_return_format` encoded `str`. :rtype: tuple """ key_pem = None csr_pem = None if not self.key_file: key_pem = crypto_util.make_key(CONFIG.RSA_KEY_SIZE) # Save file le_util.make_or_verify_dir(CONFIG.KEY_DIR, 0o700) key_f, self.key_file = le_util.unique_file( os.path.join(CONFIG.KEY_DIR, "key-letsencrypt.pem"), 0o600) key_f.write(key_pem) key_f.close() logger.info("Generating key: %s" % self.key_file) else: try: key_pem = open(self.key_file).read().replace("\r", "") except: logger.fatal("Unable to open key file: %s" % self.key_file) sys.exit(1) if not self.csr_file: csr_pem, csr_der = crypto_util.make_csr(self.key_file, self.names) # Save CSR le_util.make_or_verify_dir(CONFIG.CERT_DIR, 0o755) csr_f, self.csr_file = le_util.unique_file( os.path.join(CONFIG.CERT_DIR, "csr-letsencrypt.pem"), 0o644) csr_f.write(csr_pem) csr_f.close() logger.info("Creating CSR: %s" % self.csr_file) else: try: csr = M2Crypto.X509.load_request(self.csr_file) csr_pem, csr_der = csr.as_pem(), csr.as_der() except: logger.fatal("Unable to open CSR file: %s" % self.csr_file) sys.exit(1) if csr_return_format == 'der': return key_pem, csr_der else: return key_pem, csr_pem
def challenge_factory(self, name, challenges, path): """ :param name: TODO :type name: TODO :param challanges: A list of challenges from ACME "challenge" server message to be fulfilled by the client in order to prove possession of the identifier. :type challenges: list :param path: List of indices from `challenges`. :type path: list :returns: A pair of TODO :rtype: tuple """ sni_todo = [] # Since a single invocation of SNI challenge can satisfy multiple # challenges. We must keep track of all the challenges it satisfies sni_satisfies = [] challenge_objs = [] challenge_obj_indices = [] for index in path: chall = challenges[index] if chall["type"] == "dvsni": logger.info(" DVSNI challenge for name %s." % name) sni_satisfies.append(index) sni_todo.append((str(name), str(chall["r"]), str(chall["nonce"]))) elif chall["type"] == "recoveryToken": logger.info("\tRecovery Token Challenge for name: %s." % name) challenge_obj_indices.append(index) challenge_objs.append({ type: "recoveryToken", }) else: logger.fatal("Challenge not currently supported") sys.exit(82) if sni_todo: # SNI_Challenge can satisfy many sni challenges at once so only # one "challenge object" is issued for all sni_challenges challenge_objs.append({ "type": "dvsni", "listSNITuple": sni_todo, "dvsni_key": os.path.abspath(self.key_file), }) challenge_obj_indices.append(sni_satisfies) logger.debug(sni_todo) return challenge_objs, challenge_obj_indices
def redirect_to_ssl(self, vhost): for ssl_vh in vhost: success, redirect_vhost = self.config.redirect_all_ssl(ssl_vh) logger.info("\nRedirect vhost: " + redirect_vhost.file + " - " + str(success)) # If successful, make sure redirect site is enabled if success: if not self.config.is_site_enabled(redirect_vhost.file): self.config.enable_site(redirect_vhost) logger.info("Enabling available site: " + redirect_vhost.file)
def acme_certificate(self, csr_der): """Handle ACME "certificate" phase. :param str csr_der: CSR in DER format. :returns: ACME "certificate" message. :rtype: dict """ logger.info("Preparing and sending CSR..") return self.send_and_receive_expected( acme.certificate_request(csr_der, self.privkey.pem), "certificate")
def acme_certificate(self, csr_der): """Handle ACME "certificate" phase. :param csr_der: CSR in DER format. :type csr_der: str :returns: ACME "certificate" message. :rtype: dict """ logger.info("Preparing and sending CSR..") return self.send_and_receive_expected( acme.certificate_request(csr_der, self.key_file), "certificate")
def cleanup_challenges(self, challenges): """Cleanup configuration challenges :param dict challenges: challenges from a challenge message """ logger.info("Cleaning up challenges...") for chall in challenges: if chall["type"] in CONFIG.CONFIG_CHALLENGES: self.config.cleanup() else: # Handle other cleanup if needed pass
def redirect_to_ssl(self, vhost): """Redirect all traffic from HTTP to HTTPS :param vhost: list of ssl_vhosts :type vhost: :class:`apache_configurator.VH` """ for ssl_vh in vhost: success, redirect_vhost = self.config.enable_redirect(ssl_vh) logger.info("\nRedirect vhost: " + redirect_vhost.file + " - " + str(success)) # If successful, make sure redirect site is enabled if success: self.config.enable_site(redirect_vhost)
def list_certs_keys(self): list_file = os.path.join(CONFIG.CERT_KEY_BACKUP, "LIST") certs = [] if not os.path.isfile(list_file): logger.info( "You don't have any certificates saved from letsencrypt") return c_sha1_vh = {} for (cert, _, path) in self.config.get_all_certs_keys(): try: c_sha1_vh[M2Crypto.X509.load_cert(cert).get_fingerprint( md='sha1')] = path except: continue with open(list_file, 'rb') as csvfile: csvreader = csv.reader(csvfile) for row in csvreader: cert = crypto_util.get_cert_info(row[1]) b_k = os.path.join(CONFIG.CERT_KEY_BACKUP, os.path.basename(row[2]) + "_" + row[0]) b_c = os.path.join(CONFIG.CERT_KEY_BACKUP, os.path.basename(row[1]) + "_" + row[0]) cert.update({ "orig_key_file": row[2], "orig_cert_file": row[1], "idx": int(row[0]), "backup_key_file": b_k, "backup_cert_file": b_c, "installed": c_sha1_vh.get(cert["fingerprint"], ""), }) certs.append(cert) if certs: self.choose_certs(certs) else: display.generic_notification( "There are not any trusted Let's Encrypt " "certificates for this server.")
def get_key_csr_pem(self, csr_return_format = 'der'): """ Returns key and CSR using provided files or generating new files if necessary. Both will be saved in pem format on the filesystem. The CSR can optionally be returned in DER format as the CSR cannot be loaded back into M2Crypto. """ key_pem = None csr_pem = None if not self.key_file: key_pem = crypto_util.make_key(RSA_KEY_SIZE) # Save file le_util.make_or_verify_dir(KEY_DIR, 0700) key_f, self.key_file = le_util.unique_file( KEY_DIR + "key-letsencrypt.pem", 0600) key_f.write(key_pem) key_f.close() logger.info("Generating key: %s" % self.key_file) else: try: key_pem = open(self.key_file).read().replace("\r", "") except: logger.fatal("Unable to open key file: %s" % self.key_file) sys.exit(1) if not self.csr_file: csr_pem, csr_der = crypto_util.make_csr(self.key_file, self.names) # Save CSR le_util.make_or_verify_dir(CERT_DIR, 0755) csr_f, self.csr_file = le_util.unique_file( CERT_DIR + "csr-letsencrypt.pem", 0644) csr_f.write(csr_pem) csr_f.close() logger.info("Creating CSR: %s" % self.csr_file) else: #TODO fix this der situation try: csr_pem = open(self.csr_file).read().replace("\r", "") except: logger.fatal("Unable to open CSR file: %s" % self.csr_file) sys.exit(1) if csr_return_format == 'der': return key_pem, csr_der return key_pem, csr_pem
def get_key_csr_pem(self, csr_return_format='der'): """ Returns key and CSR using provided files or generating new files if necessary. Both will be saved in pem format on the filesystem. The CSR can optionally be returned in DER format as the CSR cannot be loaded back into M2Crypto. """ key_pem = None csr_pem = None if not self.key_file: key_pem = crypto_util.make_key(RSA_KEY_SIZE) # Save file le_util.make_or_verify_dir(KEY_DIR, 0700) key_f, self.key_file = le_util.unique_file( KEY_DIR + "key-letsencrypt.pem", 0600) key_f.write(key_pem) key_f.close() logger.info("Generating key: %s" % self.key_file) else: try: key_pem = open(self.key_file).read().replace("\r", "") except: logger.fatal("Unable to open key file: %s" % self.key_file) sys.exit(1) if not self.csr_file: csr_pem, csr_der = crypto_util.make_csr(self.key_file, self.names) # Save CSR le_util.make_or_verify_dir(CERT_DIR, 0755) csr_f, self.csr_file = le_util.unique_file( CERT_DIR + "csr-letsencrypt.pem", 0644) csr_f.write(csr_pem) csr_f.close() logger.info("Creating CSR: %s" % self.csr_file) else: #TODO fix this der situation try: csr_pem = open(self.csr_file).read().replace("\r", "") except: logger.fatal("Unable to open CSR file: %s" % self.csr_file) sys.exit(1) if csr_return_format == 'der': return key_pem, csr_der return key_pem, csr_pem
def is_expected_msg(self, response, expected, delay=3, rounds=20): """Is reponse expected ACME message? :param response: ACME response message from server. :type response: dict :param expected: Name of the expected response ACME message type. :type expected: str :param delay: Number of seconds to delay before next round in case of ACME "defer" response message. :type delay: int :param rounds: Number of resend attempts in case of ACME "defer" reponse message. :type rounds: int :raises: Exception :returns: ACME response message from server. :rtype: dict """ for _ in xrange(rounds): if response["type"] == expected: return response elif response["type"] == "error": logger.error("%s: %s - More Info: %s" % (response["error"], response.get("message", ""), response.get("moreInfo", ""))) raise errors.LetsEncryptClientError(response["error"]) elif response["type"] == "defer": logger.info("Waiting for %d seconds..." % delay) time.sleep(delay) response = self.send(acme.status_request(response["token"])) else: logger.fatal("Received unexpected message") logger.fatal("Expected: %s" % expected) logger.fatal("Received: " + response) sys.exit(33) logger.error("Server has deferred past the max of %d seconds" % (rounds * delay))
def is_expected_msg(self, response, expected, delay=3, rounds=20): """Is reponse expected ACME message? :param response: ACME response message from server. :type response: dict :param expected: Name of the expected response ACME message type. :type expected: str :param delay: Number of seconds to delay before next round in case of ACME "defer" response message. :type delay: int :param rounds: Number of resend attempts in case of ACME "defer" reponse message. :type rounds: int :raises: Exception :returns: ACME response message from server. :rtype: dict """ for _ in xrange(rounds): if response["type"] == expected: return response elif response["type"] == "error": logger.error("%s: %s - More Info: %s" % (response["error"], response.get( "message", ""), response.get("moreInfo", ""))) raise errors.LetsEncryptClientError(response["error"]) elif response["type"] == "defer": logger.info("Waiting for %d seconds..." % delay) time.sleep(delay) response = self.send(acme.status_request(response["token"])) else: logger.fatal("Received unexpected message") logger.fatal("Expected: %s" % expected) logger.fatal("Received: " + response) sys.exit(33) logger.error("Server has deferred past the max of %d seconds" % (rounds * delay))
def init_key_csr(self): """Initializes privkey and csr. Inits key and CSR using provided files or generating new files if necessary. Both will be saved in PEM format on the filesystem. The CSR is placed into DER format to allow the namedtuple to easily work with the protocol. """ if not self.privkey.file: key_pem = crypto_util.make_key(CONFIG.RSA_KEY_SIZE) # Save file le_util.make_or_verify_dir(CONFIG.KEY_DIR, 0o700) key_f, key_filename = le_util.unique_file( os.path.join(CONFIG.KEY_DIR, "key-letsencrypt.pem"), 0o600) key_f.write(key_pem) key_f.close() logger.info("Generating key: %s" % key_filename) self.privkey = Client.Key(key_filename, key_pem) if not self.csr.file: csr_pem, csr_der = crypto_util.make_csr( self.privkey.pem, self.names) # Save CSR le_util.make_or_verify_dir(CONFIG.CERT_DIR, 0o755) csr_f, csr_filename = le_util.unique_file( os.path.join(CONFIG.CERT_DIR, "csr-letsencrypt.pem"), 0o644) csr_f.write(csr_pem) csr_f.close() logger.info("Creating CSR: %s" % csr_filename) self.csr = Client.CSR(csr_filename, csr_der, "der") elif self.csr.type != "der": # The user is going to pass in a pem format file # That is why we must conver it to der since the # protocol uses der exclusively. csr_obj = M2Crypto.X509.load_request_string(self.csr.data) self.csr = Client.CSR(self.csr.file, csr_obj.as_der(), "der")
def list_certs_keys(self): """List trusted Let's Encrypt certificates.""" list_file = os.path.join(CONFIG.CERT_KEY_BACKUP, "LIST") certs = [] if not os.path.isfile(list_file): logger.info( "You don't have any certificates saved from letsencrypt") return c_sha1_vh = {} for (cert, _, path) in self.config.get_all_certs_keys(): try: c_sha1_vh[M2Crypto.X509.load_cert( cert).get_fingerprint(md='sha1')] = path except: continue with open(list_file, 'rb') as csvfile: csvreader = csv.reader(csvfile) for row in csvreader: cert = crypto_util.get_cert_info(row[1]) b_k = os.path.join(CONFIG.CERT_KEY_BACKUP, os.path.basename(row[2]) + "_" + row[0]) b_c = os.path.join(CONFIG.CERT_KEY_BACKUP, os.path.basename(row[1]) + "_" + row[0]) cert.update({ "orig_key_file": row[2], "orig_cert_file": row[1], "idx": int(row[0]), "backup_key_file": b_k, "backup_cert_file": b_c, "installed": c_sha1_vh.get(cert["fingerprint"], ""), }) certs.append(cert) if certs: self.choose_certs(certs) else: display.generic_notification( "There are not any trusted Let's Encrypt " "certificates for this server.")
def install_certificate(self, certificate_dict, vhost): cert_chain_abspath = None cert_fd, self.cert_file = le_util.unique_file(CERT_PATH, 644) cert_fd.write( crypto_util.b64_cert_to_pem(certificate_dict["certificate"])) cert_fd.close() logger.info("Server issued certificate; certificate written to %s" % self.cert_file) if certificate_dict.get("chain", None): chain_fd, chain_fn = le_util.unique_file(CHAIN_PATH, 644) for c in certificate_dict.get("chain", []): chain_fd.write(crypto_util.b64_cert_to_pem(c)) chain_fd.close() logger.info("Cert chain written to %s" % chain_fn) # This expects a valid chain file cert_chain_abspath = os.path.abspath(chain_fn) for host in vhost: self.config.deploy_cert(host, os.path.abspath(self.cert_file), os.path.abspath(self.key_file), cert_chain_abspath) # Enable any vhost that was issued to, but not enabled if not host.enabled: logger.info("Enabling Site " + host.file) self.config.enable_site(host) # sites may have been enabled / final cleanup self.config.restart(quiet=self.curses) display.success_installation(self.names)
def verify_identity(self, challenge_msg): """Verify identity. :param challenge_msg: ACME "challenge" message. :type challenge_msg: dict :returns: TODO :rtype: dict """ path = challenge.gen_challenge_path( challenge_msg["challenges"], challenge_msg.get("combinations", [])) logger.info("Performing the following challenges:") # Every indices element is a list of integers referring to which # challenges in the master list the challenge object satisfies # Single Challenge objects that can satisfy multiple server challenges # mess up the order of the challenges, thus requiring the indices challenge_objs, indices = self.challenge_factory( self.names[0], challenge_msg["challenges"], path) responses = ["null"] * len(challenge_msg["challenges"]) # Perform challenges for i, c_obj in enumerate(challenge_objs): response = "null" if c_obj["type"] in CONFIG.CONFIG_CHALLENGES: response = self.config.perform(c_obj) else: # Handle RecoveryToken type challenges pass for index in indices[i]: responses[index] = response logger.info("Configured Apache for challenges; " + "waiting for verification...") return responses, challenge_objs
def is_expected_msg(self, msg_dict, expected, delay=3, rounds=20): for i in range(rounds): if msg_dict["type"] == expected: return msg_dict elif msg_dict["type"] == "error": logger.error("%s: %s - More Info: %s" % (msg_dict["error"], msg_dict.get( "message", ""), msg_dict.get("moreInfo", ""))) raise Exception(msg_dict["error"]) elif msg_dict["type"] == "defer": logger.info("Waiting for %d seconds..." % delay) time.sleep(delay) msg_dict = self.send(self.status_request(msg_dict["token"])) else: logger.fatal("Received unexpected message") logger.fatal("Expected: %s" % expected) logger.fatal("Received: " + msg_dict) sys.exit(33) logger.error("Server has deferred past the max of %d seconds" % (rounds * delay)) return None
def list_certs_keys(self): list_file = CERT_KEY_BACKUP + "LIST" certs = [] if not os.path.isfile(CERT_KEY_BACKUP + "LIST"): logger.info("You don't have any certificates saved from letsencrypt") return c_sha1_vh = {} for x in self.config.get_all_certs_keys(): try: c_sha1_vh[M2Crypto.X509.load_cert(x[0]).get_fingerprint(md='sha1')] = x[2] except: continue with open(list_file, 'rb') as csvfile: csvreader = csv.reader(csvfile) for row in csvreader: c = crypto_util.get_cert_info(row[1]) b_k = CERT_KEY_BACKUP + os.path.basename(row[2]) + "_" + row[0] b_c = CERT_KEY_BACKUP + os.path.basename(row[1]) + "_" + row[0] c["orig_key_file"] = row[2] c["orig_cert_file"] = row[1] c["idx"] = int(row[0]) c["backup_key_file"] = b_k c["backup_cert_file"] = b_c c["installed"] = c_sha1_vh.get(c["fingerprint"], "") certs.append(c) if certs: self.choose_certs(certs) else: display.generic_notification("There are not any trusted \ Let's Encrypt certificates for this server.")
def cleanup_challenges(self, challenge_objs): logger.info("Cleaning up challenges...") for c in challenge_objs: c.cleanup()
def authenticate(self, domains=[], redirect=None, eula=False): # Check configuration if not self.config.configtest(): sys.exit(1) self.redirect = redirect # Display preview warning if not eula: with open('EULA') as f: if not display.generic_yesno(f.read()): sys.exit(0) # Display screen to select domains to validate if domains: self.sanity_check_names([self.server] + domains) self.names = domains else: # This function adds all names # found within the config to self.names # Then filters them based on user selection code, self.names = display.filter_names(self.get_all_names()) if code == display.OK: # TODO: Allow multiple names once it is setup self.names = [self.names[0]] else: sys.exit(0) # Display choice of CA screen # TODO: Use correct server depending on CA #choice = self.choice_of_ca() # Check first if mod_ssl is loaded if not self.config.check_ssl_loaded(): logger.info("Loading mod_ssl into Apache Server") self.config.enable_mod("ssl") #Request Challenges challenge_dict = self.handle_challenge() # Get key and csr to perform challenges key_pem, csr_der = self.get_key_csr_pem() #Perform Challenges responses, challenge_objs = self.verify_identity(challenge_dict) # Get Authorization self.handle_authorization(challenge_dict, challenge_objs, responses) # Retrieve certificate certificate_dict = self.handle_certificate(csr_der) # Find set of virtual hosts to deploy certificates to vhost = self.get_virtual_hosts(self.names) # Install Certificate self.install_certificate(certificate_dict, vhost) # Perform optimal config changes self.optimize_config(vhost) self.config.save("Completed Augeas Authentication") self.store_cert_key(False) return
def certificate_request(self, csr_der, key): logger.info("Preparing and sending CSR..") return {"type":"certificateRequest", "csr":jose.b64encode_url(csr_der), "signature":crypto_util.create_sig(csr_der, self.key_file)}
def authenticate(self, domains = [], redirect = None, eula = False): # Check configuration if not self.config.configtest(): sys.exit(1) self.redirect = redirect # Display preview warning if not eula: with open('EULA') as f: if not display.generic_yesno(f.read()): sys.exit(0) # Display screen to select domains to validate if domains: self.sanity_check_names([self.server] + domains) self.names = domains else: # This function adds all names # found within the config to self.names # Then filters them based on user selection code, self.names = display.filter_names(self.get_all_names()) if code == display.OK and self.names: # TODO: Allow multiple names once it is setup self.names = [self.names[0]] else: sys.exit(0) # Display choice of CA screen # TODO: Use correct server depending on CA #choice = self.choice_of_ca() # Check first if mod_ssl is loaded if not self.config.check_ssl_loaded(): logger.info("Loading mod_ssl into Apache Server") self.config.enable_mod("ssl") #Request Challenges challenge_dict = self.handle_challenge() # Get key and csr to perform challenges key_pem, csr_der = self.get_key_csr_pem() #Perform Challenges responses, challenge_objs = self.verify_identity(challenge_dict) # Get Authorization self.handle_authorization(challenge_dict, challenge_objs, responses) # Retrieve certificate certificate_dict = self.handle_certificate(csr_der) # Find set of virtual hosts to deploy certificates to vhost = self.get_virtual_hosts(self.names) # Install Certificate self.install_certificate(certificate_dict, vhost) # Perform optimal config changes self.optimize_config(vhost) self.config.save("Completed Augeas Authentication") self.store_cert_key(False) return