diff --git a/.checkov.yaml b/.checkov.yaml new file mode 100644 index 0000000..b202d59 --- /dev/null +++ b/.checkov.yaml @@ -0,0 +1,21 @@ +skip-check: + # CKV + - CKV_AWS_18 # INFO "Ensure the S3 bucket has access logging enabled" + - CKV_AWS_59 # LOW "Ensure there is no open access to back-end resources through API" + - CKV_AWS_109 # LOW "Ensure IAM policies does not allow permissions management / resource exposure without constraints" + - CKV_AWS_111 # LOW "Ensure IAM policies does not allow write access without constraints" + - CKV_AWS_116 # LOW "Ensure that AWS Lambda function is configured for a Dead Letter Queue(DLQ)" + - CKV_AWS_117 # LOW "Ensure that AWS Lambda function is configured inside a VPC" + - CKV_AWS_120 # LOW "Ensure API Gateway caching is enabled" + - CKV_AWS_144 # LOW "Ensure that S3 bucket has cross-region replication enabled" + - CKV_AWS_158 # LOW "Ensure that CloudWatch Log Group is encrypted by KMS" + - CKV_AWS_225 # LOW "Ensure API Gateway method setting caching is enabled" + - CKV_AWS_272 # HIGH "Ensure AWS Lambda function is configured to validate code-signing" + - CKV_AWS_283 # HIGH "Ensure no IAM policies documents allow ALL or any AWS principal permissions to the resource" + - CKV_AWS_337 # HIGH "Ensure SSM parameters are using KMS CMK" + - CKV_AWS_338 # INFO "Ensure CloudWatch log groups retains logs for at least 1 year" + - CKV_AWS_356 # HIGH "Ensure no IAM policies documents allow "*" as a statement's resource for restrictable actions" + # CKV2 + - CKV2_AWS_29 # MEDIUM "Ensure public API gateway are protected by WAF" + - CKV2_AWS_51 # LOW "Ensure AWS API Gateway endpoints uses client certificate authentication" + - CKV2_AWS_73 # INFO "Ensure AWS SQS uses CMK not AWS default keys for encryption" diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml index 183c567..c9a84a8 100644 --- a/.github/workflows/validate.yml +++ b/.github/workflows/validate.yml @@ -49,6 +49,7 @@ jobs: - name: "🚥 Checkov" uses: bridgecrewio/checkov-action@v12 with: + config_file: .checkov.yaml output_format: cli,sarif output_file_path: console,results.sarif diff --git a/bootstrap/imports.tf b/bootstrap/imports.tf index 0153918..b84304d 100644 --- a/bootstrap/imports.tf +++ b/bootstrap/imports.tf @@ -13,6 +13,21 @@ import { id = "telegram-bot-bootstrap-${var.aws_account_id}" } +import { + to = aws_s3_bucket_lifecycle_configuration.state + id = "telegram-bot-bootstrap-${var.aws_account_id}" +} + +import { + to = aws_s3_bucket_notification.state + id = "telegram-bot-bootstrap-${var.aws_account_id}" +} + +import { + to = aws_s3_bucket_server_side_encryption_configuration.state + id = "telegram-bot-bootstrap-${var.aws_account_id}" +} + import { to = aws_iam_openid_connect_provider.github id = "arn:aws:iam::${var.aws_account_id}:oidc-provider/token.actions.githubusercontent.com" diff --git a/bootstrap/terraform_state.tf b/bootstrap/terraform_state.tf index 0c75e5c..88276a2 100644 --- a/bootstrap/terraform_state.tf +++ b/bootstrap/terraform_state.tf @@ -3,6 +3,17 @@ resource "aws_s3_bucket" "state" { force_destroy = false } +resource "aws_s3_bucket_server_side_encryption_configuration" "state" { + bucket = aws_s3_bucket.state.id + + rule { + apply_server_side_encryption_by_default { + sse_algorithm = "aws:kms" + } + bucket_key_enabled = true + } +} + resource "aws_s3_bucket_public_access_block" "state" { bucket = aws_s3_bucket.state.id @@ -19,3 +30,26 @@ resource "aws_s3_bucket_versioning" "state" { status = "Enabled" } } + +resource "aws_s3_bucket_lifecycle_configuration" "state" { + bucket = aws_s3_bucket.state.id + + rule { + id = "incomplete-multipart-uploads" + + filter { + prefix = "" + } + + abort_incomplete_multipart_upload { + days_after_initiation = 1 + } + + status = "Enabled" + } +} + +resource "aws_s3_bucket_notification" "state" { + bucket = aws_s3_bucket.state.id + eventbridge = true +} diff --git a/infra/modules/alerting/data.tf b/infra/modules/alerting/data.tf index aa880c0..9050135 100644 --- a/infra/modules/alerting/data.tf +++ b/infra/modules/alerting/data.tf @@ -1,3 +1,6 @@ +data "aws_region" "current" {} +data "aws_caller_identity" "current" {} + data "aws_iam_policy_document" "role_policies" { count = length(var.role_policies) diff --git a/infra/modules/alerting/emails.tf b/infra/modules/alerting/emails.tf index 6603589..0e7c144 100644 --- a/infra/modules/alerting/emails.tf +++ b/infra/modules/alerting/emails.tf @@ -1,6 +1,7 @@ resource "aws_sns_topic" "emails" { - name = "${var.name}-fallback-to-emails" - tags = var.tags + name = "${var.name}-fallback-to-emails" + kms_master_key_id = "alias/aws/sns" + tags = var.tags } resource "aws_sns_topic_subscription" "emails" { diff --git a/infra/modules/alerting/main.tf b/infra/modules/alerting/main.tf index 9c3fd13..2d2739c 100644 --- a/infra/modules/alerting/main.tf +++ b/infra/modules/alerting/main.tf @@ -1,6 +1,7 @@ resource "aws_sns_topic" "this" { - name = var.name - tags = var.tags + name = var.name + kms_master_key_id = "alias/aws/sns" + tags = var.tags } resource "aws_sns_topic_policy" "this" { diff --git a/infra/modules/alerting/telegram.tf b/infra/modules/alerting/telegram.tf index 646d1f1..91e2721 100644 --- a/infra/modules/alerting/telegram.tf +++ b/infra/modules/alerting/telegram.tf @@ -1,12 +1,14 @@ resource "aws_lambda_function" "telegram" { - filename = data.archive_file.lambda_zip.output_path - function_name = var.name - role = aws_iam_role.lambda_execution.arn - handler = "bootstrap" - source_code_hash = data.archive_file.lambda_zip.output_base64sha256 - runtime = "provided.al2023" - timeout = 10 - memory_size = 128 + filename = data.archive_file.lambda_zip.output_path + function_name = var.name + role = aws_iam_role.lambda_execution.arn + handler = "bootstrap" + source_code_hash = data.archive_file.lambda_zip.output_base64sha256 + runtime = "provided.al2023" + timeout = 10 + memory_size = 128 + kms_key_arn = "arn:aws:kms:${data.aws_region.current.region}:${data.aws_caller_identity.current.account_id}:alias/aws/lambda" + reserved_concurrent_executions = 10 environment { variables = { @@ -15,6 +17,10 @@ resource "aws_lambda_function" "telegram" { } } + tracing_config { + mode = "PassThrough" + } + tags = var.tags } diff --git a/infra/modules/api/main.tf b/infra/modules/api/main.tf index 93a3ef1..b4375f8 100644 --- a/infra/modules/api/main.tf +++ b/infra/modules/api/main.tf @@ -1,9 +1,10 @@ resource "aws_sqs_queue" "webhook" { - name = "${var.api_name}-webhook.fifo" - fifo_queue = true - content_based_deduplication = true - - tags = var.tags + name = "${var.api_name}-webhook.fifo" + fifo_queue = true + content_based_deduplication = true + kms_master_key_id = "alias/aws/sqs" + kms_data_key_reuse_period_seconds = 3600 + tags = var.tags } resource "aws_api_gateway_rest_api" "this" { @@ -13,6 +14,10 @@ resource "aws_api_gateway_rest_api" "this" { types = ["REGIONAL"] } + lifecycle { + create_before_destroy = true + } + tags = var.tags } @@ -28,10 +33,18 @@ resource "aws_api_gateway_resource" "webhook" { } resource "aws_api_gateway_method" "webhook" { - rest_api_id = aws_api_gateway_rest_api.this.id - resource_id = aws_api_gateway_resource.webhook.id - http_method = "POST" - authorization = "NONE" + rest_api_id = aws_api_gateway_rest_api.this.id + resource_id = aws_api_gateway_resource.webhook.id + http_method = "POST" + authorization = "NONE" + request_validator_id = aws_api_gateway_request_validator.webhook.id +} + +resource "aws_api_gateway_request_validator" "webhook" { + rest_api_id = aws_api_gateway_rest_api.this.id + name = "${var.api_name}-webhook-request" + validate_request_body = false + validate_request_parameters = false } resource "aws_api_gateway_integration" "sqs" { @@ -110,9 +123,10 @@ resource "aws_api_gateway_deployment" "this" { } resource "aws_api_gateway_stage" "this" { - deployment_id = aws_api_gateway_deployment.this.id - rest_api_id = aws_api_gateway_rest_api.this.id - stage_name = local.stage_v1 + deployment_id = aws_api_gateway_deployment.this.id + rest_api_id = aws_api_gateway_rest_api.this.id + stage_name = local.stage_v1 + xray_tracing_enabled = true access_log_settings { destination_arn = aws_cloudwatch_log_group.access_logs.arn @@ -128,3 +142,16 @@ resource "aws_api_gateway_stage" "this" { aws_api_gateway_account.this, ] } + +resource "aws_api_gateway_method_settings" "this" { + rest_api_id = aws_api_gateway_rest_api.this.id + stage_name = aws_api_gateway_stage.this.stage_name + method_path = "*/*" + + settings { + logging_level = "INFO" + metrics_enabled = true + throttling_rate_limit = 20 + throttling_burst_limit = 10 + } +} diff --git a/infra/modules/handler/data.tf b/infra/modules/handler/data.tf index 9cae5f1..9fe38f2 100644 --- a/infra/modules/handler/data.tf +++ b/infra/modules/handler/data.tf @@ -1,3 +1,6 @@ +data "aws_region" "current" {} +data "aws_caller_identity" "current" {} + data "aws_iam_policy_document" "role_policies" { count = length(var.role_policies) diff --git a/infra/modules/handler/main.tf b/infra/modules/handler/main.tf index c338311..656a557 100644 --- a/infra/modules/handler/main.tf +++ b/infra/modules/handler/main.tf @@ -1,17 +1,23 @@ resource "aws_lambda_function" "this" { - filename = data.archive_file.lambda_zip.output_path - function_name = var.function_name - role = aws_iam_role.lambda_execution.arn - handler = "bootstrap" - source_code_hash = data.archive_file.lambda_zip.output_base64sha256 - runtime = "provided.al2023" - timeout = var.timeout - memory_size = var.memory_size + filename = data.archive_file.lambda_zip.output_path + function_name = var.function_name + role = aws_iam_role.lambda_execution.arn + handler = "bootstrap" + source_code_hash = data.archive_file.lambda_zip.output_base64sha256 + runtime = "provided.al2023" + timeout = var.timeout + memory_size = var.memory_size + kms_key_arn = "arn:aws:kms:${data.aws_region.current.region}:${data.aws_caller_identity.current.account_id}:alias/aws/lambda" + reserved_concurrent_executions = 10 environment { variables = var.environment_variables } + tracing_config { + mode = "PassThrough" + } + tags = var.tags } diff --git a/infra/modules/queue/main.tf b/infra/modules/queue/main.tf index 452da0f..a9ee949 100644 --- a/infra/modules/queue/main.tf +++ b/infra/modules/queue/main.tf @@ -1,12 +1,12 @@ resource "aws_sqs_queue" "this" { - name = "${var.queue_name}.fifo" - fifo_queue = true - content_based_deduplication = true - - visibility_timeout_seconds = var.visibility_timeout_seconds - message_retention_seconds = var.message_retention_seconds - - tags = var.tags + name = "${var.queue_name}.fifo" + fifo_queue = true + content_based_deduplication = true + kms_master_key_id = "alias/aws/sqs" + kms_data_key_reuse_period_seconds = 3600 + visibility_timeout_seconds = var.visibility_timeout_seconds + message_retention_seconds = var.message_retention_seconds + tags = var.tags } resource "aws_sqs_queue_redrive_policy" "dlq" {