From 11c20beac8a51e06b2a34359e57b815db5006355 Mon Sep 17 00:00:00 2001 From: Pat Downey Date: Wed, 17 Jul 2024 20:26:32 +0100 Subject: [PATCH] feat/remove global variables (#187) * refactor: remove reliance on global `(issuing|root)_crl_days` and `(issuing|root)_crl_seconds` variables * chore: align lambda requirement.txt's with rest of repository * refactor: remove reliance on global library references to PROJECT and ENVIRONMENT_NAME env variables * refactor: remove reliance on global internal_s3_bucket_name and external_s3_bucket_name variables * refactor: remove reliance on global max_cert_lifetime variable * refactor: remove reliance on global domain variable * refactor: remove reliance on global root_ca_info, issuing_ca_info and public_crl global variables * chore: remove redundant methods and imports * test: dump step function response to output on failure * test: output execution details not just status --- .../create_issuing_ca/create_issuing_ca.py | 45 +++++-- .../create_issuing_ca/requirements.txt | 4 +- .../create_root_ca/create_root_ca.py | 18 ++- .../create_root_ca/requirements.txt | 4 +- .../issuing_ca_crl/issuing_ca_crl.py | 49 +++++--- .../issuing_ca_crl/requirements.txt | 4 +- .../lambda_code/root_ca_crl/requirements.txt | 4 +- .../lambda_code/root_ca_crl/root_ca_crl.py | 36 ++++-- .../lambda_code/tls_cert/requirements.txt | 4 +- .../lambda_code/tls_cert/test_tls_cert.py | 54 +++++++++ .../lambda_code/tls_cert/tls_cert.py | 112 +++++++++--------- .../terraform-aws-ca-lambda/utils/certs/ca.py | 111 ++++++----------- .../terraform-aws-ca-lambda/utils/certs/db.py | 55 +++++---- .../utils/certs/kms_ca.py | 21 ---- .../terraform-aws-ca-lambda/utils/certs/s3.py | 54 ++++----- scripts/start_ca_step_function.py | 18 +-- 16 files changed, 328 insertions(+), 265 deletions(-) create mode 100644 modules/terraform-aws-ca-lambda/lambda_code/tls_cert/test_tls_cert.py delete mode 100644 modules/terraform-aws-ca-lambda/utils/certs/kms_ca.py diff --git a/modules/terraform-aws-ca-lambda/lambda_code/create_issuing_ca/create_issuing_ca.py b/modules/terraform-aws-ca-lambda/lambda_code/create_issuing_ca/create_issuing_ca.py index 3e4c29a..1055534 100644 --- a/modules/terraform-aws-ca-lambda/lambda_code/create_issuing_ca/create_issuing_ca.py +++ b/modules/terraform-aws-ca-lambda/lambda_code/create_issuing_ca/create_issuing_ca.py @@ -1,4 +1,6 @@ import base64 +import json +import os from utils.certs.kms import kms_get_kms_key_id, kms_describe_key from utils.certs.crypto import ( crypto_kms_ca_cert_signing_request, @@ -14,18 +16,31 @@ lifetime = 3650 -def lambda_handler(event, context): # pylint:disable=unused-argument - root_ca_name = ca_name("root") - ca_slug = ca_name("issuing") +def lambda_handler(event, context): # pylint:disable=unused-argument,too-many-locals + project = os.environ["PROJECT"] + env_name = os.environ["ENVIRONMENT_NAME"] + external_s3_bucket_name = os.environ["EXTERNAL_S3_BUCKET"] + internal_s3_bucket_name = os.environ["INTERNAL_S3_BUCKET"] + domain = os.environ.get("DOMAIN") + + public_crl = os.environ.get("PUBLIC_CRL") + enable_public_crl = False + if public_crl == "enabled": + enable_public_crl = True + + issuing_ca_info = json.loads(os.environ["ISSUING_CA_INFO"]) + + root_ca_name = ca_name(project, env_name, "root") + ca_slug = ca_name(project, env_name, "issuing") # check Root CA exists - if not db_list_certificates(root_ca_name): + if not db_list_certificates(project, env_name, root_ca_name): print(f"CA {root_ca_name} not found") return # check if Issuing CA already exists - if db_list_certificates(ca_slug): + if db_list_certificates(project, env_name, ca_slug): print(f"CA {ca_slug} already exists. To recreate, first delete item in DynamoDB") return @@ -45,14 +60,22 @@ def lambda_handler(event, context): # pylint:disable=unused-argument ) # get Root CA cert in PEM format - root_ca_cert_pem = base64.b64decode(db_list_certificates(root_ca_name)[0]["Certificate"]["B"]) + root_ca_cert_pem = base64.b64decode(db_list_certificates(project, env_name, root_ca_name)[0]["Certificate"]["B"]) # deserialize Root CA cert root_ca_cert = load_pem_x509_certificate(root_ca_cert_pem) # sign certificate pem_certificate = ca_kms_sign_ca_certificate_request( - csr, root_ca_cert, root_ca_kms_key_id, kms_describe_key(root_ca_kms_key_id)["SigningAlgorithms"][0] + project, + env_name, + domain, + csr, + root_ca_cert, + root_ca_kms_key_id, + enable_public_crl, + issuing_ca_info, + kms_describe_key(root_ca_kms_key_id)["SigningAlgorithms"][0], ) base64_certificate = base64.b64encode(pem_certificate) @@ -61,13 +84,15 @@ def lambda_handler(event, context): # pylint:disable=unused-argument info = crypto_cert_info(cert, ca_slug) # create entry in DynamoDB - db_ca_cert_issued(info, base64_certificate) + db_ca_cert_issued(project, env_name, info, base64_certificate) # create CA bundle cert_bundle_pem = crypto_create_ca_bundle([root_ca_cert_pem, pem_certificate]) # upload certificate and CA bundle to S3 - s3_upload(pem_certificate, f"{ca_slug}.crt") - s3_upload(cert_bundle_pem, f"{ca_bundle_name()}.pem") + s3_upload(external_s3_bucket_name, internal_s3_bucket_name, pem_certificate, f"{ca_slug}.crt") + s3_upload( + external_s3_bucket_name, internal_s3_bucket_name, cert_bundle_pem, f"{ca_bundle_name(project, env_name)}.pem" + ) return diff --git a/modules/terraform-aws-ca-lambda/lambda_code/create_issuing_ca/requirements.txt b/modules/terraform-aws-ca-lambda/lambda_code/create_issuing_ca/requirements.txt index f02f807..9a7480c 100644 --- a/modules/terraform-aws-ca-lambda/lambda_code/create_issuing_ca/requirements.txt +++ b/modules/terraform-aws-ca-lambda/lambda_code/create_issuing_ca/requirements.txt @@ -1,2 +1,2 @@ -cryptography == 42.0.4 -validators == 0.22.0 +cryptography == 42.0.8 +validators == 0.33.0 diff --git a/modules/terraform-aws-ca-lambda/lambda_code/create_root_ca/create_root_ca.py b/modules/terraform-aws-ca-lambda/lambda_code/create_root_ca/create_root_ca.py index c8cbebd..72548a1 100644 --- a/modules/terraform-aws-ca-lambda/lambda_code/create_root_ca/create_root_ca.py +++ b/modules/terraform-aws-ca-lambda/lambda_code/create_root_ca/create_root_ca.py @@ -1,4 +1,6 @@ import base64 +import json +import os from utils.certs.kms import kms_get_kms_key_id, kms_get_public_key, kms_describe_key from utils.certs.crypto import crypto_cert_info from utils.certs.ca import ca_name, ca_create_kms_root_ca @@ -12,10 +14,16 @@ def lambda_handler(event, context): # pylint:disable=unused-argument - ca_slug = ca_name("root") + project = os.environ["PROJECT"] + env_name = os.environ["ENVIRONMENT_NAME"] + external_s3_bucket_name = os.environ["EXTERNAL_S3_BUCKET"] + internal_s3_bucket_name = os.environ["INTERNAL_S3_BUCKET"] + root_ca_info = json.loads(os.environ["ROOT_CA_INFO"]) + + ca_slug = ca_name(project, env_name, "root") # check if CA already exists - if db_list_certificates(ca_slug): + if db_list_certificates(project, env_name, ca_slug): print(f"CA {ca_slug} already exists. To recreate, first delete item in DynamoDB") return @@ -28,7 +36,7 @@ def lambda_handler(event, context): # pylint:disable=unused-argument print(f"using {cipher} key pair in KMS for {ca_slug}") pem_certificate = ca_create_kms_root_ca( - public_key, kms_key_id, kms_describe_key(kms_key_id)["SigningAlgorithms"][0] + public_key, kms_key_id, root_ca_info, kms_describe_key(kms_key_id)["SigningAlgorithms"][0] ) base64_certificate = base64.b64encode(pem_certificate) @@ -37,9 +45,9 @@ def lambda_handler(event, context): # pylint:disable=unused-argument info = crypto_cert_info(cert, ca_slug) # create entry in DynamoDB - db_ca_cert_issued(info, base64_certificate) + db_ca_cert_issued(project, env_name, info, base64_certificate) # upload CRL to S3 - s3_upload(pem_certificate, f"{ca_slug}.crt") + s3_upload(external_s3_bucket_name, internal_s3_bucket_name, pem_certificate, f"{ca_slug}.crt") return diff --git a/modules/terraform-aws-ca-lambda/lambda_code/create_root_ca/requirements.txt b/modules/terraform-aws-ca-lambda/lambda_code/create_root_ca/requirements.txt index 2fa5d0e..19e53c3 100644 --- a/modules/terraform-aws-ca-lambda/lambda_code/create_root_ca/requirements.txt +++ b/modules/terraform-aws-ca-lambda/lambda_code/create_root_ca/requirements.txt @@ -1,2 +1,2 @@ -cryptography == 42.0.4 -validators == 0.22.0 \ No newline at end of file +cryptography == 42.0.8 +validators == 0.33.0 \ No newline at end of file diff --git a/modules/terraform-aws-ca-lambda/lambda_code/issuing_ca_crl/issuing_ca_crl.py b/modules/terraform-aws-ca-lambda/lambda_code/issuing_ca_crl/issuing_ca_crl.py index 6114830..50d1dbf 100644 --- a/modules/terraform-aws-ca-lambda/lambda_code/issuing_ca_crl/issuing_ca_crl.py +++ b/modules/terraform-aws-ca-lambda/lambda_code/issuing_ca_crl/issuing_ca_crl.py @@ -1,27 +1,28 @@ from cryptography.hazmat.primitives import serialization from utils.certs.kms import kms_get_kms_key_id, kms_get_public_key, kms_describe_key from utils.certs.crypto import crypto_ca_key_info, crypto_revoked_certificate -from utils.certs.ca import ca_name, ca_kms_publish_crl -from utils.certs.db import db_list_certificates, db_update_crl_number, db_revocation_date +from utils.certs.ca import ca_name, ca_kms_publish_crl, ca_get_ca_info +from utils.certs.db import ( + db_list_certificates, + db_update_crl_number, + db_revocation_date, +) from utils.certs.s3 import s3_download, s3_upload from cryptography.hazmat.primitives.serialization import load_der_public_key import datetime import json import os -issuing_crl_days = int(os.environ["ISSUING_CRL_DAYS"]) -issuing_crl_seconds = int(os.environ["ISSUING_CRL_SECONDS"]) - -def build_list_of_revoked_certs(): +def build_list_of_revoked_certs(project, env_name, external_s3_bucket_name, internal_s3_bucket_name): """Build list of revoked certificates for CRL""" # handle certificate revocation not enabled - if not s3_download("revoked.json"): + if not s3_download(external_s3_bucket_name, internal_s3_bucket_name, "revoked.json"): print("revoked.json not found") return [] # get list of certificates to be revoked - revocation_file = s3_download("revoked.json")["Body"] + revocation_file = s3_download(external_s3_bucket_name, internal_s3_bucket_name, "revoked.json")["Body"] revocation_details = json.load(revocation_file) @@ -29,19 +30,27 @@ def build_list_of_revoked_certs(): for revocation_detail in revocation_details: common_name = revocation_detail["common_name"] serial_number = revocation_detail["serial_number"] - revocation_date = db_revocation_date(common_name, serial_number) + revocation_date = db_revocation_date(project, env_name, common_name, serial_number) revoked_cert = crypto_revoked_certificate(serial_number, revocation_date) revoked_certs.append(revoked_cert) - print(f"CA {ca_name('issuing')} has {len(revoked_certs)} revoked certificates") + print(f"CA {ca_name(project, env_name, 'issuing')} has {len(revoked_certs)} revoked certificates") return revoked_certs -def lambda_handler(event, context): # pylint:disable=unused-argument - ca_slug = ca_name("issuing") +def lambda_handler(event, context): # pylint:disable=unused-argument,too-many-locals + project = os.environ["PROJECT"] + env_name = os.environ["ENVIRONMENT_NAME"] + external_s3_bucket_name = os.environ["EXTERNAL_S3_BUCKET"] + internal_s3_bucket_name = os.environ["INTERNAL_S3_BUCKET"] + + issuing_ca_info = json.loads(os.environ["ISSUING_CA_INFO"]) + root_ca_info = json.loads(os.environ["ROOT_CA_INFO"]) + + ca_slug = ca_name(project, env_name, "issuing") # check CA exists - if not db_list_certificates(ca_slug): + if not db_list_certificates(project, env_name, ca_slug): print(f"CA {ca_slug} not found") return @@ -50,18 +59,26 @@ def lambda_handler(event, context): # pylint:disable=unused-argument kms_key_id = kms_get_kms_key_id(ca_slug) public_key = load_der_public_key(kms_get_public_key(kms_key_id)) + issuing_crl_days = int(os.environ["ISSUING_CRL_DAYS"]) + issuing_crl_seconds = int(os.environ["ISSUING_CRL_SECONDS"]) + # issue CRL valid for one day 10 minutes timedelta = datetime.timedelta(issuing_crl_days, issuing_crl_seconds, 0) ca_key_info = crypto_ca_key_info(public_key, kms_key_id, ca_slug) + ca_info = ca_get_ca_info(issuing_ca_info, root_ca_info) + crl = ca_kms_publish_crl( + ca_info, ca_key_info, timedelta, - build_list_of_revoked_certs(), - db_update_crl_number(ca_slug, db_list_certificates(ca_slug)[0]["SerialNumber"]["S"]), + build_list_of_revoked_certs(project, env_name, external_s3_bucket_name, internal_s3_bucket_name), + db_update_crl_number( + project, env_name, ca_slug, db_list_certificates(project, env_name, ca_slug)[0]["SerialNumber"]["S"] + ), kms_describe_key(kms_key_id)["SigningAlgorithms"][0], ).public_bytes(encoding=serialization.Encoding.DER) # upload CRL to S3 - s3_upload(crl, f"{ca_slug}.crl") + s3_upload(external_s3_bucket_name, internal_s3_bucket_name, crl, f"{ca_slug}.crl") return diff --git a/modules/terraform-aws-ca-lambda/lambda_code/issuing_ca_crl/requirements.txt b/modules/terraform-aws-ca-lambda/lambda_code/issuing_ca_crl/requirements.txt index 2fa5d0e..19e53c3 100644 --- a/modules/terraform-aws-ca-lambda/lambda_code/issuing_ca_crl/requirements.txt +++ b/modules/terraform-aws-ca-lambda/lambda_code/issuing_ca_crl/requirements.txt @@ -1,2 +1,2 @@ -cryptography == 42.0.4 -validators == 0.22.0 \ No newline at end of file +cryptography == 42.0.8 +validators == 0.33.0 \ No newline at end of file diff --git a/modules/terraform-aws-ca-lambda/lambda_code/root_ca_crl/requirements.txt b/modules/terraform-aws-ca-lambda/lambda_code/root_ca_crl/requirements.txt index f02f807..9a7480c 100644 --- a/modules/terraform-aws-ca-lambda/lambda_code/root_ca_crl/requirements.txt +++ b/modules/terraform-aws-ca-lambda/lambda_code/root_ca_crl/requirements.txt @@ -1,2 +1,2 @@ -cryptography == 42.0.4 -validators == 0.22.0 +cryptography == 42.0.8 +validators == 0.33.0 diff --git a/modules/terraform-aws-ca-lambda/lambda_code/root_ca_crl/root_ca_crl.py b/modules/terraform-aws-ca-lambda/lambda_code/root_ca_crl/root_ca_crl.py index a9abea3..a1a1b0f 100644 --- a/modules/terraform-aws-ca-lambda/lambda_code/root_ca_crl/root_ca_crl.py +++ b/modules/terraform-aws-ca-lambda/lambda_code/root_ca_crl/root_ca_crl.py @@ -9,19 +9,16 @@ import json import os -root_crl_days = int(os.environ["ROOT_CRL_DAYS"]) -root_crl_seconds = int(os.environ["ROOT_CRL_SECONDS"]) - -def build_list_of_revoked_certs(): +def build_list_of_revoked_certs(project, env_name, external_s3_bucket_name, internal_s3_bucket_name): """Build list of revoked certificates for CRL""" # handle certificate revocation not enabled - if not s3_download("revoked-root-ca.json"): + if not s3_download(external_s3_bucket_name, internal_s3_bucket_name, "revoked-root-ca.json"): print("revoked-root-ca.json not found") return [] # get list of certificates to be revoked - revocation_file = s3_download("revoked-root-ca.json")["Body"] + revocation_file = s3_download(external_s3_bucket_name, internal_s3_bucket_name, "revoked-root-ca.json")["Body"] revocation_details = json.load(revocation_file) @@ -29,19 +26,25 @@ def build_list_of_revoked_certs(): for revocation_detail in revocation_details: common_name = revocation_detail["common_name"] serial_number = revocation_detail["serial_number"] - revocation_date = db_revocation_date(common_name, serial_number) + revocation_date = db_revocation_date(project, env_name, common_name, serial_number) revoked_cert = crypto_revoked_certificate(serial_number, revocation_date) revoked_certs.append(revoked_cert) - print(f"CA {ca_name('root')} has {len(revoked_certs)} revoked certificates") + print(f"CA {ca_name(project, env_name, 'root')} has {len(revoked_certs)} revoked certificates") return revoked_certs def lambda_handler(event, context): # pylint:disable=unused-argument - ca_slug = ca_name("root") + project = os.environ["PROJECT"] + env_name = os.environ["ENVIRONMENT_NAME"] + external_s3_bucket_name = os.environ["EXTERNAL_S3_BUCKET"] + internal_s3_bucket_name = os.environ["INTERNAL_S3_BUCKET"] + root_ca_info = json.loads(os.environ["ROOT_CA_INFO"]) + + ca_slug = ca_name(project, env_name, "root") # check CA exists - if not db_list_certificates(ca_slug): + if not db_list_certificates(project, env_name, ca_slug): print(f"CA {ca_slug} not found") return @@ -50,18 +53,25 @@ def lambda_handler(event, context): # pylint:disable=unused-argument kms_key_id = kms_get_kms_key_id(ca_slug) public_key = load_der_public_key(kms_get_public_key(kms_key_id)) + root_crl_days = int(os.environ["ROOT_CRL_DAYS"]) + root_crl_seconds = int(os.environ["ROOT_CRL_SECONDS"]) + # issue CRL valid for one day 10 minutes timedelta = datetime.timedelta(root_crl_days, root_crl_seconds, 0) ca_key_info = crypto_ca_key_info(public_key, kms_key_id, ca_slug) + crl = ca_kms_publish_crl( + root_ca_info, ca_key_info, timedelta, - build_list_of_revoked_certs(), - db_update_crl_number(ca_slug, db_list_certificates(ca_slug)[0]["SerialNumber"]["S"]), + build_list_of_revoked_certs(project, env_name, external_s3_bucket_name, internal_s3_bucket_name), + db_update_crl_number( + project, env_name, ca_slug, db_list_certificates(project, env_name, ca_slug)[0]["SerialNumber"]["S"] + ), kms_describe_key(kms_key_id)["SigningAlgorithms"][0], ).public_bytes(encoding=serialization.Encoding.DER) # upload CRL to S3 - s3_upload(crl, f"{ca_slug}.crl") + s3_upload(external_s3_bucket_name, internal_s3_bucket_name, crl, f"{ca_slug}.crl") return diff --git a/modules/terraform-aws-ca-lambda/lambda_code/tls_cert/requirements.txt b/modules/terraform-aws-ca-lambda/lambda_code/tls_cert/requirements.txt index 2fa5d0e..9a7480c 100644 --- a/modules/terraform-aws-ca-lambda/lambda_code/tls_cert/requirements.txt +++ b/modules/terraform-aws-ca-lambda/lambda_code/tls_cert/requirements.txt @@ -1,2 +1,2 @@ -cryptography == 42.0.4 -validators == 0.22.0 \ No newline at end of file +cryptography == 42.0.8 +validators == 0.33.0 diff --git a/modules/terraform-aws-ca-lambda/lambda_code/tls_cert/test_tls_cert.py b/modules/terraform-aws-ca-lambda/lambda_code/tls_cert/test_tls_cert.py new file mode 100644 index 0000000..e3a2077 --- /dev/null +++ b/modules/terraform-aws-ca-lambda/lambda_code/tls_cert/test_tls_cert.py @@ -0,0 +1,54 @@ +from .tls_cert import create_csr_info, create_csr_subject + + +def test_create_csr_info(): + event = { + "common_name": "blah.example.com", + } + + csr_info = create_csr_info(event) + assert csr_info.purposes == ["client_auth"] + assert csr_info.sans == ["blah.example.com"] + + +def test_create_csr_info_with_purpose_and_sans(): + event = { + "common_name": "blah.example.com", + "purposes": ["server_auth"], + "sans": ["a.b.d.com"], + } + + csr_info = create_csr_info(event) + assert csr_info.purposes == ["server_auth"] + assert csr_info.sans == ["a.b.d.com"] + + +def test_create_csr_info_with_purpose_no_sans(): + event = { + "common_name": "blah.example.com", + "purposes": ["server_auth"], + } + + csr_info = create_csr_info(event) + assert csr_info.purposes == ["server_auth"] + assert csr_info.sans == ["blah.example.com"] + assert csr_info.subject.common_name == "blah.example.com" + + +def test_create_csr_subject(): + event = { + "common_name": "blah.example.com", + "locality": "London", # string, location + "organization": "Acme Inc", # string, organization name + "organizational_unit": "Animation", # string, organizational unit name + "state": "England", + "email_address": "test@example.com", + "country": "GB", # string, country code + } + + subject = create_csr_subject(event) + + expected = ( + "ST=England,OU=Animation,O=Acme Inc,L=London,1.2.840.113549.1.9.1=test@example.com,C=GB,CN=blah.example.com" + ) + assert subject.x509_name().rfc4514_string() == expected diff --git a/modules/terraform-aws-ca-lambda/lambda_code/tls_cert/tls_cert.py b/modules/terraform-aws-ca-lambda/lambda_code/tls_cert/tls_cert.py index a4ec204..75f8dc3 100644 --- a/modules/terraform-aws-ca-lambda/lambda_code/tls_cert/tls_cert.py +++ b/modules/terraform-aws-ca-lambda/lambda_code/tls_cert/tls_cert.py @@ -1,12 +1,10 @@ import base64 import os -from utils.certs.kms_ca import kms_ca_generate_key_pair + from utils.certs.kms import kms_get_kms_key_id, kms_describe_key from utils.certs.crypto import ( crypto_cert_request_info, - crypto_encode_private_key, crypto_cert_info, - crypto_random_string, ) from utils.certs.types import ( Subject, @@ -15,7 +13,6 @@ from utils.certs.ca import ( ca_name, ca_kms_sign_tls_certificate_request, - ca_client_tls_cert_signing_request, ) from utils.certs.db import ( db_tls_cert_issued, @@ -24,16 +21,13 @@ ) from utils.certs.s3 import s3_download from cryptography.x509 import load_pem_x509_certificate, load_pem_x509_csr -from cryptography.hazmat.primitives.serialization import load_der_private_key from cryptography.hazmat.primitives import serialization -# support legacy capability - to be removed in future release -client_keys_in_db = os.environ.get("CLIENT_KEYS_IN_DB") - -def sign_tls_certificate(csr, ca_name, csr_info): +# pylint:disable=too-many-arguments +def sign_tls_certificate(project, env_name, csr, ca_name, csr_info, domain, max_cert_lifetime, enable_public_crl): # get CA cert from DynamoDB - ca_cert_bytes_b64 = db_list_certificates(ca_name)[0]["Certificate"]["B"] + ca_cert_bytes_b64 = db_list_certificates(project, env_name, ca_name)[0]["Certificate"]["B"] ca_cert_bytes = base64.b64decode(ca_cert_bytes_b64) ca_cert = load_pem_x509_certificate(ca_cert_bytes) @@ -45,9 +39,14 @@ def sign_tls_certificate(csr, ca_name, csr_info): # sign certificate return ca_kms_sign_tls_certificate_request( + project, + env_name, + domain, + max_cert_lifetime, cert_request_info, ca_cert, issuing_ca_kms_key_id, + enable_public_crl, kms_describe_key(issuing_ca_kms_key_id)["SigningAlgorithms"][0], ) @@ -64,28 +63,12 @@ def select_csr_crypto(ca_slug): return "RSA_2048", "RSASSA_PKCS1_V1_5_SHA_256" -def create_csr(csr_info, ca_slug, generate_passphrase): - """Creates a private key and CSR using KMS Key Pair Generation""" - key_pair_spec, csr_algorithm = select_csr_crypto(ca_slug) - response = kms_ca_generate_key_pair(key_pair_spec) - private_key = load_der_private_key(response["PrivateKeyPlaintext"], None) - csr = load_pem_x509_csr(ca_client_tls_cert_signing_request(private_key, csr_info, csr_algorithm)) - - passphrase = None - base64_passphrase = None - if generate_passphrase: - passphrase = crypto_random_string(30) - base64_passphrase = base64.b64encode(passphrase.encode("utf-8")) - - private_key_bytes = crypto_encode_private_key(private_key, passphrase) - base64_private_key = base64.b64encode(private_key_bytes) - - return (csr, base64_private_key, base64_passphrase) - - -def sign_csr(csr, ca_name, csr_info): +# pylint:disable=too-many-arguments +def sign_csr(project, env_name, csr, ca_name, csr_info, domain, max_cert_lifetime, enable_public_crl): # sign certificate - pem_certificate = sign_tls_certificate(csr, ca_name, csr_info) + pem_certificate = sign_tls_certificate( + project, env_name, csr, ca_name, csr_info, domain, max_cert_lifetime, enable_public_crl + ) # get details to upload to DynamoDB info = crypto_cert_info(load_pem_x509_certificate(pem_certificate), csr_info.subject.common_name) @@ -93,8 +76,9 @@ def sign_csr(csr, ca_name, csr_info): return base64.b64encode(pem_certificate), info -def is_invalid_certificate_request(ca_name, common_name, csr, lifetime, force_issue): - if not db_list_certificates(ca_name): +# pylint:disable=too-many-arguments +def is_invalid_certificate_request(project, env_name, ca_name, common_name, csr, lifetime, force_issue): + if not db_list_certificates(project, env_name, ca_name): return {"error": f"CA {ca_name} not found"} # get public key from CSR @@ -109,7 +93,7 @@ def is_invalid_certificate_request(ca_name, common_name, csr, lifetime, force_is ).decode("utf-8") # check for private key reuse - if not force_issue and not db_issue_certificate(common_name, request_public_key_pem): + if not force_issue and not db_issue_certificate(project, env_name, common_name, request_public_key_pem): return {"error": "Private key has already been used for a certificate"} # check lifetime is at least 1 day @@ -119,23 +103,27 @@ def is_invalid_certificate_request(ca_name, common_name, csr, lifetime, force_is return None -def create_cert_bundle_from_certificate(base64_certificate): +def create_cert_bundle_from_certificate(project, env_name, base64_certificate): """ Creates a certificate bundle in PEM format containing Client Issuing CA and Root CA Certificates """ - root_ca_name = ca_name("root") - issuing_ca_name = ca_name("issuing") + root_ca_name = ca_name(project, env_name, "root") + issuing_ca_name = ca_name(project, env_name, "issuing") cert_bundle = "" return cert_bundle.join( [ base64.b64decode(base64_certificate.decode("utf-8")).decode("utf-8"), - base64.b64decode(db_list_certificates(issuing_ca_name)[0]["Certificate"]["B"]).decode("utf-8"), - base64.b64decode(db_list_certificates(root_ca_name)[0]["Certificate"]["B"]).decode("utf-8"), + base64.b64decode(db_list_certificates(project, env_name, issuing_ca_name)[0]["Certificate"]["B"]).decode( + "utf-8" + ), + base64.b64decode(db_list_certificates(project, env_name, root_ca_name)[0]["Certificate"]["B"]).decode( + "utf-8" + ), ] ) -def create_csr_subject(event): +def create_csr_subject(event) -> Subject: subject = Subject(event["common_name"]) subject.locality = event.get("locality") # string, location subject.organization = event.get("organization") # string, organization name @@ -146,24 +134,33 @@ def create_csr_subject(event): return subject -def create_csr_info(event): - csr_info = CsrInfo(Subject(event["common_name"])) - - csr_info.subject = create_csr_subject(event) +def create_csr_info(event) -> CsrInfo: + lifetime = int(event.get("lifetime", 30)) + purposes = event.get("purposes") + sans = event.get("sans") - lifetime = event.get("lifetime", 30) + subject = create_csr_subject(event) - csr_info.lifetime = int(lifetime) - csr_info.purposes = event.get("purposes") - csr_info.sans = event.get("sans") + csr_info = CsrInfo(subject, lifetime=lifetime, purposes=purposes, sans=sans) return csr_info -def lambda_handler(event, context): # pylint:disable=unused-argument +def lambda_handler(event, context): # pylint:disable=unused-argument,too-many-locals + project = os.environ["PROJECT"] + env_name = os.environ["ENVIRONMENT_NAME"] + external_s3_bucket_name = os.environ["EXTERNAL_S3_BUCKET"] + internal_s3_bucket_name = os.environ["INTERNAL_S3_BUCKET"] + max_cert_lifetime = int(os.environ["MAX_CERT_LIFETIME"]) + domain = os.environ.get("DOMAIN") + + public_crl = os.environ.get("PUBLIC_CRL") + enable_public_crl = False + if public_crl == "enabled": + enable_public_crl = True # get Issuing CA name - issuing_ca_name = ca_name("issuing") + issuing_ca_name = ca_name(project, env_name, "issuing") # process input print(f"Input: {event}") @@ -176,11 +173,16 @@ def lambda_handler(event, context): # pylint:disable=unused-argument base64_csr_data = event.get("base64_csr_data") # base64 encoded CSR PEM file if csr_file: - csr = load_pem_x509_csr(s3_download(f"csrs/{csr_file}")["Body"].read()) + csr_file_contents = s3_download(external_s3_bucket_name, internal_s3_bucket_name, f"csrs/{csr_file}")[ + "Body" + ].read() + csr = load_pem_x509_csr(csr_file_contents) else: csr = load_pem_x509_csr(base64.standard_b64decode(base64_csr_data)) validation_error = is_invalid_certificate_request( + project, + env_name, issuing_ca_name, csr_info.subject.common_name, csr, @@ -190,12 +192,14 @@ def lambda_handler(event, context): # pylint:disable=unused-argument if validation_error: return validation_error - base64_certificate, cert_info = sign_csr(csr, issuing_ca_name, csr_info) + base64_certificate, cert_info = sign_csr( + project, env_name, csr, issuing_ca_name, csr_info, domain, max_cert_lifetime, enable_public_crl + ) - db_tls_cert_issued(cert_info, base64_certificate) + db_tls_cert_issued(project, env_name, cert_info, base64_certificate) if create_cert_bundle: - cert_bundle = create_cert_bundle_from_certificate(base64_certificate) + cert_bundle = create_cert_bundle_from_certificate(project, env_name, base64_certificate) base64_certificate = base64.b64encode(cert_bundle.encode("utf-8")) response_data = { diff --git a/modules/terraform-aws-ca-lambda/utils/certs/ca.py b/modules/terraform-aws-ca-lambda/utils/certs/ca.py index 428ca6f..195a69f 100644 --- a/modules/terraform-aws-ca-lambda/utils/certs/ca.py +++ b/modules/terraform-aws-ca-lambda/utils/certs/ca.py @@ -1,6 +1,3 @@ -import os -import json - from datetime import datetime, timezone, timedelta from cryptography import x509 from cryptography.x509 import ( @@ -11,7 +8,6 @@ ) from cryptography.x509.oid import AuthorityInformationAccessOID, ExtendedKeyUsageOID from cryptography.hazmat.primitives import serialization -from validators import domain as domain_validator from .crypto import ( crypto_select_class, crypto_hash_algorithm, @@ -19,17 +15,8 @@ ) from .types import Subject -# TODO: How can we get rid of these globals? -domain = os.environ.get("DOMAIN") -env_name = os.environ["ENVIRONMENT_NAME"] -issuing_ca_info = json.loads(os.environ["ISSUING_CA_INFO"]) -max_cert_lifetime = int(os.environ["MAX_CERT_LIFETIME"]) -project = os.environ["PROJECT"] -public_crl = os.environ["PUBLIC_CRL"] -root_ca_info = json.loads(os.environ["ROOT_CA_INFO"]) - -def ca_name(hierarchy): +def ca_name(project, env_name, hierarchy): if env_name in ["prd", "prod"]: return f"{project}-{hierarchy.lower()}-ca" @@ -64,8 +51,17 @@ def tls_cert_construct_subject_name(csr_cert, cert_request_info): return subject.x509_name() +# pylint:disable=too-many-arguments def ca_kms_sign_ca_certificate_request( - csr_cert, ca_cert, kms_key_id, kms_signing_algorithm="RSASSA_PKCS1_V1_5_SHA_256" + project, + env_name, + domain, + csr_cert, + ca_cert, + kms_key_id, + enable_public_crl, + issuing_ca_info, + kms_signing_algorithm="RSASSA_PKCS1_V1_5_SHA_256", ): """Sign CA certificate signing request using private key in AWS KMS""" @@ -75,7 +71,7 @@ def ca_kms_sign_ca_certificate_request( subject = ca_construct_subject_name(issuing_ca_info, "issuing") crl_dp = x509.DistributionPoint( - [UniformResourceIdentifier(f"http://{domain}/{ca_name('root')}.crl")], + [UniformResourceIdentifier(f"http://{domain}/{ca_name(project, env_name, 'root')}.crl")], relative_name=None, reasons=None, crl_issuer=None, @@ -85,7 +81,7 @@ def ca_kms_sign_ca_certificate_request( [ AccessDescription( AuthorityInformationAccessOID.CA_ISSUERS, - UniformResourceIdentifier(f"http://{domain}/{ca_name('root')}.crt"), + UniformResourceIdentifier(f"http://{domain}/{ca_name(project, env_name, 'root')}.crt"), ) ] ) @@ -123,7 +119,7 @@ def ca_kms_sign_ca_certificate_request( ) ) - if public_crl == "enabled": + if enable_public_crl: cert = cert.add_extension(x509.CRLDistributionPoints([crl_dp]), critical=False) cert = cert.add_extension(aia, critical=False) @@ -183,8 +179,17 @@ def ca_build_cert(csr_cert, ca_cert, lifetime, delta, cert_request_info): ) +# pylint:disable=too-many-arguments,too-many-locals def ca_kms_sign_tls_certificate_request( - cert_request_info, ca_cert, kms_key_id, kms_signing_algorithm="RSASSA_PKCS1_V1_5_SHA_256" + project, + env_name, + domain, + max_cert_lifetime, + cert_request_info, + ca_cert, + kms_key_id, + enable_public_crl, + kms_signing_algorithm="RSASSA_PKCS1_V1_5_SHA_256", ): csr_cert = cert_request_info["CsrCert"] x509_dns_names = cert_request_info["x509Sans"] @@ -203,11 +208,11 @@ def ca_kms_sign_tls_certificate_request( critical=False, ) - if public_crl == "enabled": + if enable_public_crl: # construct CRL distribution point crl_dp = x509.DistributionPoint( - [UniformResourceIdentifier(f"http://{domain}/{ca_name('issuing')}.crl")], + [UniformResourceIdentifier(f"http://{domain}/{ca_name(project, env_name, 'issuing')}.crl")], relative_name=None, reasons=None, crl_issuer=None, @@ -218,7 +223,7 @@ def ca_kms_sign_tls_certificate_request( [ AccessDescription( AuthorityInformationAccessOID.CA_ISSUERS, - UniformResourceIdentifier(f"http://{domain}/{ca_name('issuing')}.crt"), + UniformResourceIdentifier(f"http://{domain}/{ca_name(project, env_name, 'issuing')}.crt"), ) ] ) @@ -235,14 +240,14 @@ def ca_kms_sign_tls_certificate_request( return cert.public_bytes(serialization.Encoding.PEM) -def ca_bundle_name(): +def ca_bundle_name(project, env_name): """Returns CA bundle name for uploading to S3""" if env_name in ["prd", "prod"]: return f"{project}-ca-bundle" return f"{project}-ca-bundle-{env_name}" -def ca_create_root_ca(public_key, private_key, kms_signing_algorithm="RSASSA_PKCS1_V1_5_SHA_256"): +def ca_create_root_ca(public_key, private_key, root_ca_info, kms_signing_algorithm="RSASSA_PKCS1_V1_5_SHA_256"): """Creates Root CA self-signed certificate with defined private key""" # get Root CA info @@ -289,11 +294,11 @@ def ca_create_root_ca(public_key, private_key, kms_signing_algorithm="RSASSA_PKC return cert.public_bytes(serialization.Encoding.PEM) -def ca_create_kms_root_ca(public_key, kms_key_id, kms_signing_algorithm="RSASSA_PKCS1_V1_5_SHA_256"): +def ca_create_kms_root_ca(public_key, kms_key_id, root_ca_info, kms_signing_algorithm="RSASSA_PKCS1_V1_5_SHA_256"): """Creates Root CA self-signed certificate with private key in KMS""" private_key = crypto_select_class(kms_signing_algorithm)(kms_key_id, crypto_hash_algorithm(kms_signing_algorithm)) - return ca_create_root_ca(public_key, private_key, kms_signing_algorithm) + return ca_create_root_ca(public_key, private_key, root_ca_info, kms_signing_algorithm) def ca_get_ca_info(issuing_ca_info, root_ca_info): @@ -320,15 +325,19 @@ def subject_from_ca_info(ca_info, default_common_name=None): return subject +# pylint:disable=too-many-arguments def ca_kms_publish_crl( - ca_key_info, time_delta, revoked_certs, crl_number, kms_signing_algorithm="RSASSA_PKCS1_V1_5_SHA_256" + ca_info, + ca_key_info, + time_delta, + revoked_certs, + crl_number, + kms_signing_algorithm="RSASSA_PKCS1_V1_5_SHA_256", ): """Publishes certificate revocation list signed by private key in KMS""" kms_key_id = ca_key_info["KmsKeyId"] public_key = ca_key_info["PublicKey"] - ca_info = ca_get_ca_info(issuing_ca_info, root_ca_info) - subject = subject_from_ca_info(ca_info, "Serverless Root CA") issuer = subject.x509_name() @@ -347,47 +356,3 @@ def ca_kms_publish_crl( crypto_select_class(kms_signing_algorithm)(kms_key_id, crypto_hash_algorithm(kms_signing_algorithm)), crypto_hash_class(kms_signing_algorithm), ) - - -def ca_client_tls_cert_signing_request(private_key, csr_info, kms_signing_algorithm="RSASSA_PKCS1_V1_5_SHA_256"): - - # get CSR info, using Issuing CA info if needed - common_name = csr_info.get("commonName") - subject = Subject(common_name) - subject.country = csr_info.get("country") or issuing_ca_info.get("country") - subject.state = csr_info.get("state") or issuing_ca_info.get("state") - subject.locality = csr_info.get("locality") or issuing_ca_info.get("locality") - subject.organization = csr_info.get("organization") or issuing_ca_info.get("organization") - subject.organizational_unit = csr_info.get("organizationalUnit") - - subject.email_address = csr_info.get("emailAddress") - - subject_x509_name = subject.x509_name() - - if domain_validator(common_name): - csr = ( - x509.CertificateSigningRequestBuilder() - .subject_name( - x509.Name(subject_x509_name), - ) - .add_extension( - x509.SubjectAlternativeName( - [ - x509.DNSName(common_name), - ] - ), - critical=False, - ) - .sign(private_key, crypto_hash_class(kms_signing_algorithm)) - ) - - else: - csr = ( - x509.CertificateSigningRequestBuilder() - .subject_name( - x509.Name(subject_x509_name), - ) - .sign(private_key, crypto_hash_class(kms_signing_algorithm)) - ) - - return csr.public_bytes(serialization.Encoding.PEM) diff --git a/modules/terraform-aws-ca-lambda/utils/certs/db.py b/modules/terraform-aws-ca-lambda/utils/certs/db.py index 5113e68..d89fa58 100644 --- a/modules/terraform-aws-ca-lambda/utils/certs/db.py +++ b/modules/terraform-aws-ca-lambda/utils/certs/db.py @@ -1,16 +1,13 @@ import boto3 -import os import base64 from datetime import datetime from cryptography.x509 import load_pem_x509_certificate from cryptography.hazmat.primitives import serialization -project = os.environ["PROJECT"] -env_name = os.environ["ENVIRONMENT_NAME"] base_table_name = "CA" -def db_get_table_name(): +def db_get_table_name(project, env_name): # constructs the DynamoDB table name, e.g. SecureEmailCADev capitalised_project = project.replace("-", " ").title().replace(" ", "") @@ -20,14 +17,16 @@ def db_get_table_name(): return table_name -def db_list_certificates(common_name): +def db_list_certificates(project, env_name, common_name): # returns list of certificates for a specified common_name client = boto3.client("dynamodb") - print(f"querying DynamoDB table {db_get_table_name()} for {common_name}") + table_name = db_get_table_name(project, env_name) + + print(f"querying DynamoDB table {table_name} for {common_name}") response = client.query( - TableName=db_get_table_name(), + TableName=table_name, KeyConditionExpression="CommonName = :CommonName", ExpressionAttributeValues={":CommonName": {"S": common_name}}, ) @@ -35,10 +34,10 @@ def db_list_certificates(common_name): return response["Items"] -def db_issue_certificate(common_name, request_public_key_pem): +def db_issue_certificate(project, env_name, common_name, request_public_key_pem): """Determines whether certificate should be issued""" - certificates = db_list_certificates(common_name) + certificates = db_list_certificates(project, env_name, common_name) # if there are no certificates with that name, issue a certificate if not certificates: @@ -68,7 +67,7 @@ def db_issue_certificate(common_name, request_public_key_pem): return True -def db_ca_cert_issued(cert_info, certificate, encrypted_private_key=None): +def db_ca_cert_issued(project, env_name, cert_info, certificate, encrypted_private_key=None): # creates a new item in DynamoDB when a CA certificate is issued common_name = cert_info["CommonName"] @@ -76,11 +75,13 @@ def db_ca_cert_issued(cert_info, certificate, encrypted_private_key=None): expires = cert_info["Expires"] serial_number = cert_info["SerialNumber"] + table_name = db_get_table_name(project, env_name) + client = boto3.client("dynamodb") - print(f"adding {common_name} certificate details to DynamoDB table {db_get_table_name()}") + print(f"adding {common_name} certificate details to DynamoDB table {table_name}") if encrypted_private_key is None: client.put_item( - TableName=db_get_table_name(), + TableName=table_name, Item={ "SerialNumber": {"S": serial_number}, "CommonName": {"S": common_name}, @@ -94,7 +95,7 @@ def db_ca_cert_issued(cert_info, certificate, encrypted_private_key=None): return client.put_item( - TableName=db_get_table_name(), + TableName=table_name, Item={ "SerialNumber": {"S": serial_number}, "CommonName": {"S": common_name}, @@ -107,7 +108,7 @@ def db_ca_cert_issued(cert_info, certificate, encrypted_private_key=None): ) -def db_tls_cert_issued(cert_info, certificate): +def db_tls_cert_issued(project, env_name, cert_info, certificate): """creates a new item in DynamoDB when a TLS certificate is issued""" # if a passphrase is used, private key must be encrypted @@ -116,11 +117,13 @@ def db_tls_cert_issued(cert_info, certificate): expires = cert_info["Expires"] serial_number = cert_info["SerialNumber"] + table_name = db_get_table_name(project, env_name) + client = boto3.client("dynamodb") - print(f"adding {common_name} certificate details to DynamoDB table {db_get_table_name()}") + print(f"adding {common_name} certificate details to DynamoDB table {table_name}") client.put_item( - TableName=db_get_table_name(), + TableName=table_name, Item={ "SerialNumber": {"S": serial_number}, "CommonName": {"S": common_name}, @@ -131,7 +134,7 @@ def db_tls_cert_issued(cert_info, certificate): ) -def db_update_crl_number(common_name, serial_number): +def db_update_crl_number(project, env_name, common_name, serial_number): """increments CRL number by 1 and returns new value as integer""" client = boto3.client("dynamodb") @@ -139,7 +142,7 @@ def db_update_crl_number(common_name, serial_number): print(f"updating {common_name} CRL number in DynamoDB") response = client.update_item( - TableName=db_get_table_name(), + TableName=db_get_table_name(project, env_name), ExpressionAttributeNames={"#N": "CRLNumber"}, UpdateExpression="SET #N = #N + :n", ExpressionAttributeValues={":n": {"N": "1"}}, @@ -150,14 +153,16 @@ def db_update_crl_number(common_name, serial_number): return int(response["Attributes"]["CRLNumber"]["N"]) -def db_get_certificate(common_name, serial_number): +def db_get_certificate(project, env_name, common_name, serial_number): """returns certificate with specified serial_number""" client = boto3.client("dynamodb") - print(f"querying DynamoDB table {db_get_table_name()} for {serial_number}") + table_name = db_get_table_name(project, env_name) + + print(f"querying DynamoDB table {table_name} for {serial_number}") response = client.query( - TableName=db_get_table_name(), + TableName=table_name, KeyConditionExpression="CommonName = :CommonName", ExpressionAttributeValues={":CommonName": {"S": common_name}}, ) @@ -166,11 +171,13 @@ def db_get_certificate(common_name, serial_number): return [i for i in items if i["SerialNumber"]["S"] == serial_number][0] -def db_revocation_date(common_name, serial_number): +def db_revocation_date(project, env_name, common_name, serial_number): """adds revocation date to existing item in DynamoDB, or returns date already revoked""" client = boto3.client("dynamodb") - certificate = db_get_certificate(common_name, serial_number) + table_name = db_get_table_name(project, env_name) + + certificate = db_get_certificate(project, env_name, common_name, serial_number) # if certificate is already revoked, return revocation date if certificate.get("Revoked"): @@ -184,7 +191,7 @@ def db_revocation_date(common_name, serial_number): # write today's date to DynamoDB to record revocation client.update_item( - TableName=db_get_table_name(), + TableName=table_name, Key={ "CommonName": {"S": common_name}, "SerialNumber": {"S": serial_number}, diff --git a/modules/terraform-aws-ca-lambda/utils/certs/kms_ca.py b/modules/terraform-aws-ca-lambda/utils/certs/kms_ca.py deleted file mode 100644 index d9ca508..0000000 --- a/modules/terraform-aws-ca-lambda/utils/certs/kms_ca.py +++ /dev/null @@ -1,21 +0,0 @@ -import os -import boto3 -from .kms import kms_get_kms_key_id - -project = os.environ["PROJECT"] -env_name = os.environ["ENVIRONMENT_NAME"] - - -def kms_ca_generate_key_pair(key_pair_spec="RSA_2048"): - client = boto3.client(service_name="kms") - - if env_name in ["prd", "prod"]: - tls_keygen_kms_arn = kms_get_kms_key_id(f"{project}-tls-keygen") - - else: - tls_keygen_kms_arn = kms_get_kms_key_id(f"{project}-tls-keygen-{env_name}") - - return client.generate_data_key_pair( - KeyId=tls_keygen_kms_arn, - KeyPairSpec=key_pair_spec, - ) diff --git a/modules/terraform-aws-ca-lambda/utils/certs/s3.py b/modules/terraform-aws-ca-lambda/utils/certs/s3.py index 132c268..4ec07b0 100644 --- a/modules/terraform-aws-ca-lambda/utils/certs/s3.py +++ b/modules/terraform-aws-ca-lambda/utils/certs/s3.py @@ -1,49 +1,41 @@ -import os import boto3 -project = os.environ["PROJECT"] -env_name = os.environ["ENVIRONMENT_NAME"] -external_s3_bucket_name = os.environ["EXTERNAL_S3_BUCKET"] -internal_s3_bucket_name = os.environ["INTERNAL_S3_BUCKET"] - -def s3_download(key, internal=True): +def s3_download_file(bucket_name, key): client = boto3.client("s3") - if internal: - print(f"downloading {key} from s3 bucket {internal_s3_bucket_name}") - - try: - return client.get_object( - Bucket=internal_s3_bucket_name, - Key=key, - ) - except client.exceptions.NoSuchKey: - print(f"file {key} not found in s3 bucket {internal_s3_bucket_name}") - return None - - print(f"downloading {key} from s3 bucket {external_s3_bucket_name}") + print(f"downloading {key} from s3 bucket {bucket_name}") try: return client.get_object( - Bucket=external_s3_bucket_name, + Bucket=bucket_name, Key=key, ) except client.exceptions.NoSuchKey: - print(f"file {key} not found in s3 bucket {external_s3_bucket_name}") - return None + print(f"file {key} not found in s3 bucket {bucket_name}") + + return None -def s3_upload(file, key, content_type="application/x-pkcs7-crl", external=True): +def s3_download(external_s3_bucket_name, internal_s3_bucket_name, key, internal=True): + if internal: + return s3_download_file(internal_s3_bucket_name, key) + + return s3_download_file(external_s3_bucket_name, key) + + +def s3_upload_file(file, bucket_name, key, content_type): client = boto3.client("s3") - if external: - client.put_object(Body=file, Bucket=external_s3_bucket_name, Key=key, ContentType=content_type) - print(f"uploaded {key} to s3 bucket {external_s3_bucket_name}") + client.put_object(Body=file, Bucket=bucket_name, Key=key, ContentType=content_type) + print(f"uploaded {key} to s3 bucket {bucket_name}") - return - client.put_object(Body=file, Bucket=internal_s3_bucket_name, Key=key, ContentType=content_type) - print(f"uploaded {key} to s3 bucket {internal_s3_bucket_name}") +# pylint:disable=too-many-arguments +def s3_upload( + external_s3_bucket_name, internal_s3_bucket_name, file, key, content_type="application/x-pkcs7-crl", external=True +): + if external: + return s3_upload_file(file, external_s3_bucket_name, key, content_type) - return + return s3_upload_file(file, internal_s3_bucket_name, key, content_type) diff --git a/scripts/start_ca_step_function.py b/scripts/start_ca_step_function.py index 7663ce2..f67bfeb 100644 --- a/scripts/start_ca_step_function.py +++ b/scripts/start_ca_step_function.py @@ -31,24 +31,26 @@ def monitor_step_function_execution(execution_arn): stepfunctions_client = client("stepfunctions") - execution_status = stepfunctions_client.describe_execution(executionArn=execution_arn)["status"] + execution_details = stepfunctions_client.describe_execution(executionArn=execution_arn) - while execution_status == "RUNNING": - execution_status = stepfunctions_client.describe_execution(executionArn=execution_arn)["status"] - print(f"CA Step Function status: {execution_status}") + while execution_details["status"] == "RUNNING": + execution_details = stepfunctions_client.describe_execution(executionArn=execution_arn) + print(f'"CA Step Function status: {execution_details["status"]}') sleep(5) - return execution_status + return execution_details if __name__ == "__main__": step_function_arn, step_function_name = get_ca_step_function_details() print(f"Starting {step_function_name}...") execution_arn = start_ca_step_function(step_function_arn)["executionArn"] - execution_status = monitor_step_function_execution(execution_arn) + execution_details = monitor_step_function_execution(execution_arn) - if execution_status == "SUCCEEDED": + exec_status = execution_details["status"] + if exec_status == "SUCCEEDED": print(f"Step Function {step_function_name} completed successfully") - if execution_status == "FAILED": + if exec_status == "FAILED": + print(f"Step Function Output: {execution_details}") raise SystemExit(f"Step Function {step_function_name} failed")