diff --git a/cloudwatch.tf b/cloudwatch.tf index e6f9afb..f64f215 100644 --- a/cloudwatch.tf +++ b/cloudwatch.tf @@ -3,8 +3,8 @@ resource "aws_cloudwatch_log_group" "lambda_log" { name = "${var.log_group_prefix}${var.name}" retention_in_days = 365 kms_key_id = aws_kms_key.encryption.arn + depends_on = [aws_kms_key.encryption] } -# #https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_log_stream resource "aws_cloudwatch_log_stream" "log_stream" { name = "${var.name}-lambda-log-stream" diff --git a/data.tf b/data.tf index 4a339a7..3c8f9bc 100644 --- a/data.tf +++ b/data.tf @@ -1,3 +1,4 @@ +#https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/caller_identity data "aws_caller_identity" "current" {} locals { principal_root_arn = "arn:aws:iam::${data.aws_caller_identity.current.account_id}:root" diff --git a/iam_role.tf b/iam_role.tf index 713b1ef..01bb9ef 100644 --- a/iam_role.tf +++ b/iam_role.tf @@ -54,6 +54,17 @@ resource "aws_iam_policy" "lambda_policy" { "kms:Decrypt" ] Resource = [aws_kms_key.encryption.arn] + }, + # Add S3 permissions for accessing signed code + { + Effect = "Allow", + Action = [ + "s3:GetObject", + "s3:GetObjectVersion" + ], + Resource = [ + "${aws_s3_bucket.lambda_source.arn}/*" + ] } ] }) diff --git a/kms_storage.tf b/kms_storage.tf new file mode 100644 index 0000000..7325b0c --- /dev/null +++ b/kms_storage.tf @@ -0,0 +1,65 @@ +#https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key +resource "aws_kms_key" "encrypt_storage" { + enable_key_rotation = true + description = "Key to encrypt S3 bucket in ${var.name}." + deletion_window_in_days = 7 +} +#https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_alias +resource "aws_kms_alias" "encrypt_storage" { + name = "alias/${var.name}-encrypt-storage" + target_key_id = aws_kms_key.encrypt_storage.key_id +} +#https://registry.terraform.io/providers/hashicorp/aws/latest/docs/data-sources/iam_policy_document +data "aws_iam_policy_document" "encrypt_storage_policy" { + statement { + sid = "Enable IAM User Permissions" + effect = "Allow" + principals { + type = "AWS" + identifiers = ["arn:aws:iam::${data.aws_caller_identity.current.account_id}:root"] + } + actions = [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey", + "kms:Create*", + "kms:Enable*", + "kms:List*", + "kms:Put*", + "kms:Update*", + "kms:Revoke*", + "kms:Disable*", + "kms:Get*", + "kms:Delete*", + "kms:ScheduleKeyDeletion", + "kms:CancelKeyDeletion", + "kms:TagResource", + "kms:UntagResource" + ] + resources = [aws_kms_key.encrypt_storage.arn] + } + statement { + sid = "Allow S3 to use the key" + effect = "Allow" + principals { + type = "Service" + identifiers = ["s3.amazonaws.com"] + } + actions = [ + "kms:Encrypt", + "kms:Decrypt", + "kms:ReEncrypt*", + "kms:GenerateDataKey*", + "kms:DescribeKey", + "kms:CreateGrant" + ] + resources = [aws_kms_key.encrypt_storage.arn] + } +} +#https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/kms_key_policy +resource "aws_kms_key_policy" "encrypt_storage" { + key_id = aws_kms_key.encrypt_storage.id + policy = data.aws_iam_policy_document.encrypt_storage_policy.json +} \ No newline at end of file diff --git a/lambda.tf b/lambda.tf index 497a21e..2822b20 100644 --- a/lambda.tf +++ b/lambda.tf @@ -4,9 +4,19 @@ data "archive_file" "python_file" { source_dir = "${path.module}/lambda_function/" output_path = "${path.module}/lambda_function/lambda_function.zip" } +# #https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_object +#Upload Lambda code to source S3 bucket +resource "aws_s3_object" "lambda_zip" { + bucket = aws_s3_bucket.lambda_source.bucket + key = "lambda_function.zip" + source = data.archive_file.python_file.output_path + etag = filemd5(data.archive_file.python_file.output_path) +} #https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function resource "aws_lambda_function" "lambda_run" { - filename = "${path.module}/lambda_function/lambda_function.zip" + s3_bucket = aws_signer_signing_job.build_signing_job.signed_object[0].s3[0].bucket + s3_key = aws_signer_signing_job.build_signing_job.signed_object[0].s3[0].key + #filename = data.archive_file.python_file.output_path source_code_hash = data.archive_file.python_file.output_base64sha256 function_name = var.name role = aws_iam_role.lambda_role.arn @@ -36,6 +46,8 @@ resource "aws_lambda_function" "lambda_run" { code_signing_config_arn = aws_lambda_code_signing_config.configuration.arn reserved_concurrent_executions = 5 #checkov:skip=CKV_AWS_50: Not applicable in this use case: X-Ray tracing is enabled for Lambda + # Ensure the code signing config is created before the Lambda function + depends_on = [aws_lambda_code_signing_config.configuration, aws_signer_signing_job.build_signing_job, aws_iam_role_policy_attachment.lambda_policy_attachement] } #https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/cloudwatch_event_rule resource "aws_cloudwatch_event_rule" "lambda_trigger" { diff --git a/lambda_signer.tf b/lambda_signer.tf index e237632..2591b4d 100644 --- a/lambda_signer.tf +++ b/lambda_signer.tf @@ -1,15 +1,46 @@ +# Generate a random string to use as a suffix +#https://registry.terraform.io/providers/hashicorp/random/latest/docs/resources/string +resource "random_string" "suffix" { + length = 8 + special = false + upper = false +} #https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/signer_signing_profile resource "aws_signer_signing_profile" "lambda_signing_profile" { platform_id = "AWSLambda-SHA384-ECDSA" - name = "${replace(var.name, "-", "_")}_lambda_signing_profile" + name = "${replace(var.name, "-", "_")}_lambda_signing_profile_${random_string.suffix.result}" + signature_validity_period { + value = 135 + type = "MONTHS" + } } #https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_code_signing_config resource "aws_lambda_code_signing_config" "configuration" { allowed_publishers { - signing_profile_version_arns = [aws_signer_signing_profile.lambda_signing_profile.arn] + signing_profile_version_arns = [aws_signer_signing_profile.lambda_signing_profile.version_arn] } policies { untrusted_artifact_on_deployment = "Enforce" } + description = "Code signing configuration for ${var.name} Lambda function." +} +#https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/signer_signing_job +resource "aws_signer_signing_job" "build_signing_job" { + profile_name = aws_signer_signing_profile.lambda_signing_profile.name + + source { + s3 { + bucket = aws_s3_bucket.lambda_source.bucket + key = aws_s3_object.lambda_zip.key + version = aws_s3_object.lambda_zip.version_id + } + } + + destination { + s3 { + bucket = aws_s3_bucket.lambda_source.bucket + prefix = "signed/" + } + } } \ No newline at end of file diff --git a/storage.tf b/storage.tf new file mode 100644 index 0000000..f55e67e --- /dev/null +++ b/storage.tf @@ -0,0 +1,62 @@ +#https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket +# Create S3 bucket for Lambda source code +resource "aws_s3_bucket" "lambda_source" { + bucket = "${var.name}-lambda-source-${data.aws_caller_identity.current.account_id}" + force_destroy = true + #checkov:skip=CKV_AWS_18: AWS Access logging not enabled on S3 buckets + #checkov:skip=CKV_AWS_144: Region replication not enabled on S3 bucket + #checkov:skip=CKV2_AWS_62: Ensure S3 buckets should have event notifications enabled + # These security controls are suppressed as this bucket is only used temporarily for Lambda code signing + # and is not intended for long-term storage or public access. The bucket has other security measures + # like encryption and public access blocking enabled. +} +#https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_versioning +# Enable versioning on the source S3 bucket (required for signing) +resource "aws_s3_bucket_versioning" "lambda_source" { + bucket = aws_s3_bucket.lambda_source.id + versioning_configuration { + status = "Enabled" + } +} +#https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_public_access_block +resource "aws_s3_bucket_public_access_block" "lambda_source" { + bucket = aws_s3_bucket.lambda_source.id + block_public_acls = true + block_public_policy = true + restrict_public_buckets = true + ignore_public_acls = true +} +#https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_server_side_encryption_configuration +resource "aws_s3_bucket_server_side_encryption_configuration" "lambda_source" { + bucket = aws_s3_bucket.lambda_source.id + + rule { + apply_server_side_encryption_by_default { + kms_master_key_id = aws_kms_key.encrypt_storage.arn + sse_algorithm = "aws:kms" + } + } +} + +#https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/s3_bucket_lifecycle_configuration +resource "aws_s3_bucket_lifecycle_configuration" "lambda_source" { + bucket = aws_s3_bucket.lambda_source.id + + rule { + id = "abort-multipart-uploads" + status = "Enabled" + + abort_incomplete_multipart_upload { + days_after_initiation = 3 + } + } + rule { + id = "cleanup-old-versions" + status = "Enabled" + + noncurrent_version_expiration { + noncurrent_days = 90 + } + } + +} \ No newline at end of file