Deploy AWS Cloudfront and security headers with Terraform

I made a post in July 2021 regarding AWS CloudFront + security headers. Using AWS Cloudfront and Cloudfront Functions, security headers were injected in viewer responses; however, AWS Cloudfront natively supports security headers as of Nov. 2nd, 2021 alongside configurable CORS and custom HTTP response headers.

Security Headers

Cloudfront can natively support all the security headers from the last post:

  • permissions-policy
  • referrer-policy
  • strict-transport-security
  • x-content-type-options
  • x-frame-options
  • x-xss-protection

Terraform Code

Begin with defining an aws_cloudfront_response_headers_policy resource in Terraform. This resources contains all the header policy information. In the following example, the values for each security_headers_config were copied from AWS’s documentation.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
resource "aws_cloudfront_response_headers_policy" "headers_policy" {
  name = "security-headers-policy"

  custom_headers_config {
    items {
      header = "permissions-policy"
      override = true
      value = "accelerometer=(), camera=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()"
    }
  }

  security_headers_config {
    content_type_options {
      override = true
    }

    frame_options {
      override = true
      frame_option = "DENY"
    }

    referrer_policy {
      override = true
      referrer_policy = "same-origin"
    }

    strict_transport_security {
      override = true
      access_control_max_age_sec = 63072000
      include_subdomains = true
      preload = true
    }

    xss_protection {
      override = true
      mode_block = true
      protection = true
    }
  }
}

Attach it to a Cloudfront Behavior. In this case, I attached it to the default_cache_behavior block.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
locals {
  cloudfront_origin_id = "s3"
}

resource "aws_cloudfront_distribution" "distribution" {
  aliases = var.domain_names
  enabled = true

  origin {
    domain_name = aws_s3_bucket.bucket.bucket_domain_name
    origin_id = local.cloudfront_origin_id
  }

  default_cache_behavior {
    allowed_methods = ["GET", "HEAD"]
    cached_methods = ["GET", "HEAD"]
    compress = true
    target_origin_id = local.cloudfront_origin_id
    viewer_protocol_policy = "redirect-to-https"
    response_headers_policy_id = aws_cloudfront_response_headers_policy.headers_policy.id
  }

  restrictions {
    geo_restriction {
      restriction_type = "none"
    }
  }
}

Once your infrastructure is deployed, AWS CloudFront will forward security headers. To see this in action, refer to the how.wtf repository.