diff --git a/edge-terraform-router/modules/service-vcl/provider.tf b/edge-terraform-router/modules/service-vcl/provider.tf new file mode 100644 index 0000000..746219f --- /dev/null +++ b/edge-terraform-router/modules/service-vcl/provider.tf @@ -0,0 +1,10 @@ +# Terraform 0.13+ requires providers to be declared in a "required_providers" block +# https://registry.terraform.io/providers/fastly/fastly/latest/docs +terraform { + required_providers { + fastly = { + source = "fastly/fastly" + version = ">= 8.8.0" + } + } +} diff --git a/edge-terraform-router/modules/service-vcl/variables.tf b/edge-terraform-router/modules/service-vcl/variables.tf new file mode 100644 index 0000000..a4611c5 --- /dev/null +++ b/edge-terraform-router/modules/service-vcl/variables.tf @@ -0,0 +1,8 @@ +variable "fastly_routes" { + description = "A list of objects representing the domain and corresponding origin names." + type = list(object({ + domain_name = string + origin_name = string + })) + default = [] +} \ No newline at end of file diff --git a/edge-terraform-router/modules/service-vcl/vcl-service.tf b/edge-terraform-router/modules/service-vcl/vcl-service.tf new file mode 100644 index 0000000..c2f3be7 --- /dev/null +++ b/edge-terraform-router/modules/service-vcl/vcl-service.tf @@ -0,0 +1,46 @@ +resource "fastly_service_vcl" "edge-terraform-router-demo" { + name = "edge-terraform-router-demo" + + # Iterates over the array to create multiple domain blocks + dynamic "domain" { + for_each = var.fastly_routes + content { + name = domain.value.domain_name + comment = "Managed by Terraform" + } + } + + # Iterates over the array to create corresponding backends + dynamic "backend" { + for_each = var.fastly_routes + content { + address = backend.value.origin_name + # Creates a unique backend name based on the domain (e.g., "backend-example-com") + name = "backend-${replace(backend.value.domain_name, ".", "-")}" + shield = "iad-va-us" + port = 443 + use_ssl = true + ssl_cert_hostname = backend.value.origin_name + ssl_sni_hostname = backend.value.origin_name + override_host = backend.value.origin_name + + # Links this backend to the unique request condition below + request_condition = "condition-${replace(backend.value.domain_name, ".", "-")}" + } + } + + # Iterates over the array to create routing conditions + dynamic "condition" { + for_each = var.fastly_routes + content { + # Matches the name defined in the backend block + name = "condition-${replace(condition.value.domain_name, ".", "-")}" + priority = 10 + # Evaluates the incoming request host against the domain variable + statement = "std.tolower(req.http.host) == \"${condition.value.domain_name}\"" + type = "REQUEST" + } + } + + force_destroy = true +} \ No newline at end of file diff --git a/edge-terraform-router/prod-delivery/main.tf b/edge-terraform-router/prod-delivery/main.tf new file mode 100644 index 0000000..260110f --- /dev/null +++ b/edge-terraform-router/prod-delivery/main.tf @@ -0,0 +1,8 @@ +module "service_vcl" { + source = "../modules/service-vcl" + fastly_routes = var.fastly_routes + + providers = { + fastly = fastly + } +} diff --git a/edge-terraform-router/prod-delivery/provider.tf b/edge-terraform-router/prod-delivery/provider.tf new file mode 100644 index 0000000..746219f --- /dev/null +++ b/edge-terraform-router/prod-delivery/provider.tf @@ -0,0 +1,10 @@ +# Terraform 0.13+ requires providers to be declared in a "required_providers" block +# https://registry.terraform.io/providers/fastly/fastly/latest/docs +terraform { + required_providers { + fastly = { + source = "fastly/fastly" + version = ">= 8.8.0" + } + } +} diff --git a/edge-terraform-router/prod-delivery/variables.tf b/edge-terraform-router/prod-delivery/variables.tf new file mode 100644 index 0000000..a4611c5 --- /dev/null +++ b/edge-terraform-router/prod-delivery/variables.tf @@ -0,0 +1,8 @@ +variable "fastly_routes" { + description = "A list of objects representing the domain and corresponding origin names." + type = list(object({ + domain_name = string + origin_name = string + })) + default = [] +} \ No newline at end of file diff --git a/edge-terraform-starter/README.md b/edge-terraform-starter/README.md new file mode 100644 index 0000000..d3a92e7 --- /dev/null +++ b/edge-terraform-starter/README.md @@ -0,0 +1,10 @@ +# Copy this starter to get up and running with terraform and Fastly + +# What's in this repo? +* Per environment directory for WAF security settings +* Per environment directory for Delivery settings +* An example directory that uses a for loop to create multiple delivery services. + +The implementation follows the guidance from the following documentation. +[multiple-environments](https://www.fastly.com/documentation/guides/integrations/orchestration/terraform/#multiple-environments) + diff --git a/edge-terraform-starter/dev-delivery/inputs.auto.tfvars b/edge-terraform-starter/dev-delivery/inputs.auto.tfvars new file mode 100644 index 0000000..c47a84c --- /dev/null +++ b/edge-terraform-starter/dev-delivery/inputs.auto.tfvars @@ -0,0 +1,6 @@ +#### Service VCL variables +## Free TLS with "*.global.ssl.fastly.net" +SERVICE_VCL_FRONTEND_DOMAIN_NAME = "dev-tf-demo.global.ssl.fastly.net" +SERVICE_VCL_BACKEND_HOSTNAME = "http-me.edgecompute.app" + +NGWAF_SITE = "dev_tf_ngwaf_site" diff --git a/edge-terraform-starter/dev-delivery/main.tf b/edge-terraform-starter/dev-delivery/main.tf new file mode 100644 index 0000000..73a894c --- /dev/null +++ b/edge-terraform-starter/dev-delivery/main.tf @@ -0,0 +1,25 @@ +provider "sigsci" { + corp = var.NGWAF_CORP + email = var.NGWAF_EMAIL + auth_token = var.NGWAF_TOKEN +} + +provider "fastly" { + api_key = var.FASTLY_API_KEY +} + +module "service_vcl" { + source = "../modules/service-vcl" + FASTLY_API_KEY = var.FASTLY_API_KEY + SERVICE_VCL_FRONTEND_DOMAIN_NAME = var.SERVICE_VCL_FRONTEND_DOMAIN_NAME + SERVICE_VCL_BACKEND_HOSTNAME = var.SERVICE_VCL_BACKEND_HOSTNAME + NGWAF_SITE = var.NGWAF_SITE + providers = { + sigsci = sigsci + fastly = fastly + } +} + +output "live_waf_love" { + value = module.service_vcl.vcl_service_output +} diff --git a/edge-terraform-starter/dev-delivery/providers.tf b/edge-terraform-starter/dev-delivery/providers.tf new file mode 100644 index 0000000..f7b264f --- /dev/null +++ b/edge-terraform-starter/dev-delivery/providers.tf @@ -0,0 +1,12 @@ +terraform { + required_providers { + fastly = { + source = "fastly/fastly" + version = ">= 5.7.0" + } + sigsci = { + source = "signalsciences/sigsci" + version = ">= 2.1.0" + } + } +} diff --git a/edge-terraform-starter/dev-delivery/variables.tf b/edge-terraform-starter/dev-delivery/variables.tf new file mode 100644 index 0000000..a326b00 --- /dev/null +++ b/edge-terraform-starter/dev-delivery/variables.tf @@ -0,0 +1,40 @@ +# Fastly Edge VCL configuration +variable "FASTLY_API_KEY" { + type = string + description = "This is API key for the Fastly VCL edge configuration." +} + +#### VCL Service variables - Start +variable "SERVICE_VCL_FRONTEND_DOMAIN_NAME" { + type = string + description = "Frontend domain for your service." +} + +variable "SERVICE_VCL_BACKEND_HOSTNAME" { + type = string + description = "hostname used for backend." + default = "http-me.edgecompute.app" +} + +#### VCL Service variables - End + +#### NGWAF Vars - Start +variable "NGWAF_CORP" { + type = string + description = "NGWAF Corp where configuration changes will be made." +} +variable "NGWAF_SITE" { + type = string + description = "NGWAF Site where configuration changes will be made." +} +variable "NGWAF_EMAIL" { + type = string + description = "Email address associated with the token for the NGWAF API." +} +variable "NGWAF_TOKEN" { + type = string + description = "Secret token for the NGWAF API." + sensitive = true +} + +#### NGWAF Vars - End diff --git a/edge-terraform-starter/dev-security/inputs.auto.tfvars b/edge-terraform-starter/dev-security/inputs.auto.tfvars new file mode 100644 index 0000000..fd8cdfb --- /dev/null +++ b/edge-terraform-starter/dev-security/inputs.auto.tfvars @@ -0,0 +1,3 @@ +#### NGWAF Variable input values + +NGWAF_SITE = "dev_tf_ngwaf_site" diff --git a/edge-terraform-starter/dev-security/main.tf b/edge-terraform-starter/dev-security/main.tf new file mode 100644 index 0000000..09f88ea --- /dev/null +++ b/edge-terraform-starter/dev-security/main.tf @@ -0,0 +1,84 @@ +# Try to follow recommended practices here, https://developer.hashicorp.com/terraform/cloud-docs/recommended-practices + +#### Supply NGWAF API authentication - Start +# environment variables must be available using "TF_VAR_*" in your terminal. +# For example, `echo $TF_VAR_NGWAF_CORP` should return your intended corp. +provider "sigsci" { + corp = var.NGWAF_CORP + email = var.NGWAF_EMAIL + auth_token = var.NGWAF_TOKEN +} +#### Supply NGWAF API authentication - End + +module "edge_security" { + source = "../modules/edge-security" + NGWAF_EMAIL = var.NGWAF_EMAIL + NGWAF_TOKEN = var.NGWAF_TOKEN + NGWAF_CORP = var.NGWAF_CORP + NGWAF_SITE = var.NGWAF_SITE +} + +#### Rate Limiting Enumeration Attempts - Start +resource "sigsci_site_signal_tag" "bad-response-signal" { + # site_short_name = var.NGWAF_SITE + site_short_name = module.edge_security.sigsci_site.ngwaf_workspace_site.short_name + name = "bad-response" + description = "Identification of attacks from malicious IPs" +} + +resource "sigsci_site_rule" "enumeration-attack-rule" { + site_short_name = module.edge_security.sigsci_site.ngwaf_workspace_site.short_name + type = "rateLimit" + group_operator = "any" + enabled = true + reason = "Blocking IPs that have too many bad responses. Likely an enumeration attack." + expiration = "" + + conditions { + type = "single" + field = "responseCode" + operator = "like" + value = "4[0-9][0-9]" + } + conditions { + type = "single" + field = "responseCode" + operator = "like" + value = "5[0-9][0-9]" + } + # actions { + # type = "blockSignal" + # signal = "ALL-REQUESTS" + # response_code = 406 + # } + + actions { + type = "logRequest" + signal = sigsci_site_signal_tag.bad-response-signal.id + } + + rate_limit = { + threshold = 10, + interval = 1, + duration = 600, + # clientIdentifiers = "ip" Defaults to IP + } + signal = sigsci_site_signal_tag.bad-response-signal.id + + depends_on = [ + sigsci_site_signal_tag.bad-response-signal, + ] +} + +#### Rate Limiting Enumeration Attempts - End + + +output "live_waf_love_output" { + value = < d if d.name == "Edge_Security" + } + service_id = fastly_service_vcl.frontend_vcl_service.id + dictionary_id = each.value.dictionary_id + items = { + Enabled : "100" + } +} + +resource "fastly_service_dynamic_snippet_content" "ngwaf_config_init" { + for_each = { + for d in fastly_service_vcl.frontend_vcl_service.dynamicsnippet : d.name => d if d.name == "ngwaf_config_init" + } + + service_id = fastly_service_vcl.frontend_vcl_service.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.frontend_vcl_service.dynamicsnippet : d.name => d if d.name == "ngwaf_config_miss" + } + + service_id = fastly_service_vcl.frontend_vcl_service.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.frontend_vcl_service.dynamicsnippet : d.name => d if d.name == "ngwaf_config_pass" + } + + service_id = fastly_service_vcl.frontend_vcl_service.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.frontend_vcl_service.dynamicsnippet : d.name => d if d.name == "ngwaf_config_deliver" + } + + service_id = fastly_service_vcl.frontend_vcl_service.id + snippet_id = each.value.snippet_id + + content = "### Fastly managed ngwaf_config_deliver" + + manage_snippets = false +} + +#### Fastly VCL Service - End + +#### Edge deploy and sync - Start + +# provider "sigsci" { +# corp = var.NGWAF_CORP +# email = var.NGWAF_EMAIL +# auth_token = var.NGWAF_TOKEN +# fastly_api_key = var.FASTLY_API_KEY +# } + +# resource "sigsci_edge_deployment" "ngwaf_edge_site_service" { +# # https://registry.terraform.io/providers/signalsciences/sigsci/latest/docs/resources/edge_deployment +# site_short_name = var.NGWAF_SITE +# } + +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 = var.NGWAF_SITE + fastly_sid = fastly_service_vcl.frontend_vcl_service.id + + activate_version = true + percent_enabled = 100 + + depends_on = [ + fastly_service_vcl.frontend_vcl_service, + fastly_service_dictionary_items.edge_security_dictionary_items, + 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, + ] +} + +#### Edge deploy and sync - End + +output "vcl_service_output" { + value = <