diff --git a/aws_acm_certificates.tf b/aws_acm_certificates.tf index 234bd01..1d578df 100644 --- a/aws_acm_certificates.tf +++ b/aws_acm_certificates.tf @@ -13,19 +13,19 @@ resource "aws_acm_certificate_validation" "cert" { provider = aws.cloudfront certificate_arn = aws_acm_certificate.certificate.arn validation_record_fqdns = [for record in aws_route53_record.certificate_validation : record.fqdn] - depends_on = [aws_route53_record.certificate_validation] + depends_on = [aws_route53_record.certificate_validation] } resource "aws_route53_record" "certificate_validation" { - for_each = { + for_each = var.whitelabel_domain == false ? { for dvo in aws_acm_certificate.certificate.domain_validation_options : dvo.domain_name => { name = dvo.resource_record_name record = dvo.resource_record_value type = dvo.resource_record_type } - } + } : {} - zone_id = data.aws_route53_zone.current.zone_id + zone_id = one(data.aws_route53_zone.current).zone_id allow_overwrite = true name = each.value.name records = [each.value.record] diff --git a/aws_cloudfront_distribution.tf b/aws_cloudfront_distribution.tf index 0eb519a..b6f20f4 100644 --- a/aws_cloudfront_distribution.tf +++ b/aws_cloudfront_distribution.tf @@ -8,7 +8,7 @@ resource "aws_cloudfront_distribution" "s3_distribution" { origin_access_identity = aws_cloudfront_origin_access_identity.current.cloudfront_access_identity_path } } - comment = "${var.distribution_name} distribution" + comment = "${var.distribution_name} distribution" enabled = true is_ipv6_enabled = true @@ -18,13 +18,13 @@ resource "aws_cloudfront_distribution" "s3_distribution" { cloudfront_default_certificate = var.use_cloudfront_default_certificate acm_certificate_arn = aws_acm_certificate.certificate.arn ssl_support_method = "sni-only" - minimum_protocol_version = "TLSv1.2_2021" + minimum_protocol_version = var.minimum_protocol_version } custom_error_response { - error_caching_min_ttl = 300 - error_code = 404 - response_code = 200 + error_caching_min_ttl = var.custom_error_response_min_ttl + error_code = var.custom_error_response_error_code + response_code = var.custom_error_response_code response_page_path = "/index.html" } @@ -33,14 +33,14 @@ resource "aws_cloudfront_distribution" "s3_distribution" { ] logging_config { - bucket = module.bucket_cloudwatch_logs_backup.bucket_domain_name + bucket = module.bucket_cloudwatch_logs_backup.s3_bucket_bucket_domain_name include_cookies = false prefix = "cloudfront/" } #caching default_cache_behavior { - response_headers_policy_id = aws_cloudfront_response_headers_policy.security_headers_policy.id + response_headers_policy_id = var.response_header_policy_enable ? one(aws_cloudfront_response_headers_policy.security_headers_policy).id : "" min_ttl = var.cloudfront_cache_min_ttl default_ttl = var.cloudfront_cache_default_ttl @@ -68,12 +68,11 @@ resource "aws_cloudfront_distribution" "s3_distribution" { target_origin_id = "${data.aws_s3_bucket.origin_bucket.id}-origin" viewer_protocol_policy = "redirect-to-https" - dynamic "lambda_function_association" { - for_each = var.lambda_function_association + dynamic "function_association" { + for_each = var.function_associations content { - event_type = lambda_function_association.value.event_type - include_body = lookup(lambda_function_association.value, "include_body", null) - lambda_arn = lambda_function_association.value.lambda_arn + event_type = function_association.value.event_type + function_arn = function_association.value.function_arn } } } @@ -96,8 +95,9 @@ resource "aws_cloudfront_distribution" "s3_distribution" { resource "aws_cloudfront_origin_access_identity" "current" {} - resource "aws_cloudfront_response_headers_policy" "security_headers_policy" { - name = "${var.distribution_name}-cloudfront-security-headers-policy" +resource "aws_cloudfront_response_headers_policy" "security_headers_policy" { + name = "${var.distribution_name}-cloudfront-security-headers-policy" + count = var.response_header_policy_enable ? 1 : 0 security_headers_config { # https://infosec.mozilla.org/guidelines/web_security#x-content-type-options # content_type_options { @@ -106,7 +106,7 @@ resource "aws_cloudfront_origin_access_identity" "current" {} # https://infosec.mozilla.org/guidelines/web_security#x-frame-options frame_options { frame_option = "DENY" - override = true + override = true } # https://infosec.mozilla.org/guidelines/web_security#referrer-policy # referrer_policy { @@ -122,9 +122,9 @@ resource "aws_cloudfront_origin_access_identity" "current" {} # https://infosec.mozilla.org/guidelines/web_security#http-strict-transport-security strict_transport_security { access_control_max_age_sec = "63072000" - include_subdomains = true - preload = true - override = true + include_subdomains = true + preload = true + override = true } # https://infosec.mozilla.org/guidelines/web_security#content-security-policy # content_security_policy { diff --git a/aws_route53_a_record.tf b/aws_route53_a_record.tf index dd6e866..ea7337f 100644 --- a/aws_route53_a_record.tf +++ b/aws_route53_a_record.tf @@ -1,7 +1,10 @@ resource "aws_route53_record" "fqdn_cloudfront_dist" { - zone_id = data.aws_route53_zone.current.zone_id + count = var.whitelabel_domain ? 0 : 1 + zone_id = one(data.aws_route53_zone.current).zone_id name = var.distribution_fqdn - type = "A" + + allow_overwrite = false + type = "A" alias { evaluate_target_health = false name = aws_cloudfront_distribution.s3_distribution.domain_name diff --git a/data.aws_route53_zone.tf b/data.aws_route53_zone.tf index cfc5328..2a75147 100644 --- a/data.aws_route53_zone.tf +++ b/data.aws_route53_zone.tf @@ -1,4 +1,5 @@ data "aws_route53_zone" "current" { + count = var.whitelabel_domain ? 0 : 1 name = var.hosted_zone_name private_zone = false } diff --git a/data_aws_caller_identity.tf b/data_aws_caller_identity.tf index 5ea1bc1..3b55507 100644 --- a/data_aws_caller_identity.tf +++ b/data_aws_caller_identity.tf @@ -4,4 +4,6 @@ data "aws_availability_zones" "this" {} data "aws_caller_identity" "current" {} +data "aws_canonical_user_id" "current" {} + data "aws_iam_account_alias" "current" {} \ No newline at end of file diff --git a/data_aws_s3_cloudfront_origin_bucket.tf b/data_aws_s3_cloudfront_origin_bucket.tf index f4e4fe3..675951b 100644 --- a/data_aws_s3_cloudfront_origin_bucket.tf +++ b/data_aws_s3_cloudfront_origin_bucket.tf @@ -1,3 +1,3 @@ data "aws_s3_bucket" "origin_bucket" { - bucket = var.s3_source_bukcet_name + bucket = var.s3_source_bucket_name } \ No newline at end of file diff --git a/example/module_cloudfront_example.tf b/example/module_cloudfront_example.tf index d4a400e..fd4c93d 100644 --- a/example/module_cloudfront_example.tf +++ b/example/module_cloudfront_example.tf @@ -1,9 +1,9 @@ module "cloudfront_example" { source = "git::ssh://git@github.com/osodevops/aws-terraform-module-cloudfront-s3.git" - s3_source_bukcet_name = local.example_bucket_name + s3_source_bucket_name = local.example_bucket_name distribution_fqdn = "example.domain-name.com" distribution_name = "example" hosted_zone_name = "domain-name.com" common_tags = var.common_tags cloudfront_cache_compress_content = var.cloudfront_cache_compress_content -} \ No newline at end of file +} diff --git a/module_s3_bucket_cloudfront_logging.tf b/module_s3_bucket_cloudfront_logging.tf index b152838..7ede0f6 100644 --- a/module_s3_bucket_cloudfront_logging.tf +++ b/module_s3_bucket_cloudfront_logging.tf @@ -1,9 +1,14 @@ module "bucket_cloudwatch_logs_backup" { - source = "git::ssh://git@github.com/osodevops/aws-terraform-module-s3.git" - s3_bucket_name = local.logging_bucket_name - s3_bucket_force_destroy = false - s3_bucket_policy = "" - common_tags = var.common_tags + source = "terraform-aws-modules/s3-bucket/aws" + version = "~>4.0" + + bucket = local.logging_bucket_name + force_destroy = false + tags = var.common_tags + acl = var.whitelabel_domain ? null : "private" + object_ownership = "ObjectWriter" + control_object_ownership = var.whitelabel_domain ? true : false + attach_access_log_delivery_policy = var.whitelabel_domain ? true : false # Bucket public access restrict_public_buckets = true @@ -12,16 +17,20 @@ module "bucket_cloudwatch_logs_backup" { ignore_public_acls = true versioning = { - status = "Enabled" + status = var.s3_logging_versioning mfa_delete = "Disabled" } - cors_rule = { - allowed_headers = ["Authorization"] - allowed_methods = ["GET"] - allowed_origins = ["*"] - expose_headers = [] - max_age_seconds = 3000 + server_side_encryption_configuration = { + rule = { + bucket_key_enabled = false + + apply_server_side_encryption_by_default = { + sse_algorithm = "AES256" + } + } } -} \ No newline at end of file + cors_rule = var.cors_rules + +} diff --git a/outputs.tf b/outputs.tf index 694c8fa..f135447 100644 --- a/outputs.tf +++ b/outputs.tf @@ -1,5 +1,5 @@ output "logging_bucket" { - value = module.bucket_cloudwatch_logs_backup.s3_id + value = module.bucket_cloudwatch_logs_backup.s3_bucket_id } output "distribution" { diff --git a/terraform.tf b/terraform.tf index f563c66..8e338d5 100644 --- a/terraform.tf +++ b/terraform.tf @@ -2,7 +2,7 @@ terraform { required_providers { aws = { source = "hashicorp/aws" - version = "4.67.0" + version = ">=4.67.0" } } } \ No newline at end of file diff --git a/variables.tf b/variables.tf index dbf0514..9dc84e5 100644 --- a/variables.tf +++ b/variables.tf @@ -22,13 +22,43 @@ variable "cloudfront_cache_compress_content" { default = false } +variable "cors_rules" { + description = "List of maps of cors rules to ap[ply to the logging bucket" + type = list(object({ + allowed_headers = list(string) + allowed_methods = list(string) + allowed_origins = list(string) + expose_headers = list(string) + max_age_seconds = number + })) + default = [] +} + +variable "custom_error_response_error_code" { + description = "Custom error code for error response" + type = number + default = 404 +} + +variable "custom_error_response_min_ttl" { + description = "Minimum time-to-live for error caching" + type = number + default = 300 +} + +variable "custom_error_response_code" { + description = "Custom error code for error response" + type = number + default = 200 +} + variable "distribution_fqdn" { type = string description = "Fully qualified domain bound to Cloudfront." } variable "distribution_name" { - type = string + type = string description = "A unique name give to the distribution." } @@ -37,13 +67,25 @@ variable "hosted_zone_name" { description = "The route53 zone." } +variable "s3_logging_versioning" { + description = "Whether to version the contents of the logging bucket" + type = string + default = "Suspended" +} + +variable "minimum_protocol_version" { + description = "Minimum protocol version for the viewer certificate" + type = string + default = "TLSv1.2_2021" +} + variable "price_class" { - type = string + type = string description = "The price class for this distribution." - default = "PriceClass_100" + default = "PriceClass_100" } -variable "s3_source_bukcet_name" { +variable "s3_source_bucket_name" { type = string } @@ -52,15 +94,19 @@ variable "ttl" { default = "300" } -variable "lambda_function_association" { - type = list(object({ +variable "function_associations" { + description = "A config block that triggers a function with specific actions" + type = list(object({ event_type = string - include_body = bool - lambda_arn = string + function_arn = string })) + default = [] +} - description = "A config block that triggers a lambda function with specific actions" - default = [] +variable "response_header_policy_enable" { + description = "Feature-flag for including response header policy" + type = bool + default = true } variable "use_cloudfront_default_certificate" { @@ -75,6 +121,12 @@ variable "web_acl_id" { default = "" } +variable "whitelabel_domain" { + description = "Flag to toggle whitelabeling the domain" + type = bool + default = false +} + variable "common_tags" { type = map(string) description = "Implements the common tags."