From ba06ab732bb7ee3874a09ce0c38b19f48585f017 Mon Sep 17 00:00:00 2001 From: bgorbuntsov <54511351+bgorbuntsov@users.noreply.github.com> Date: Thu, 19 Dec 2024 21:22:56 +0000 Subject: [PATCH] utils scripts enhancements (#288) * ignore of vim swap files added * Client certificate creation variables moved to external JSON template file * AWS session usage processing added * Original variable values reverted * AWS session processing added. File and directory names adapted * black code style check errors fixed * black code style check errors fixed. try2 * python venv and pyenv staff added * certififcate variables moved to dedicated directory * common generate cert script added * arg description fixed * function for aws session opening modified * code styling fixes * code styling fixes * server certificate generation file adapted to use session * default varfile name fixed * empty session creation fixed * empty session creation is server cert generation script fixed * Certificate chain export description fixed --------- Co-authored-by: Boris Gorbuntsov Co-authored-by: Paul Schwarzenberger --- .gitignore | 8 ++ utils/client-cert.py | 70 +++++++-- utils/generate-cert.py | 269 +++++++++++++++++++++++++++++++++++ utils/modules/aws/kms.py | 8 +- utils/modules/aws/lambdas.py | 15 +- utils/modules/aws/s3.py | 42 ++++-- utils/modules/certs/kms.py | 25 +++- utils/server-cert.py | 68 +++++++-- utils/variables/client.json | 13 ++ utils/variables/server.json | 14 ++ 10 files changed, 486 insertions(+), 46 deletions(-) create mode 100644 utils/generate-cert.py create mode 100644 utils/variables/client.json create mode 100644 utils/variables/server.json diff --git a/.gitignore b/.gitignore index a0b2bf1..df7dac7 100644 --- a/.gitignore +++ b/.gitignore @@ -9,6 +9,11 @@ modules/terraform-aws-ca-lambda/build/ # Python __pycache__ .venv +.python-version +**/pyvenv.cfg +**/bin +**/lib +**/lib64 # Terraform .terraform/ @@ -22,3 +27,6 @@ terraform.tfvars # MacOS .DS_Store + +# Vim +**.swp diff --git a/utils/client-cert.py b/utils/client-cert.py index d0f9720..aa49cc6 100644 --- a/utils/client-cert.py +++ b/utils/client-cert.py @@ -1,8 +1,10 @@ #!/usr/bin/env python3 +import os +import sys import json import base64 +import argparse import boto3 -import os from cryptography.hazmat.primitives.serialization import load_der_private_key from modules.certs.crypto import create_csr_info, crypto_encode_private_key, crypto_tls_cert_signing_request from modules.certs.kms import kms_generate_key_pair, kms_get_kms_key_id @@ -16,11 +18,50 @@ os.makedirs(base_path) -def main(): # pylint:disable=too-many-locals +def create_session(profile): + """ + Creates and returns AWS session using profile + """ + + try: + if profile is not None: + session = boto3.session.Session(profile_name=profile) + else: + session = boto3.session.Session() + except Exception as e: + session = {"error": e} + if isinstance(session, dict): + print(f"Error: Unable to open session using profile {profile}. {session['error']}") + sys.exit(1) + else: + print(f"AWS session opened using profile: {profile}") + return session + + +def parse_arguments(): + """ + Read arguments and return arguments dictonary + """ + + arguments = {} + parser = argparse.ArgumentParser() + parser.add_argument("--profile", default=None, help="AWS profile described in .aws/config file") + parser.add_argument("--verbose", action="store_true", help="Output of all generated payload data") + arguments = vars(parser.parse_args()) + + return arguments + + +def main(): # pylint:disable=too-many-locals,too-many-statements """ Create test client certificate for default Serverless CA environment """ + args = parse_arguments() + + # create AWS session + session = create_session(args["profile"]) + # set variables lifetime = 90 common_name = "My Test Certificate" @@ -31,14 +72,17 @@ def main(): # pylint:disable=too-many-locals organizational_unit = "Security Operations" purposes = ["client_auth"] output_path_cert_key = f"{base_path}/client-key.pem" - output_path_cert_pem = f"{base_path}/client-cert.pem" + output_path_cert_pem = f"{base_path}/ca-bundle.pem" output_path_cert_crt = f"{base_path}/client-cert.crt" - output_path_cert_combined = f"{base_path}/client-cert-key.pem" + output_path_cert_combined = f"{base_path}/ca-cert-key-bundle.pem" key_alias = "serverless-tls-keygen-dev" # create key pair using symmetric KMS key to provide entropy - key_id = kms_get_kms_key_id(key_alias) - kms_response = kms_generate_key_pair(key_id) + key_id = kms_get_kms_key_id(key_alias, session=session) + if isinstance(key_id, dict): + print(f'Error: {key_id["error"]}') + sys.exit(1) + kms_response = kms_generate_key_pair(key_id, session=session) private_key = load_der_private_key(kms_response["PrivateKeyPlaintext"], None) # create CSR @@ -58,8 +102,8 @@ def main(): # pylint:disable=too-many-locals request_payload_bytes = json.dumps(request_payload) # Invoke TLS certificate Lambda function - lambda_name = get_lambda_name("tls-cert") - client = boto3.client("lambda") + lambda_name = get_lambda_name("tls-cert", session=session) + client = session.client("lambda") response = client.invoke( FunctionName=lambda_name, InvocationType="RequestResponse", @@ -71,7 +115,8 @@ def main(): # pylint:disable=too-many-locals response_payload = response["Payload"] payload_data = json.load(response_payload) print(f"Certificate issued for {common_name}") - print(payload_data) + if args["verbose"]: + print(payload_data) # Extract certificate and private key from response base64_cert_data = payload_data["Base64Certificate"] @@ -87,7 +132,7 @@ def main(): # pylint:disable=too-many-locals if output_path_cert_pem: with open(output_path_cert_pem, "w", encoding="utf-8") as f: f.write(cert_data.decode("utf-8")) - print(f"Certificate written to {output_path_cert_pem}") + print(f"Combined client certificate and CA bundle written to {output_path_cert_pem}") if output_path_cert_crt: with open(output_path_cert_crt, "w", encoding="utf-8") as f: @@ -98,8 +143,9 @@ def main(): # pylint:disable=too-many-locals with open(output_path_cert_combined, "w", encoding="utf-8") as f: f.write(key_data.decode("utf-8")) f.write(cert_data.decode("utf-8")) - - print(f"Certificate and key written to {output_path_cert_combined}") + print( + f"Combined issuing and root certificate bundle and private key written to {output_path_cert_combined}" + ) if __name__ == "__main__": diff --git a/utils/generate-cert.py b/utils/generate-cert.py new file mode 100644 index 0000000..122b465 --- /dev/null +++ b/utils/generate-cert.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python3 + +import json +import base64 +import os +import sys +import argparse +import boto3 +from cryptography.hazmat.primitives.serialization import load_der_private_key +from modules.certs.crypto import create_csr_info, crypto_encode_private_key, crypto_tls_cert_signing_request +from modules.certs.kms import kms_generate_key_pair, kms_get_kms_key_id +from modules.aws.lambdas import get_lambda_name + +# identify home directory and create certs subdirectory if needed +homedir = os.path.expanduser("~") +dir_path = os.path.dirname(os.path.realpath(__file__)) +default_vardir = f"{dir_path}/variables" +default_varfile = "client.json" +default_base_path = f"{homedir}/ca/certificates" +default_key_pair_spec = "ECC_NIST_P256" + + +def parse_variables(input_file): + """ + Parse variables file to obtain variables + """ + + if os.path.isfile(input_file): + try: + with open(input_file, encoding="utf-8") as json_file: + data = json.load(json_file) + except Exception as e: + data = {"error": f"Unable to parse JSON. {e}"} + else: + data = {"error": "No file"} + return data + + +def create_session(profile): + """ + Creates and returns AWS session using profile + """ + + try: + session = boto3.session.Session(profile_name=profile) + except Exception as e: + session = {"error": e} + if isinstance(session, dict): + print(f"Error: Unable to open session using profile {profile}. {session['error']}") + sys.exit(1) + else: + print(f"AWS session opened using profile: {profile}") + return session + + +def parse_arguments(): + """ + Read arguments and return arguments dictonary + """ + + parser = argparse.ArgumentParser() + parser.add_argument("--server", action="store_true", help="The role for which the certificate is being generated") + parser.add_argument("--profile", default="default", help="AWS profile described in .aws/config file") + parser.add_argument("--vardir", default=default_vardir, help="Directory with JSON files with variables") + parser.add_argument("--varfile", default=default_varfile, help="JSON variables file") + parser.add_argument("--keyalgo", default=default_key_pair_spec, help="Key algorithm") + parser.add_argument("--destination", default=default_base_path, help="Destination directory for generated keys") + parser.add_argument("--keygenalias", default=None, help="Alias for KMS key") + parser.add_argument("--verbose", action="store_true", help="Output of all generated payload data") + + return vars(parser.parse_args()) + + +def get_first_certificate(data): + """ + Leave only the first certificate + """ + + certs = data.split("-----END CERTIFICATE-----") + certs = [cert.strip() + "\n-----END CERTIFICATE-----" for cert in certs if cert.strip()] + if certs: + return certs[0] + return None + + +def prepare_certificate_variables(variables, args, is_server): + """ + Prepare certificate-related variables from the parsed JSON + """ + + lifetime = variables["lifetime"] + common_name = variables["common_name"] + country = variables["country"] + locality = variables["locality"] + state = variables["state"] + organization = variables["organization"] + organizational_unit = variables["organizational_unit"] + purposes = variables["purposes"] + key_alias = variables["key_alias"] if args["keygenalias"] is None else args["keygenalias"] + sans = variables.get("sans", None) if is_server else None + + return lifetime, common_name, country, locality, state, organization, organizational_unit, purposes, key_alias, sans + + +def generate_key_pair(key_alias, session, key_algo): + """ + Generate key pair using symmetric KMS key + """ + + key_id = kms_get_kms_key_id(key_alias, session=session) + if isinstance(key_id, dict): + print(f'Error: {key_id["error"]}') + sys.exit(1) + + kms_response = kms_generate_key_pair(key_id, key_pair_spec=key_algo, session=session) + private_key = load_der_private_key(kms_response["PrivateKeyPlaintext"], None) + + return private_key + + +def create_csr(private_key, csr_data, state): + """ + Create CSR + """ + + common_name = csr_data["common_name"] + country = csr_data["country"] + locality = csr_data["locality"] + organization = csr_data["organization"] + organizational_unit = csr_data["organizational_unit"] + state = csr_data["state"] + csr_info = create_csr_info(common_name, country, locality, organization, organizational_unit, state) + + return crypto_tls_cert_signing_request(private_key, csr_info) + + +def prepare_request_payload(csr_pem, common_name, purposes, lifetime, sans): + """ + Prepare the request payload for Lambda + """ + + request_payload = { + "common_name": common_name, + "purposes": purposes, + "lifetime": lifetime, + "base64_csr_data": base64.b64encode(csr_pem).decode("utf-8"), + "force_issue": True, + "cert_bundle": True, + } + if sans is not None: + request_payload["sans"] = sans + + return json.dumps(request_payload) + + +def invoke_lambda(session, request_payload_bytes): + """ + Invoke the TLS certificate Lambda function + """ + + lambda_name = get_lambda_name("tls-cert", session=session) + client = session.client("lambda") + response = client.invoke( + FunctionName=lambda_name, + InvocationType="RequestResponse", + LogType="None", + Payload=bytes(request_payload_bytes.encode("utf-8")), + ) + response_payload = response["Payload"] + + return json.load(response_payload) + + +def write_certificate_files(cert_data, key_data, common_name, base_path, is_server): + """ + Write certificate and private key to files + """ + + clean_common_name = common_name.split(".")[0] + certificate_dir = f"{base_path}/{clean_common_name}" + os.makedirs(certificate_dir, exist_ok=True) + + role = "server" if is_server else "client" + + # Set output file paths + output_path_cert_key = f"{certificate_dir}/{role}-key.pem" # private key + output_path_cert_crt = f"{certificate_dir}/{role}-cert.crt" # certificate + output_path_cert_pem = f"{certificate_dir}/ca.pem" # cert + issuing CA + root CA PEM bundle + + # Write certificate and private key to files + with open(output_path_cert_key, "w", encoding="utf-8") as f: + f.write(key_data.decode("utf-8")) + print(f"Private key written to {output_path_cert_key}") + + with open(output_path_cert_pem, "w", encoding="utf-8") as f: + f.write(cert_data.decode("utf-8")) + print(f"Certificate chain written to {output_path_cert_pem}") + + cert = get_first_certificate(cert_data.decode("utf-8")) or cert_data.decode("utf-8") + with open(output_path_cert_crt, "w", encoding="utf-8") as f: + f.write(cert) + print(f"Certificate written to {output_path_cert_crt}") + + +def main(): # pylint:disable=too-many-locals + """ + Create certificate for default Serverless CA environment + """ + + args = parse_arguments() + + varfile = f"{args['vardir']}/{args['varfile']}" + is_server = args["server"] + + if is_server: + print("Generating SERVER certificate") + else: + print("Generating CLIENT certificate") + + # set variables + variables = parse_variables(varfile) + if "error" in variables: + print(f'Error: Unable to read variables. {variables["error"]}') + sys.exit(1) + else: + print(f"Variables are obtained from file {varfile}") + + lifetime, common_name, country, locality, state, organization, organizational_unit, purposes, key_alias, sans = ( + prepare_certificate_variables(variables, args, is_server) + ) + + # Create AWS session + session = create_session(args["profile"]) + + # Generate key pair + private_key = generate_key_pair(key_alias, session, args["keyalgo"]) + + # Create CSR + csr_org_data = { + "common_name": common_name, + "country": country, + "locality": locality, + "organization": organization, + "organizational_unit": organizational_unit, + "state": state, + } + csr_pem = create_csr(private_key, csr_org_data, state) + + # Prepare request payload for Lambda + request_payload_bytes = prepare_request_payload(csr_pem, common_name, purposes, lifetime, sans) + + # Invoke Lambda function + payload_data = invoke_lambda(session, request_payload_bytes) + print(f"Certificate issued for {common_name}") + + if args["verbose"]: + print(payload_data) + + # Extract certificate and private key + base64_cert_data = payload_data["Base64Certificate"] + cert_data = base64.b64decode(base64_cert_data) + key_data = crypto_encode_private_key(private_key) + + # Write certificate and private key to files + write_certificate_files(cert_data, key_data, common_name, args["destination"], is_server) + + +if __name__ == "__main__": + main() diff --git a/utils/modules/aws/kms.py b/utils/modules/aws/kms.py index dc134e4..a18537c 100644 --- a/utils/modules/aws/kms.py +++ b/utils/modules/aws/kms.py @@ -1,12 +1,14 @@ import boto3 -def get_kms_details(key_purpose): +def get_kms_details(key_purpose, session=None): """ Get the KMS ARN based on the key purpose """ - - kms_client = boto3.client("kms") + if session is None: + kms_client = boto3.client("kms") + else: + kms_client = session.client("kms") key_aliases = kms_client.list_aliases()["Aliases"] key_aliases = [k for k in key_aliases if key_purpose in k["AliasName"]] diff --git a/utils/modules/aws/lambdas.py b/utils/modules/aws/lambdas.py index cf7edfa..0f13fd4 100644 --- a/utils/modules/aws/lambdas.py +++ b/utils/modules/aws/lambdas.py @@ -2,12 +2,14 @@ import json -def get_lambda_name(lambda_purpose): +def get_lambda_name(lambda_purpose, session=None): """ Get the full name of the Lambda function based on its purpose """ - - lambda_client = boto3.client("lambda") + if session is None: + lambda_client = boto3.client("lambda") + else: + lambda_client = session.client("lambda") lambdas = lambda_client.list_functions()["Functions"] lambdas = [la for la in lambdas if lambda_purpose in la["FunctionName"]] @@ -15,12 +17,15 @@ def get_lambda_name(lambda_purpose): return lambdas[0]["FunctionName"] -def invoke_lambda(function_name, json_data): +def invoke_lambda(function_name, json_data, session=None): """ Invoke TLS certificate Lambda function """ - lambda_client = boto3.client("lambda") + if session is None: + lambda_client = boto3.client("lambda") + else: + lambda_client = session.client("lambda") response = lambda_client.invoke( FunctionName=function_name, diff --git a/utils/modules/aws/s3.py b/utils/modules/aws/s3.py index 115dc69..8d87d76 100644 --- a/utils/modules/aws/s3.py +++ b/utils/modules/aws/s3.py @@ -1,22 +1,30 @@ import boto3 -def get_s3_bucket(bucket_purpose="internal"): +def get_s3_bucket(bucket_purpose="internal", session=None): """ Get the full name of the S3 bucket based on its purpose """ - s3_client = boto3.client("s3") + if session is None: + s3_client = boto3.client("s3") + else: + s3_client = session.client("s3") + s3_buckets = s3_client.list_buckets()["Buckets"] return [b["Name"] for b in s3_buckets if f"-{bucket_purpose}-" in b["Name"]][0] -def list_s3_object_keys(bucket_name): +def list_s3_object_keys(bucket_name, session=None): """ List object keys in S3 bucket """ - s3_client = boto3.client("s3") + if session is None: + s3_client = boto3.client("s3") + else: + s3_client = session.client("s3") + response = s3_client.list_objects_v2(Bucket=bucket_name) s3_objects = response["Contents"] @@ -24,32 +32,46 @@ def list_s3_object_keys(bucket_name): return [s3_object["Key"] for s3_object in s3_objects] -def get_s3_object(bucket_name, key): +def get_s3_object(bucket_name, key, session=None): """ Get object from S3 """ - s3_client = boto3.client("s3") + if session is None: + s3_client = boto3.client("s3") + else: + s3_client = session.client("s3") + response = s3_client.get_object(Bucket=bucket_name, Key=key) return response["Body"].read() -def put_s3_object(bucket_name, kms_arn, key, data, encryption_algorithm="aws:kms"): +def put_s3_object( + bucket_name, kms_arn, key, data, encryption_algorithm="aws:kms", session=None +): # pylint:disable=too-many-arguments,too-many-positional-arguments """ Put object in S3 bucket """ - s3_client = boto3.client("s3") + if session is None: + s3_client = boto3.client("s3") + else: + s3_client = session.client("s3") + s3_client.put_object( Bucket=bucket_name, SSEKMSKeyId=kms_arn, ServerSideEncryption=encryption_algorithm, Key=key, Body=data ) -def delete_s3_object(bucket_name, key): +def delete_s3_object(bucket_name, key, session=None): """ Delete object from S3 """ - s3_client = boto3.client("s3") + if session is None: + s3_client = boto3.client("s3") + else: + s3_client = session.client("s3") + s3_client.delete_object(Bucket=bucket_name, Key=key) diff --git a/utils/modules/certs/kms.py b/utils/modules/certs/kms.py index 3183e8e..2280d4c 100644 --- a/utils/modules/certs/kms.py +++ b/utils/modules/certs/kms.py @@ -1,8 +1,13 @@ import boto3 -def kms_generate_key_pair(key_id, key_pair_spec="ECC_NIST_P256"): - client = boto3.client(service_name="kms") +def kms_generate_key_pair(key_id, key_pair_spec="ECC_NIST_P256", session=None): + """Generate KMS key pair""" + + if session is None: + client = boto3.client(service_name="kms") + else: + client = session.client(service_name="kms") return client.generate_data_key_pair( KeyId=key_id, @@ -10,9 +15,19 @@ def kms_generate_key_pair(key_id, key_pair_spec="ECC_NIST_P256"): ) -def kms_get_kms_key_id(alias): +def kms_get_kms_key_id(alias, session=None): """returns the KMS Key ARN for a specified alias""" - client = boto3.client(service_name="kms") + + if session is None: + client = boto3.client(service_name="kms") + else: + client = session.client(service_name="kms") + aliases = client.list_aliases(Limit=100)["Aliases"] + try: + key_id_list = [a for a in aliases if a["AliasName"] == f"alias/{alias}"] + key_id = key_id_list[0]["TargetKeyId"] + except Exception as e: + key_id = {"error": f"Failed to get KMS key ID. {e}"} - return [a for a in aliases if a["AliasName"] == f"alias/{alias}"][0]["TargetKeyId"] + return key_id diff --git a/utils/server-cert.py b/utils/server-cert.py index e346a4b..f6c7216 100644 --- a/utils/server-cert.py +++ b/utils/server-cert.py @@ -1,8 +1,10 @@ #!/usr/bin/env python3 +import os +import sys import json import base64 +import argparse import boto3 -import os from cryptography.hazmat.primitives.serialization import load_der_private_key from modules.certs.crypto import create_csr_info, crypto_encode_private_key, crypto_tls_cert_signing_request from modules.certs.kms import kms_generate_key_pair, kms_get_kms_key_id @@ -16,11 +18,50 @@ os.makedirs(base_path) -def main(): # pylint:disable=too-many-locals +def create_session(profile): + """ + Creates and returns AWS session using profile + """ + + try: + if profile is not None: + session = boto3.session.Session(profile_name=profile) + else: + session = boto3.session.Session() + except Exception as e: + session = {"error": e} + if isinstance(session, dict): + print(f"Error: Unable to open session using profile {profile}. {session['error']}") + sys.exit(1) + else: + print(f"AWS session opened using profile: {profile}") + return session + + +def parse_arguments(): + """ + Read arguments and return arguments dictonary + """ + + arguments = {} + parser = argparse.ArgumentParser() + parser.add_argument("--profile", default=None, help="AWS profile described in .aws/config file") + parser.add_argument("--verbose", action="store_true", help="Output of all generated payload data") + arguments = vars(parser.parse_args()) + + return arguments + + +def main(): # pylint:disable=too-many-locals,too-many-statements """ - Create test server certificate for default Serverless CA environment + Create test client certificate for default Serverless CA environment """ + args = parse_arguments() + + # create AWS session + session = create_session(args["profile"]) + # set variables lifetime = 90 common_name = "test.example.com" @@ -38,8 +79,11 @@ def main(): # pylint:disable=too-many-locals key_alias = "serverless-tls-keygen-dev" # create key pair using symmetric KMS key to provide entropy - key_id = kms_get_kms_key_id(key_alias) - kms_response = kms_generate_key_pair(key_id) + key_id = kms_get_kms_key_id(key_alias, session=session) + if isinstance(key_id, dict): + print(f'Error: {key_id["error"]}') + sys.exit(1) + kms_response = kms_generate_key_pair(key_id, session=session) private_key = load_der_private_key(kms_response["PrivateKeyPlaintext"], None) # create CSR @@ -60,8 +104,8 @@ def main(): # pylint:disable=too-many-locals request_payload_bytes = json.dumps(request_payload) # Invoke TLS certificate Lambda function - lambda_name = get_lambda_name("tls-cert") - client = boto3.client("lambda") + lambda_name = get_lambda_name("tls-cert", session=session) + client = session.client("lambda") response = client.invoke( FunctionName=lambda_name, InvocationType="RequestResponse", @@ -73,7 +117,8 @@ def main(): # pylint:disable=too-many-locals response_payload = response["Payload"] payload_data = json.load(response_payload) print(f"Certificate issued for {common_name}") - print(payload_data) + if args["verbose"]: + print(payload_data) # Extract certificate and private key from response base64_cert_data = payload_data["Base64Certificate"] @@ -89,7 +134,7 @@ def main(): # pylint:disable=too-many-locals if output_path_cert_pem: with open(output_path_cert_pem, "w", encoding="utf-8") as f: f.write(cert_data.decode("utf-8")) - print(f"Certificate written to {output_path_cert_pem}") + print(f"Combined client certificate and CA bundle written to {output_path_cert_pem}") if output_path_cert_crt: with open(output_path_cert_crt, "w", encoding="utf-8") as f: @@ -100,8 +145,9 @@ def main(): # pylint:disable=too-many-locals with open(output_path_cert_combined, "w", encoding="utf-8") as f: f.write(key_data.decode("utf-8")) f.write(cert_data.decode("utf-8")) - - print(f"Certificate and key written to {output_path_cert_combined}") + print( + f"Combined issuing and root certificate bundle and private key written to {output_path_cert_combined}" + ) if __name__ == "__main__": diff --git a/utils/variables/client.json b/utils/variables/client.json new file mode 100644 index 0000000..e73fad7 --- /dev/null +++ b/utils/variables/client.json @@ -0,0 +1,13 @@ +{ + "lifetime": 90, + "common_name": "My Test Certificate", + "country": "GB", + "locality": "London", + "state": "England", + "organization": "Serverless Inc", + "organizational_unit": "Security Operations", + "purposes": [ + "client_auth" + ], + "key_alias": "serverless-tls-keygen-dev" +} diff --git a/utils/variables/server.json b/utils/variables/server.json new file mode 100644 index 0000000..5eb174e --- /dev/null +++ b/utils/variables/server.json @@ -0,0 +1,14 @@ +{ + "lifetime": 90, + "common_name": "test.example.com", + "sans": ["test.example.com", "test2.example.com"], + "country": "GB", + "locality": "London", + "state": "England", + "organization": "Serverless Inc", + "organizational_unit": "Security Operations", + "purposes": [ + "server_auth" + ], + "key_alias": "serverless-tls-keygen-dev" +}