google_fastly_waf/main.tf (209 lines of code) (raw):

locals { domains = setunion(var.domains, var.subscription_domains) } resource "fastly_service_vcl" "default" { name = "${var.application}-${var.realm}-${var.environment}" product_enablement { brotli_compression = true bot_management = true } gzip { name = "${var.application}-${var.realm}-${var.environment}-compression" content_types = [ "text/html", "application/x-javascript", "text/css", "application/javascript", "text/javascript", "application/json", "application/vnd.ms-fontobject", "application/x-font-opentype", "application/x-font-truetype", "application/x-font-ttf", "application/xml", "font/eot", "font/opentype", "font/otf", "image/svg+xml", "image/vnd.microsoft.icon", "text/plain", "text/xml" ] extensions = ["css", "js", "html", "eot", "ico", "otf", "ttf", "json", "svg"] } dynamic "domain" { for_each = local.domains content { name = domain.value.name comment = lookup(domain.value, "comment", null) } } dynamic "backend" { # See https://www.fastly.com/documentation/reference/api/services/backend/ # for field reference. for_each = var.backends content { name = backend.value.name address = backend.value.address connect_timeout = lookup(backend.value, "connect_timeout", 10000) first_byte_timeout = lookup(backend.value, "first_byte_timeout", 60000) keepalive_time = lookup(backend.value, "keepalive_time", 5) override_host = lookup(backend.value, "override_host", "") port = lookup(backend.value, "port", 443) request_condition = lookup(backend.value, "request_condition", "False") shield = lookup(backend.value, "shield", "") ssl_sni_hostname = lookup(backend.value, "ssl_sni_hostname", "") use_ssl = lookup(backend.value, "use_ssl", false) # health check is a string, created in another block healthcheck = lookup(backend.value, "health_check_name", "") # If use_ssl is true, then we force ssl_check_cert as a good security # practice. When ssl_check_cert is true, Fastly requires that # ssl_cert_hostname to be set. We don't use "lookup" here because we # want to fail early if ssl_cert_hostname is not given a value. If we # don't fail early, "terraform apply" will fail when it calls Fastly API. ssl_check_cert = lookup(backend.value, "use_ssl", false) ? true : false ssl_cert_hostname = lookup(backend.value, "use_ssl", false) ? backend.value.ssl_cert_hostname : "" } } # Allow passing in arbitrary snippets for VCL configuration dynamic "snippet" { for_each = var.snippets content { content = snippet.value.content name = snippet.value.name type = snippet.value.type priority = lookup(snippet.value, "priority", "") } } # https://www.fastly.com/documentation/solutions/tutorials/next-gen-waf-edge-integration/ #### NGWAF Dynamic Snippets and dictionary - MANAGED BY FASTLY - Start dynamicsnippet { name = "ngwaf_config_init" type = "init" priority = 0 } dynamicsnippet { name = "ngwaf_config_miss" type = "miss" priority = 9000 } dynamicsnippet { name = "ngwaf_config_pass" type = "pass" priority = 9000 } dynamicsnippet { name = "ngwaf_config_deliver" type = "deliver" priority = 9000 } # percentage of traffic going through WAF # usually 100%, this is called later in the file to set # how much traffic to send to the WAF dictionary { name = "Edge_Security" } #### NGWAF Dynamic Snippets and dictionary - MANAGED BY FASTLY - End dynamic "healthcheck" { # only enable the health on endpoints that need healthcheck enabled for_each = { for healthcheck, values in var.backends : healthcheck => values if lookup(values, "health_check_enabled", false) } content { name = lookup(healthcheck.value, "health_check_name", "") host = healthcheck.value.address path = lookup(healthcheck.value, "health_check_path", "") method = lookup(healthcheck.value, "health_check_method", "") check_interval = 50000 } } condition { name = "False" statement = "false" type = "REQUEST" } dynamic "condition" { for_each = var.conditions content { name = condition.value.name statement = condition.value.statement type = condition.value.type } } vcl { name = "main" content = templatefile("${path.module}/vcl/main.vcl.tftpl", { realm = var.realm, environment = var.environment } ) main = true } default_ttl = 0 logging_bigquery { dataset = google_bigquery_dataset.fastly.dataset_id name = "bigquery-default" project_id = var.project_id table = google_bigquery_table.fastly.table_id account_name = google_service_account.log_uploader.account_id format = file("${path.module}/logging/bq_format.txt") } logging_gcs { bucket_name = google_storage_bucket.fastly.name name = "gcs-default" project_id = var.project_id account_name = google_service_account.log_uploader.account_id gzip_level = 9 period = 300 # 5 minutes } } #### NGWAF Dynamic Snippets and dictionary - MANAGED BY FASTLY - Start resource "fastly_service_dynamic_snippet_content" "ngwaf_config_init" { for_each = { for d in fastly_service_vcl.default.dynamicsnippet : d.name => d if d.name == "ngwaf_config_init" } service_id = fastly_service_vcl.default.id snippet_id = each.value.snippet_id content = "### Fastly managed ngwaf_config_init" manage_snippets = false } resource "fastly_service_dynamic_snippet_content" "ngwaf_config_miss" { for_each = { for d in fastly_service_vcl.default.dynamicsnippet : d.name => d if d.name == "ngwaf_config_miss" } service_id = fastly_service_vcl.default.id snippet_id = each.value.snippet_id content = "### Fastly managed ngwaf_config_miss" manage_snippets = false } resource "fastly_service_dynamic_snippet_content" "ngwaf_config_pass" { for_each = { for d in fastly_service_vcl.default.dynamicsnippet : d.name => d if d.name == "ngwaf_config_pass" } service_id = fastly_service_vcl.default.id snippet_id = each.value.snippet_id content = "### Fastly managed ngwaf_config_pass" manage_snippets = false } resource "fastly_service_dynamic_snippet_content" "ngwaf_config_deliver" { for_each = { for d in fastly_service_vcl.default.dynamicsnippet : d.name => d if d.name == "ngwaf_config_deliver" } service_id = fastly_service_vcl.default.id snippet_id = each.value.snippet_id content = "### Fastly managed ngwaf_config_deliver" manage_snippets = false } #### NGWAF Dynamic Snippets - MANAGED BY FASTLY - End resource "sigsci_edge_deployment" "ngwaf_edge_site_service" { # https://registry.terraform.io/providers/signalsciences/sigsci/latest/docs/resources/edge_deployment site_short_name = sigsci_site.ngwaf_edge_site.short_name } resource "sigsci_edge_deployment_service" "ngwaf_edge_service_link" { # https://registry.terraform.io/providers/signalsciences/sigsci/latest/docs/resources/edge_deployment_service site_short_name = sigsci_site.ngwaf_edge_site.short_name fastly_sid = fastly_service_vcl.default.id activate_version = true percent_enabled = 100 depends_on = [ sigsci_edge_deployment.ngwaf_edge_site_service, fastly_service_vcl.default, fastly_service_dynamic_snippet_content.ngwaf_config_init, fastly_service_dynamic_snippet_content.ngwaf_config_miss, fastly_service_dynamic_snippet_content.ngwaf_config_pass, fastly_service_dynamic_snippet_content.ngwaf_config_deliver, ] } # This creates the actual WAF object resource "sigsci_site" "ngwaf_edge_site" { short_name = "${var.application}-${var.realm}-${var.environment}" display_name = "${var.application}-${var.realm}-${var.environment}" block_duration_seconds = 86400 agent_anon_mode = "" agent_level = var.ngwaf_agent_level # this setting dictates blocking mode immediate_block = var.ngwaf_immediate_block } resource "sigsci_edge_deployment_service_backend" "ngwaf_edge_service_backend_sync" { site_short_name = sigsci_site.ngwaf_edge_site.short_name fastly_sid = fastly_service_vcl.default.id fastly_service_vcl_active_version = fastly_service_vcl.default.active_version depends_on = [ sigsci_edge_deployment_service.ngwaf_edge_service_link, ] }