diff --git a/lemur/certificates/cli.py b/lemur/certificates/cli.py index b883dee0..f23948be 100644 --- a/lemur/certificates/cli.py +++ b/lemur/certificates/cli.py @@ -735,3 +735,45 @@ def automatically_enable_autorotate(): }) cert.rotation = True database.update(cert) + + +@manager.command +def deactivate_entrust_certificates(): + """ + Attempt to deactivate test certificates issued by Entrust + """ + + log_data = { + "function": f"{__name__}.{sys._getframe().f_code.co_name}", + "message": "Deactivating Entrust certificates" + } + + certificates = get_all_valid_certs(['entrust-issuer']) + entrust_plugin = plugins.get('entrust-issuer') + for cert in certificates: + try: + response = entrust_plugin.deactivate_certificate(cert) + if response == 200: + cert.status = "revoked" + else: + cert.status = "unknown" + + log_data["valid"] = cert.status + log_data["certificate_name"] = cert.name + log_data["certificate_id"] = cert.id + metrics.send( + "certificate_deactivate", + "counter", + 1, + metric_tags={"status": log_data["valid"], + "certificate_name": log_data["certificate_name"], + "certificate_id": log_data["certificate_id"]}, + ) + current_app.logger.info(log_data) + + database.update(cert) + + except Exception as e: + current_app.logger.info(log_data) + sentry.captureException() + current_app.logger.exception(e) diff --git a/lemur/certificates/service.py b/lemur/certificates/service.py index 6d1bd2ac..1716ccb2 100644 --- a/lemur/certificates/service.py +++ b/lemur/certificates/service.py @@ -105,7 +105,7 @@ def get_all_certs(): def get_all_valid_certs(authority_plugin_name): """ - Retrieves all valid (not expired) certificates within Lemur, for the given authority plugin names + Retrieves all valid (not expired & not revoked) certificates within Lemur, for the given authority plugin names ignored if no authority_plugin_name provided. Note that depending on the DB size retrieving all certificates might an expensive operation @@ -116,11 +116,12 @@ def get_all_valid_certs(authority_plugin_name): return ( Certificate.query.outerjoin(Authority, Authority.id == Certificate.authority_id).filter( Certificate.not_after > arrow.now().format("YYYY-MM-DD")).filter( - Authority.plugin_name.in_(authority_plugin_name)).all() + Authority.plugin_name.in_(authority_plugin_name)).filter(Certificate.revoked.is_(False)).all() ) else: return ( - Certificate.query.filter(Certificate.not_after > arrow.now().format("YYYY-MM-DD")).all() + Certificate.query.filter(Certificate.not_after > arrow.now().format("YYYY-MM-DD")).filter( + Certificate.revoked.is_(False)).all() ) @@ -359,7 +360,12 @@ def create(**kwargs): try: cert_body, private_key, cert_chain, external_id, csr = mint(**kwargs) except Exception: - current_app.logger.error("Exception minting certificate", exc_info=True) + log_data = { + "message": "Exception minting certificate", + "issuer": kwargs["authority"].name, + "cn": kwargs["common_name"], + } + current_app.logger.error(log_data, exc_info=True) sentry.captureException() raise kwargs["body"] = cert_body diff --git a/lemur/common/celery.py b/lemur/common/celery.py index a490b13b..f9d58bd9 100644 --- a/lemur/common/celery.py +++ b/lemur/common/celery.py @@ -759,7 +759,7 @@ def check_revoked(): log_data = { "function": function, - "message": "check if any certificates are revoked revoked", + "message": "check if any valid certificate is revoked", "task_id": task_id, } @@ -842,3 +842,39 @@ def enable_autorotate_for_certs_attached_to_endpoint(): cli_certificate.automatically_enable_autorotate() metrics.send(f"{function}.success", "counter", 1) return log_data + + +@celery.task(soft_time_limit=3600) +def deactivate_entrust_test_certificates(): + """ + This celery task attempts to deactivate all not yet deactivated Entrust certificates, and should only run in TEST + :return: + """ + function = f"{__name__}.{sys._getframe().f_code.co_name}" + task_id = None + if celery.current_task: + task_id = celery.current_task.request.id + + log_data = { + "function": function, + "message": "deactivate entrust certificates", + "task_id": task_id, + } + + if task_id and is_task_active(function, task_id, None): + log_data["message"] = "Skipping task: Task is already active" + current_app.logger.debug(log_data) + return + + current_app.logger.debug(log_data) + try: + cli_certificate.deactivate_entrust_certificates() + except SoftTimeLimitExceeded: + log_data["message"] = "Time limit exceeded." + current_app.logger.error(log_data) + sentry.captureException() + metrics.send("celery.timeout", "counter", 1, metric_tags={"function": function}) + return + + metrics.send(f"{function}.success", "counter", 1) + return log_data diff --git a/lemur/plugins/lemur_digicert/plugin.py b/lemur/plugins/lemur_digicert/plugin.py index f28279a6..ec3a0792 100644 --- a/lemur/plugins/lemur_digicert/plugin.py +++ b/lemur/plugins/lemur_digicert/plugin.py @@ -37,7 +37,13 @@ def log_status_code(r, *args, **kwargs): :param kwargs: :return: """ + log_data = { + "reason": (r.reason if r.reason else ""), + "status_code": r.status_code, + "url": (r.url if r.url else ""), + } metrics.send("digicert_status_code_{}".format(r.status_code), "counter", 1) + current_app.logger.info(log_data) def signature_hash(signing_algorithm): @@ -171,7 +177,7 @@ def map_cis_fields(options, csr): "csr": csr, "signature_hash": signature_hash(options.get("signing_algorithm")), "validity": { - "valid_to": validity_end.format("YYYY-MM-DDTHH:MM") + "Z" + "valid_to": validity_end.format("YYYY-MM-DDTHH:mm:ss") + "Z" }, "organization": { "name": options["organization"], @@ -204,7 +210,7 @@ def handle_response(response): :return: """ if response.status_code > 399: - raise Exception(response.json()["errors"][0]["message"]) + raise Exception("DigiCert rejected request with the error:" + response.json()["errors"][0]["message"]) return response.json() @@ -215,10 +221,17 @@ def handle_cis_response(response): :param response: :return: """ - if response.status_code > 399: - raise Exception(response.text) + if response.status_code == 404: + raise Exception("DigiCert: order not in issued state") + elif response.status_code == 406: + raise Exception("DigiCert: wrong header request format") + elif response.status_code > 399: + raise Exception("DigiCert rejected request with the error:" + response.text) - return response.json() + if response.url.endswith("download"): + return response.content + else: + return response.json() @retry(stop_max_attempt_number=10, wait_fixed=10000) @@ -238,11 +251,9 @@ def get_cis_certificate(session, base_url, order_id): certificate_url = "{0}/platform/cis/certificate/{1}/download".format(base_url, order_id) session.headers.update({"Accept": "application/x-pkcs7-certificates"}) response = session.get(certificate_url) + response_content = handle_cis_response(response) - if response.status_code == 404: - raise Exception("Order not in issued state.") - - cert_chain_pem = convert_pkcs7_bytes_to_pem(response.content) + cert_chain_pem = convert_pkcs7_bytes_to_pem(response_content) if len(cert_chain_pem) < 3: raise Exception("Missing the certificate chain") return cert_chain_pem diff --git a/lemur/plugins/lemur_digicert/tests/test_digicert.py b/lemur/plugins/lemur_digicert/tests/test_digicert.py index 34dcef71..fd07ea2b 100644 --- a/lemur/plugins/lemur_digicert/tests/test_digicert.py +++ b/lemur/plugins/lemur_digicert/tests/test_digicert.py @@ -123,7 +123,7 @@ def test_map_cis_fields_with_validity_years(mock_current_app, authority): "signature_hash": "sha256", "organization": {"name": "Example, Inc."}, "validity": { - "valid_to": arrow.get(2018, 11, 3).format("YYYY-MM-DDTHH:MM") + "Z" + "valid_to": arrow.get(2018, 11, 3).format("YYYY-MM-DDTHH:mm:ss") + "Z" }, "profile_name": None, } @@ -159,7 +159,7 @@ def test_map_cis_fields_with_validity_end_and_start(mock_current_app, app, autho "signature_hash": "sha256", "organization": {"name": "Example, Inc."}, "validity": { - "valid_to": arrow.get(2017, 5, 7).format("YYYY-MM-DDTHH:MM") + "Z" + "valid_to": arrow.get(2017, 5, 7).format("YYYY-MM-DDTHH:mm:ss") + "Z" }, "profile_name": None, } diff --git a/lemur/plugins/lemur_entrust/plugin.py b/lemur/plugins/lemur_entrust/plugin.py index 515e2400..ffb5765d 100644 --- a/lemur/plugins/lemur_entrust/plugin.py +++ b/lemur/plugins/lemur_entrust/plugin.py @@ -20,7 +20,13 @@ def log_status_code(r, *args, **kwargs): :param kwargs: :return: """ + log_data = { + "reason": (r.reason if r.reason else ""), + "status_code": r.status_code, + "url": (r.url if r.url else ""), + } metrics.send(f"entrust_status_code_{r.status_code}", "counter", 1) + current_app.logger.info(log_data) def determine_end_date(end_date): @@ -109,7 +115,12 @@ def handle_response(my_response): "response": d } current_app.logger.info(log_data) - return d + if d == {'response': 'No detailed message'}: + # status if no data + return s + else: + # return data from the response + return d class EntrustIssuerPlugin(IssuerPlugin):