|
@@ -0,0 +1,208 @@
|
|
|
+# trussworks/wafv2/aws has a basic WAF with the AWS Managed Ruleset
|
|
|
+# See https://registry.terraform.io/modules/trussworks/wafv2/aws/latest
|
|
|
+#
|
|
|
+# Attempted to add some sane defaults so we can customize as needed
|
|
|
+#
|
|
|
+# IMPORTANT NOTES:
|
|
|
+# An 'Allow' action stops processing immediately. Avoid them!
|
|
|
+
|
|
|
+# Goals:
|
|
|
+# - US IPs only - https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-geo-match.html
|
|
|
+
|
|
|
+resource "aws_wafv2_ip_set" "blocked" {
|
|
|
+ name = "blocked_ips"
|
|
|
+
|
|
|
+ scope = "REGIONAL"
|
|
|
+ ip_address_version = "IPV4"
|
|
|
+
|
|
|
+ addresses = toset(concat(var.additional_blocked_ips, local.blocked_ips))
|
|
|
+}
|
|
|
+
|
|
|
+resource "aws_wafv2_ip_set" "allowed" {
|
|
|
+ name = "allowed_ips"
|
|
|
+
|
|
|
+ scope = "REGIONAL"
|
|
|
+ ip_address_version = "IPV4"
|
|
|
+
|
|
|
+ addresses = var.allowed_ips
|
|
|
+}
|
|
|
+
|
|
|
+resource "aws_wafv2_rule_group" "xdr_custom_rules" {
|
|
|
+ name = "xdr_custom_rules"
|
|
|
+ scope = "REGIONAL"
|
|
|
+ capacity = 1
|
|
|
+
|
|
|
+ # Note, there is visibilty config for the group and for the rule
|
|
|
+ visibility_config {
|
|
|
+ cloudwatch_metrics_enabled = true
|
|
|
+ metric_name = "xdr_custom_rules"
|
|
|
+ sampled_requests_enabled = true
|
|
|
+ }
|
|
|
+
|
|
|
+ rule {
|
|
|
+ name = "Block_Nonpermitted_Countries"
|
|
|
+ priority = 100
|
|
|
+
|
|
|
+ action {
|
|
|
+ block {}
|
|
|
+ }
|
|
|
+
|
|
|
+ statement {
|
|
|
+ not_statement {
|
|
|
+ statement {
|
|
|
+ geo_match_statement {
|
|
|
+ country_codes = [
|
|
|
+ "US",
|
|
|
+ "DE",
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ visibility_config {
|
|
|
+ cloudwatch_metrics_enabled = true
|
|
|
+ metric_name = "Block_Nonpermitted_Countries"
|
|
|
+ sampled_requests_enabled = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ # Add additional custom rules here
|
|
|
+}
|
|
|
+
|
|
|
+module "wafv2" {
|
|
|
+ source = "trussworks/wafv2/aws"
|
|
|
+ version = "= 2.4.0"
|
|
|
+
|
|
|
+ name = replace(var.fqdns[0], ".", "_")
|
|
|
+ scope = "REGIONAL"
|
|
|
+
|
|
|
+ alb_arn = var.resource_arn
|
|
|
+ associate_alb = true
|
|
|
+ default_action = "block" # Note: The final action is actually to 'allow', provided the host header is correct
|
|
|
+
|
|
|
+ filtered_header_rule = {
|
|
|
+ header_types = var.fqdns
|
|
|
+ header_value = "host"
|
|
|
+ priority = 900
|
|
|
+ action = "allow"
|
|
|
+ }
|
|
|
+
|
|
|
+ # IP based rules are processed first.
|
|
|
+ # If an IP is blocked, it is blocked no matter what.
|
|
|
+ # If an IP is allowed, it is allowed only if it's not blocked, but no other WAF rules are processed.
|
|
|
+ ip_sets_rule = [
|
|
|
+ {
|
|
|
+ name = "blocked_ips"
|
|
|
+ action = "block"
|
|
|
+ priority = 10
|
|
|
+ ip_set_arn = aws_wafv2_ip_set.blocked.arn
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name = "allowed_ips"
|
|
|
+ action = "allow"
|
|
|
+ priority = 20
|
|
|
+ ip_set_arn = aws_wafv2_ip_set.allowed.arn
|
|
|
+ }
|
|
|
+ ]
|
|
|
+
|
|
|
+ # Custom Rules are defined above
|
|
|
+ group_rules = [
|
|
|
+ {
|
|
|
+ name = aws_wafv2_rule_group.xdr_custom_rules.name
|
|
|
+ arn = aws_wafv2_rule_group.xdr_custom_rules.arn
|
|
|
+ priority = 100
|
|
|
+ override_action = "none"
|
|
|
+ excluded_rules = []
|
|
|
+ }
|
|
|
+ ]
|
|
|
+
|
|
|
+ # A rate-based rule tracks the rate of requests for each originating IP address, and triggers the rule action when the rate exceeds a limit that you specify on the number of requests in any 5-minute time span
|
|
|
+ ip_rate_based_rule = {
|
|
|
+ name = "Rate_Limit"
|
|
|
+ priority = 200
|
|
|
+ limit = 900 # 900 requests per 5 minutes= 3 requests/second (sustained for 5 minutes)
|
|
|
+ action = "block"
|
|
|
+ }
|
|
|
+
|
|
|
+ # AWS managed rulesets
|
|
|
+ # Baseline was from trussworks/wafv2/aws, but copied here to be customized for our use and renumbered.
|
|
|
+ managed_rules = [
|
|
|
+ {
|
|
|
+ "excluded_rules": [],
|
|
|
+ "name": "AWSManagedRulesCommonRuleSet",
|
|
|
+ "override_action": "none",
|
|
|
+ "priority": 510
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "excluded_rules": [],
|
|
|
+ "name": "AWSManagedRulesAmazonIpReputationList",
|
|
|
+ "override_action": "none",
|
|
|
+ "priority": 520
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "excluded_rules": [],
|
|
|
+ "name": "AWSManagedRulesKnownBadInputsRuleSet",
|
|
|
+ "override_action": "none",
|
|
|
+ "priority": 530
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "excluded_rules": [],
|
|
|
+ "name": "AWSManagedRulesSQLiRuleSet",
|
|
|
+ "override_action": "none",
|
|
|
+ "priority": 540
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "excluded_rules": [],
|
|
|
+ "name": "AWSManagedRulesLinuxRuleSet",
|
|
|
+ "override_action": "none",
|
|
|
+ "priority": 550
|
|
|
+ },
|
|
|
+ {
|
|
|
+ "excluded_rules": [],
|
|
|
+ "name": "AWSManagedRulesUnixRuleSet",
|
|
|
+ "override_action": "none",
|
|
|
+ "priority": 560
|
|
|
+ }
|
|
|
+ ]
|
|
|
+ tags = var.tags
|
|
|
+}
|
|
|
+
|
|
|
+resource "aws_wafv2_web_acl_logging_configuration" "waf_logs" {
|
|
|
+ log_destination_configs = [ "arn:${var.aws_partition}:firehose:${var.aws_region}:${var.aws_account_id}:deliverystream/aws-waf-logs-splunk" ]
|
|
|
+ resource_arn = module.wafv2.web_acl_id
|
|
|
+
|
|
|
+# logging_filter {
|
|
|
+# default_behavior = "KEEP"
|
|
|
+#
|
|
|
+# filter {
|
|
|
+# behavior = "DROP"
|
|
|
+#
|
|
|
+# condition {
|
|
|
+# action_condition {
|
|
|
+# action = "COUNT"
|
|
|
+# }
|
|
|
+# }
|
|
|
+#
|
|
|
+# condition {
|
|
|
+# label_name_condition {
|
|
|
+# label_name = "awswaf:111122223333:rulegroup:testRules:LabelNameZ"
|
|
|
+# }
|
|
|
+# }
|
|
|
+#
|
|
|
+# requirement = "MEETS_ALL"
|
|
|
+# }
|
|
|
+#
|
|
|
+# filter {
|
|
|
+# behavior = "KEEP"
|
|
|
+#
|
|
|
+# condition {
|
|
|
+# action_condition {
|
|
|
+# action = "ALLOW"
|
|
|
+# }
|
|
|
+# }
|
|
|
+#
|
|
|
+# requirement = "MEETS_ANY"
|
|
|
+# }
|
|
|
+# }
|
|
|
+}
|