modules/regional-backend-security-policy/main.tf (297 lines of code) (raw):
/**
* Copyright 2024 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
locals {
### find all the preconfigured rule with no include or exclude expression
pre_configured_rules_no_cond_expr = { for name, policy in var.pre_configured_rules : name => {
expression = "evaluatePreconfiguredWaf('${policy["target_rule_set"]}', {'sensitivity': ${policy["sensitivity_level"]}})"
} if length(policy["include_target_rule_ids"]) == 0 && length(policy["exclude_target_rule_ids"]) == 0
}
### find all the preconfigured rule with include (Opt In rules) expression
pre_configured_rules_include = { for name, policy in var.pre_configured_rules : name => {
target_rule_set = policy.target_rule_set
include_target_rule_ids = replace(join(",", policy.include_target_rule_ids), ",", "','")
sensitivity_level = policy.sensitivity_level
action = policy.action
priority = 0
description = policy.description
preview = policy.preview
rate_limit_options = policy.rate_limit_options
} if length(policy["include_target_rule_ids"]) > 0
}
pre_configured_rules_include_expr = { for name, policy in local.pre_configured_rules_include : name => {
expression = "evaluatePreconfiguredWaf('${policy["target_rule_set"]}', {'sensitivity': 0, 'opt_in_rule_ids': ['${policy.include_target_rule_ids}']})"
}
}
### find all the preconfigured rule with Exclude (Opt out rules) expression
pre_configured_rules_exclude = { for name, policy in var.pre_configured_rules : name => {
target_rule_set = policy.target_rule_set
exclude_target_rule_ids = replace(join(",", policy.exclude_target_rule_ids), ",", "','")
sensitivity_level = policy.sensitivity_level
action = policy.action
priority = policy.priority
description = policy.description
preview = policy.preview
rate_limit_options = policy.rate_limit_options
} if length(policy["include_target_rule_ids"]) == 0 && length(policy["exclude_target_rule_ids"]) > 0
}
pre_configured_rules_exclude_expr = { for name, policy in local.pre_configured_rules_exclude : name => {
expression = "evaluatePreconfiguredWaf('${policy["target_rule_set"]}', {'sensitivity': ${policy.sensitivity_level}, 'opt_out_rule_ids': ['${policy.exclude_target_rule_ids}']})"
}
}
## Combine all the preconfigured rules
pre_configured_rules_expr = merge(local.pre_configured_rules_no_cond_expr, local.pre_configured_rules_include_expr, local.pre_configured_rules_exclude_expr)
}
resource "google_compute_region_security_policy" "security_policy" {
provider = google-beta
project = var.project_id
name = var.name
description = var.description
type = var.type
region = var.region
}
##### Security Rules Block IP addresses
resource "google_compute_region_security_policy_rule" "security_rules" {
provider = google-beta
for_each = var.security_rules == null ? {} : { for x in var.security_rules : x.priority => x }
project = var.project_id
region = var.region
security_policy = google_compute_region_security_policy.security_policy.name
action = each.value["action"]
priority = each.value["priority"]
preview = each.value["preview"]
description = each.value["description"]
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = each.value["src_ip_ranges"]
}
}
### Rate limit. Execute only if Action is "rate_based_ban" or "throttle"
dynamic "rate_limit_options" {
for_each = each.value["action"] == "rate_based_ban" || each.value["action"] == "throttle" ? ["rate_limits"] : []
content {
conform_action = "allow"
ban_duration_sec = each.value["action"] == "rate_based_ban" ? lookup(each.value["rate_limit_options"], "ban_duration_sec") : null
exceed_action = lookup(each.value["rate_limit_options"], "exceed_action")
enforce_on_key = lookup(each.value["rate_limit_options"], "enforce_on_key_configs") == null ? lookup(each.value["rate_limit_options"], "enforce_on_key", null) : ""
enforce_on_key_name = lookup(each.value["rate_limit_options"], "enforce_on_key_configs") == null ? lookup(each.value["rate_limit_options"], "enforce_on_key_name", null) : null
dynamic "enforce_on_key_configs" {
for_each = lookup(each.value["rate_limit_options"], "enforce_on_key_configs") == null ? {} : { for x in lookup(each.value["rate_limit_options"], "enforce_on_key_configs") : x.enforce_on_key_type => x }
content {
enforce_on_key_type = enforce_on_key_configs.value.enforce_on_key_type
enforce_on_key_name = enforce_on_key_configs.value.enforce_on_key_name
}
}
## Required for all rate limit options
dynamic "rate_limit_threshold" {
for_each = each.value["action"] == "rate_based_ban" || each.value["action"] == "throttle" ? ["rate_limit_options"] : []
content {
count = each.value["rate_limit_options"].rate_limit_http_request_count
interval_sec = each.value["rate_limit_options"].rate_limit_http_request_interval_sec
}
}
## Optional. Can be provided for for rate based ban. Not needed for throttle
dynamic "ban_threshold" {
for_each = each.value["action"] == "rate_based_ban" && lookup(each.value["rate_limit_options"], "ban_http_request_count", null) != null && lookup(each.value["rate_limit_options"], "ban_http_request_interval_sec", null) != null ? ["ban_threshold"] : []
content {
count = lookup(each.value["rate_limit_options"], "ban_http_request_count")
interval_sec = lookup(each.value["rate_limit_options"], "ban_http_request_interval_sec")
}
}
}
}
}
##### Custom Rules
resource "google_compute_region_security_policy_rule" "custom_rules" {
provider = google-beta
for_each = var.custom_rules == null ? {} : { for x in var.custom_rules : x.priority => x }
project = var.project_id
region = var.region
security_policy = google_compute_region_security_policy.security_policy.name
action = each.value["action"]
priority = each.value["priority"]
preview = each.value["preview"]
description = each.value["description"]
match {
expr {
expression = each.value["expression"]
}
}
### Rate limit. Execute only if Action is "rate_based_ban" or "throttle"
dynamic "rate_limit_options" {
for_each = each.value["action"] == "rate_based_ban" || each.value["action"] == "throttle" ? ["rate_limits"] : []
content {
conform_action = "allow"
ban_duration_sec = each.value["action"] == "rate_based_ban" ? lookup(each.value["rate_limit_options"], "ban_duration_sec") : null
exceed_action = lookup(each.value["rate_limit_options"], "exceed_action")
enforce_on_key = lookup(each.value["rate_limit_options"], "enforce_on_key_configs") == null ? lookup(each.value["rate_limit_options"], "enforce_on_key", null) : ""
enforce_on_key_name = lookup(each.value["rate_limit_options"], "enforce_on_key_configs") == null ? lookup(each.value["rate_limit_options"], "enforce_on_key_name", null) : null
dynamic "enforce_on_key_configs" {
for_each = lookup(each.value["rate_limit_options"], "enforce_on_key_configs") == null ? {} : { for x in lookup(each.value["rate_limit_options"], "enforce_on_key_configs") : x.enforce_on_key_type => x }
content {
enforce_on_key_type = enforce_on_key_configs.value.enforce_on_key_type
enforce_on_key_name = enforce_on_key_configs.value.enforce_on_key_name
}
}
## Required for all rate limit options
dynamic "rate_limit_threshold" {
for_each = each.value["action"] == "rate_based_ban" || each.value["action"] == "throttle" ? ["rate_limit_options"] : []
content {
count = each.value["rate_limit_options"].rate_limit_http_request_count
interval_sec = each.value["rate_limit_options"].rate_limit_http_request_interval_sec
}
}
## Optional. Can be provided for for rate based ban. Not needed for throttle
dynamic "ban_threshold" {
for_each = each.value["action"] == "rate_based_ban" && lookup(each.value["rate_limit_options"], "ban_http_request_count", null) != null && lookup(each.value["rate_limit_options"], "ban_http_request_interval_sec", null) != null ? ["ban_threshold"] : []
content {
count = lookup(each.value["rate_limit_options"], "ban_http_request_count")
interval_sec = lookup(each.value["rate_limit_options"], "ban_http_request_interval_sec")
}
}
}
}
# Optional preconfigured_waf_config Block if preconfigured_waf_config_exclusion is provided
dynamic "preconfigured_waf_config" {
for_each = each.value.preconfigured_waf_config_exclusions == null ? [] : ["preconfigured_waf_config_exclusions"] #each.value.preconfigured_waf_config_exclusions
content {
dynamic "exclusion" {
for_each = each.value.preconfigured_waf_config_exclusions
content {
target_rule_set = exclusion.value.target_rule_set
target_rule_ids = exclusion.value.target_rule_ids
dynamic "request_header" {
for_each = exclusion.value.request_header == null ? {} : { for x in exclusion.value.request_header : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_header.value.operator
value = request_header.value.operator == "EQUALS_ANY" ? null : request_header.value.value
}
}
dynamic "request_cookie" {
for_each = exclusion.value.request_cookie == null ? {} : { for x in exclusion.value.request_cookie : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_cookie.value.operator
value = request_cookie.value.operator == "EQUALS_ANY" ? null : request_cookie.value.value
}
}
dynamic "request_uri" {
for_each = exclusion.value.request_uri == null ? {} : { for x in exclusion.value.request_uri : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_uri.value.operator
value = request_uri.value.operator == "EQUALS_ANY" ? null : request_uri.value.value
}
}
dynamic "request_query_param" {
for_each = exclusion.value.request_query_param == null ? {} : { for x in exclusion.value.request_query_param : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_query_param.value.operator
value = request_query_param.value.operator == "EQUALS_ANY" ? null : request_query_param.value.value
}
}
}
}
}
}
}
##### Preconfigured WAF Rules
resource "google_compute_region_security_policy_rule" "pre_configured_rules" {
provider = google-beta
for_each = var.pre_configured_rules
project = var.project_id
region = var.region
security_policy = google_compute_region_security_policy.security_policy.name
action = each.value["action"]
priority = each.value["priority"]
preview = each.value["preview"]
description = each.value["description"]
match {
expr {
expression = local.pre_configured_rules_expr[each.key].expression
}
}
### Rate limit. Execute only if Action is "rate_based_ban" or "throttle"
dynamic "rate_limit_options" {
for_each = each.value["action"] == "rate_based_ban" || each.value["action"] == "throttle" ? ["rate_limits"] : []
content {
conform_action = "allow"
ban_duration_sec = each.value["action"] == "rate_based_ban" ? lookup(each.value["rate_limit_options"], "ban_duration_sec") : null
exceed_action = lookup(each.value["rate_limit_options"], "exceed_action")
enforce_on_key = lookup(each.value["rate_limit_options"], "enforce_on_key_configs") == null ? lookup(each.value["rate_limit_options"], "enforce_on_key", null) : ""
enforce_on_key_name = lookup(each.value["rate_limit_options"], "enforce_on_key_configs") == null ? lookup(each.value["rate_limit_options"], "enforce_on_key_name", null) : null
dynamic "enforce_on_key_configs" {
for_each = lookup(each.value["rate_limit_options"], "enforce_on_key_configs") == null ? {} : { for x in lookup(each.value["rate_limit_options"], "enforce_on_key_configs") : x.enforce_on_key_type => x }
content {
enforce_on_key_type = enforce_on_key_configs.value.enforce_on_key_type
enforce_on_key_name = enforce_on_key_configs.value.enforce_on_key_name
}
}
## Required for all rate limit options
dynamic "rate_limit_threshold" {
for_each = each.value["action"] == "rate_based_ban" || each.value["action"] == "throttle" ? ["rate_limit_options"] : []
content {
count = each.value["rate_limit_options"].rate_limit_http_request_count
interval_sec = each.value["rate_limit_options"].rate_limit_http_request_interval_sec
}
}
## Optional. Can be provided for for rate based ban. Not needed for throttle
dynamic "ban_threshold" {
for_each = each.value["action"] == "rate_based_ban" && lookup(each.value["rate_limit_options"], "ban_http_request_count", null) != null && lookup(each.value["rate_limit_options"], "ban_http_request_interval_sec", null) != null ? ["ban_threshold"] : []
content {
count = lookup(each.value["rate_limit_options"], "ban_http_request_count")
interval_sec = lookup(each.value["rate_limit_options"], "ban_http_request_interval_sec")
}
}
}
}
# Optional preconfigured_waf_config Block if preconfigured_waf_config_exclusion is provided
dynamic "preconfigured_waf_config" {
for_each = each.value.preconfigured_waf_config_exclusions == null ? [] : ["preconfigured_waf_config_exclusions"] #each.value.preconfigured_waf_config_exclusions
content {
dynamic "exclusion" {
for_each = each.value.preconfigured_waf_config_exclusions
content {
target_rule_set = exclusion.value.target_rule_set
target_rule_ids = exclusion.value.target_rule_ids
dynamic "request_header" {
for_each = exclusion.value.request_header == null ? {} : { for x in exclusion.value.request_header : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_header.value.operator
value = request_header.value.operator == "EQUALS_ANY" ? null : request_header.value.value
}
}
dynamic "request_cookie" {
for_each = exclusion.value.request_cookie == null ? {} : { for x in exclusion.value.request_cookie : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_cookie.value.operator
value = request_cookie.value.operator == "EQUALS_ANY" ? null : request_cookie.value.value
}
}
dynamic "request_uri" {
for_each = exclusion.value.request_uri == null ? {} : { for x in exclusion.value.request_uri : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_uri.value.operator
value = request_uri.value.operator == "EQUALS_ANY" ? null : request_uri.value.value
}
}
dynamic "request_query_param" {
for_each = exclusion.value.request_query_param == null ? {} : { for x in exclusion.value.request_query_param : "${x.operator}-${base64encode(coalesce(x.value, "test"))}" => x }
content {
operator = request_query_param.value.operator
value = request_query_param.value.operator == "EQUALS_ANY" ? null : request_query_param.value.value
}
}
}
}
}
}
}
##### Default Rule
resource "google_compute_region_security_policy_rule" "default_rule" {
provider = google-beta
region = var.region
project = var.project_id
security_policy = google_compute_region_security_policy.security_policy.name
description = "default rule"
action = var.default_rule_action
priority = "2147483647"
match {
versioned_expr = "SRC_IPS_V1"
config {
src_ip_ranges = ["*"]
}
}
}