Skip to content

OpenTofu module for a CloudFront distribution that passes traffic through WAF without caching.

License

Notifications You must be signed in to change notification settings

codeforamerica/tofu-modules-aws-cloudfront-waf

Repository files navigation

CloudFront WAF Module

Main Checks GitHub Release

This module creates a CloudFront distribution that passes traffic through a Web Application Firewall (WAF) without caching.

Usage

Add this module to your main.tf (or appropriate) file and configure the inputs to match your desired configuration. For example, to create a new distribution my-project.org that points to origin.my-project.org, you could use:

module "cloudfront_waf" {
  source = "github.com/codeforamerica/tofu-modules-aws-cloudfront-waf?ref=1.4.0"

  project     = "my-project"
  environment = "dev"
  domain      = "my-project.org"
  log_bucket  = module.logging.bucket
}

Make sure you re-run tofu init after adding the module to your configuration.

tofu init
tofu plan

To update the source for this module, pass -upgrade to tofu init:

tofu init -upgrade

Rules

The WAF is configured with the following managed rules groups. The priorities of these rules are spaced out to allow for custom rules to be inserted between.

Rule Group Name Priority Description
AWSManagedRulesAmazonIpReputationList 200 Protects against IP addresses with a poor reputation.
AWSManagedRulesCommonRuleSet 300 Protects against common threats.
AWSManagedRulesKnownBadInputsRuleSet 400 Protects against known bad inputs.
AWSManagedRulesSQLiRuleSet 500 Protects against SQL injection attacks.

Inputs

Name Description Type Default Required
domain Primary domain for the distribution. The hosted zone for this domain should be in the same account. string n/a yes
log_bucket Domain name of the S3 bucket to send logs to. string n/a yes
log_group CloudWatch log group to send WAF logs to. string n/a yes
project Project that these resources are supporting. string n/a yes
custom_headers Custom headers to send to the origin. map(string) {} no
environment The environment for the deployment. string "dev" no
ip_set_rules Custom IP Set rules for the WAF map(object) {} no
rate_limit_rules Rate limiting configuration for the WAF. map(object) {} no
origin_domain Fully qualified domain name for the origin. Defaults to origin.${subdomain}.${domain}. string n/a no
passive Enable passive mode for the WAF, counting all requests rather than blocking. bool false no
request_policy Managed request policy to associate with the distribution. See the managed policies for valid values. string "AllViewer" no
subdomain Subdomain for the distribution. Defaults to the environment. string n/a no
tags Optional tags to be applied to all resources. map(string) {} no
upload_paths Optional paths to allow uploads to. list(object) [] no

custom_headers

Note

Some headers can not be added to the request. These mostly represent common headers and those reserved for specific use cases, such as Content-Length and X-Amz-*. The full list of restricted headers can be found in the CloudFront documentation.

You can add custom headers to the request before passing it on to the origin. Simply specify the headers you want to add in a map. For example:

module "cloudfront_waf" {
  source = "github.com/codeforamerica/tofu-modules-aws-cloudfront-waf?ref=1.4.0"

  project     = "my-project"
  environment = "dev"
  domain      = "my-project.org"
  log_bucket  = module.logging.bucket

  custom_headers = {
    x-custom-header = "my-custom-value"
    x-origin-token  = "my-origin-token"
  }
}

ip_set_rules

To allow or deny traffic based on IP address, you can specify a map of IP set rules to create. You will need to create the IP set in your configuration, and provide the ARN of the resource. An IP set can be created with the wafv2_ip_set resource.

For example:

resource "aws_wafv2_ip_set" "security_scanners" {
  name               = "my-project-staging-security-scanners"
  description        = "Security scanners that are allowed to access the site."
  scope              = "CLOUDFRONT"
  ip_address_version = "IPV4"
  addresses          = [
    "1.2.3.4/32",
    "5.6.7.8/32"
  ]
}

module "cloudfront_waf" {
  source = "github.com/codeforamerica/tofu-modules-aws-cloudfront-waf?ref=1.4.0"

  project     = "my-project"
  environment = "staging"
  domain      = "my-project.org"
  log_bucket  = module.logging.bucket

  ip_set_rules = {
    scanners = {
      name     = "my-project-staging-security-scanners"
      priority = 0
      action   = "allow"
      arn      = aws_wafv2_ip_set.security_scanners.arn
    }
  }
}
Name Description Type Default Required
action The action to perform. string "allow" no
arn ARN of the IP set to match on. string n/a yes
name Name for this rule. Defaults to ${project}-${environment}-rate-${rule.key}. string "" no
priority Rule priority. Defaults to the rule's position in the map. number nil no

rate_limit_rules

To rate limit traffic based on IP address, you can specify a map of rate limit rules to create. The rate limit rules are applied in the order they are defined, or though the priority field.

Note

Rate limit rules are added after all IP set rules by default. Use priority to order your rules if you need more control.

For example, to rate limit requests to 300 over a 5-minute period:

module "cloudfront_waf" {
  source = "github.com/codeforamerica/tofu-modules-aws-cloudfront-waf?ref=1.4.0"

  project     = "my-project"
  environment = "staging"
  domain      = "my-project.org"
  log_bucket  = module.logging.bucket

  rate_limit_rules = {
    limit = {
      name   = "my-project-staging-rate-limit"
      action = "block"
      limit  = 500
      window = 500
    }
  }
}
Name Description Type Default Required
action The action to perform. string "block" no
name Name for this rule. Defaults to ${project}-${environment}-rate-${rule.key}. string "" no
limit The number of requests allowed within the window. Minimum value of 10. number 10 no
priority Rule priority. Defaults to the rule's position in the map + the number of IP set rules. number nil no
window Number of seconds to limit requests in. Options are: 60, 120, 300, 600 number 60 no

upload_paths

The AWSManagedRulesCommonRuleSet rule group, by default, will block requests over 8KB in size, via the SizeRestrictions_BODY rule. Additionally, random characters in the file metadata can trigger the CrossSiteScripting_BODY and SQLi_BODY rules. We can override this to exclude certain paths that are used for file uploads.

The new rule created by this override will be given the priority of 550, to ensure it comes after the common and SQLi rule sets.

Note

The constraint field defines how the path is matched. Valid values are: EXACTLY, STARTS_WITH, ENDS_WITH, CONTAINS, CONTAINS_WORD.

For more information on how these are applied, see the AWS documentation.

module "cloudfront_waf" {
  source = "github.com/codeforamerica/tofu-modules-aws-cloudfront-waf?ref=1.4.0"

  project     = "my-project"
  environment = "staging"
  domain      = "my-project.org"
  log_bucket  = module.logging.bucket

  upload_paths = [
    {
      constraint = "ENDS_WITH"
      path       = "/documents"
    },
    {
      constraint = "EXACTLY"
      path       = "/upload"
    }
  ]
}
Name Description Type Default Required
path The path to exempt from the rule. string n/a yes
constraint Constraint to apply when testing for the path string "EXACTLY" no

About

OpenTofu module for a CloudFront distribution that passes traffic through WAF without caching.

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Languages