diff --git a/google_fastly_waf/README.md b/google_fastly_waf/README.md index 9da31bb5..acc93266 100644 --- a/google_fastly_waf/README.md +++ b/google_fastly_waf/README.md @@ -148,8 +148,10 @@ module "fastly_stage" { | [cache\_settings](#input\_cache\_settings) | List of cache settings for the Fastly service. |
list(object({
name = string
action = optional(string)
cache_condition = optional(string)
stale_ttl = optional(number)
ttl = optional(number)
})) | `[]` | no |
| [conditions](#input\_conditions) | List of Fastly conditions to create (REQUEST, RESPONSE or CACHE). | list(object({
name = string # required, unique
statement = string # VCL conditional expression
type = string # one of: REQUEST, RESPONSE, CACHE
priority = optional(number) # lower runs first, default 10
})) | `[]` | no |
| [ddos\_protection](#input\_ddos\_protection) | Optional DDoS Protection configuration for the Fastly service product enablement. | object({
enabled = bool
mode = string
}) | `null` | no |
+| [ddos\_protection\_alert](#input\_ddos\_protection\_alert) | Optional Slack alerting for Fastly DDoS Protection. When set, the module creates a Slack `fastly_integration` and a `fastly_alert` on the `ddos_protection_requests_detect_count` stats metric that notifies the channel behind the webhook. Intended to be paired with `ddos_protection` being enabled. Set to `null` (the default) to create no alerting resources. | object({
enabled = optional(bool, true)
slack_webhook_secret = string
threshold = optional(number, 1)
period = optional(string, "5m")
}) | `null` | no |
| [domains](#input\_domains) | A list of domains | `list(any)` | `[]` | no |
| [https\_redirect\_enabled](#input\_https\_redirect\_enabled) | n/a | `bool` | `true` | no |
+| [legacy\_edge\_deployment](#input\_legacy\_edge\_deployment) | If true (default), deploy NGWAF via the legacy sigsci EdgeDeployment APIs and Fastly dynamic snippets. If false, deploy via Fastly's product\_enablement ngwaf block. Default preserves behavior for services still on the legacy method. | `bool` | `true` | no |
| [log\_sampling\_enabled](#input\_log\_sampling\_enabled) | n/a | `bool` | `false` | no |
| [log\_sampling\_percent](#input\_log\_sampling\_percent) | n/a | `string` | `"10"` | no |
| [ngwaf\_agent\_level](#input\_ngwaf\_agent\_level) | This is the site wide blocking level | `string` | `"log"` | no |
diff --git a/google_fastly_waf/alerts.tf b/google_fastly_waf/alerts.tf
new file mode 100644
index 00000000..52e0a726
--- /dev/null
+++ b/google_fastly_waf/alerts.tf
@@ -0,0 +1,40 @@
+# DDoS Protection alerting
+# When var.ddos_protection_alert is set, create a Slack integration and a
+# Fastly stats alert that fires on the `ddos_protection_requests_detect_count`
+# metric (requests classified as DDoS attacks). This is only meaningful when
+# ddos_protection is enabled (mode "log" or "block").
+
+locals {
+ ddos_protection_alert_enabled = (
+ var.ddos_protection_alert != null && var.ddos_protection_alert.enabled
+ )
+}
+
+resource "fastly_integration" "ddos_protection_slack" {
+ count = local.ddos_protection_alert_enabled ? 1 : 0
+
+ name = "${var.application}-${var.realm}-${var.environment} DDoS Protection Slack integration"
+ description = "Slack notifications for Fastly DDoS Protection detection events"
+ type = "slack"
+
+ config = {
+ webhook = sensitive(var.ddos_protection_alert.slack_webhook_secret)
+ }
+}
+
+resource "fastly_alert" "ddos_protection" {
+ count = local.ddos_protection_alert_enabled ? 1 : 0
+
+ name = "${var.application}-${var.realm}-${var.environment} DDoS Protection events"
+ service_id = fastly_service_vcl.default.id
+ source = "stats"
+ metric = "ddos_protection_requests_detect_count"
+
+ evaluation_strategy {
+ type = "above_threshold"
+ period = var.ddos_protection_alert.period
+ threshold = var.ddos_protection_alert.threshold
+ }
+
+ integration_ids = [fastly_integration.ddos_protection_slack[0].id]
+}
diff --git a/google_fastly_waf/variables.tf b/google_fastly_waf/variables.tf
index 725d095e..43290cf6 100644
--- a/google_fastly_waf/variables.tf
+++ b/google_fastly_waf/variables.tf
@@ -143,6 +143,41 @@ variable "ddos_protection" {
}
}
+variable "ddos_protection_alert" {
+ description = <<-EOT
+ Optional Slack alerting for Fastly DDoS Protection. When set, the module
+ creates a Slack `fastly_integration` and a `fastly_alert` on the
+ `ddos_protection_requests_detect_count` stats metric that notifies the
+ channel behind the webhook. Intended to be paired with `ddos_protection`
+ being enabled.
+ Set to `null` (the default) to create no alerting resources.
+ EOT
+ type = object({
+ enabled = optional(bool, true) # set false to keep config but tear the alert down
+ slack_webhook_secret = string # Slack incoming-webhook URL (sensitive)
+ threshold = optional(number, 1) # ddos_protection_requests_detect_count that fires the alert
+ period = optional(string, "5m") # evaluation window: 2m, 3m, 5m, 15m, or 30m
+ })
+ default = null
+ validation {
+ condition = var.ddos_protection_alert == null || contains(
+ ["2m", "3m", "5m", "15m", "30m"], var.ddos_protection_alert.period
+ )
+ error_message = "ddos_protection_alert.period must be one of: 2m, 3m, 5m, 15m, or 30m."
+ }
+ validation {
+ # The detect-count metric is only populated when DDoS Protection is actively
+ # inspecting traffic, so alerting requires ddos_protection enabled in log or
+ # block mode. (Cross-variable validation requires Terraform >= 1.9.)
+ condition = var.ddos_protection_alert == null || (
+ var.ddos_protection != null &&
+ var.ddos_protection.enabled &&
+ var.ddos_protection.mode != "off"
+ )
+ error_message = "ddos_protection_alert requires ddos_protection to be enabled with mode \"log\" or \"block\"."
+ }
+}
+
## NGWAF
variable "legacy_edge_deployment" {
type = bool
diff --git a/google_fastly_waf/versions.tf b/google_fastly_waf/versions.tf
index b7721a65..5c90a7fb 100644
--- a/google_fastly_waf/versions.tf
+++ b/google_fastly_waf/versions.tf
@@ -1,4 +1,8 @@
terraform {
+ # >= 1.9 is required for cross-variable references in validation blocks
+ # (see ddos_protection_alert in variables.tf).
+ required_version = ">= 1.9"
+
required_providers {
fastly = {
source = "fastly/fastly"