Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add ngwaf #2527

Merged
merged 15 commits into from
Sep 16, 2024
22 changes: 22 additions & 0 deletions infra/.terraform.lock.hcl

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

28 changes: 26 additions & 2 deletions infra/cdn/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,29 @@ N/A
## Requirements

Tested on
- Tested on Terraform 1.8.5
- Fastly provider 5.13.0
- Tested on Terraform 1.9.5
- Fastly provider 5.13.0

# Fastly's NGWAF

This module also conditionally can set up the Fastly Next-Gen Web Application Firewall (NGWAF)
for our Fastly services related to python.org / test.python.org.

## Usage

```hcl
module "fastly_production" {
source = "./cdn"

...
activate_ngwaf_service = true
...
}
```

## Requirements

Tested on
- Terraform 1.9.5
- Fastly provider 5.13.0
- SigSci provider 3.3.0
69 changes: 69 additions & 0 deletions infra/cdn/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -342,4 +342,73 @@ resource "fastly_service_vcl" "python_org" {
response = "Forbidden"
status = 403
}

dynamic "dictionary" {
for_each = var.activate_ngwaf_service ? [1] : []
content {
name = var.edge_security_dictionary
}
}

dynamic "dynamicsnippet" {
for_each = var.activate_ngwaf_service ? [1] : []
content {
name = "ngwaf_config_init"
type = "init"
priority = 0
}
}

dynamic "dynamicsnippet" {
for_each = var.activate_ngwaf_service ? [1] : []
content {
name = "ngwaf_config_miss"
type = "miss"
priority = 9000
}
}

dynamic "dynamicsnippet" {
for_each = var.activate_ngwaf_service ? [1] : []
content {
name = "ngwaf_config_pass"
type = "pass"
priority = 9000
}
}

dynamic "dynamicsnippet" {
for_each = var.activate_ngwaf_service ? [1] : []
content {
name = "ngwaf_config_deliver"
type = "deliver"
priority = 9000
}
}

lifecycle {
ignore_changes = [
product_enablement,
]
}
}

output "service_id" {
value = fastly_service_vcl.python_org.id
description = "The ID of the Fastly service"
}

output "backend_address" {
value = var.backend_address
description = "The backend address for the service."
}

output "service_name" {
value = var.name
description = "The name of the Fastly service"
}

output "domain" {
value = var.domain
description = "The domain of the Fastly service"
}
49 changes: 49 additions & 0 deletions infra/cdn/ngwaf.tf
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
resource "fastly_service_dictionary_items" "edge_security_dictionary_items" {
count = var.activate_ngwaf_service ? 1 : 0
service_id = fastly_service_vcl.python_org.id
dictionary_id = one([for d in fastly_service_vcl.python_org.dictionary : d.dictionary_id if d.name == var.edge_security_dictionary])
items = {
Enabled : "100"
}
}

resource "fastly_service_dynamic_snippet_content" "ngwaf_config_snippets" {
for_each = var.activate_ngwaf_service ? toset(["init", "miss", "pass", "deliver"]) : []
service_id = fastly_service_vcl.python_org.id
snippet_id = one([for d in fastly_service_vcl.python_org.dynamicsnippet : d.snippet_id if d.name == "ngwaf_config_${each.key}"])
content = "### Terraform managed ngwaf_config_${each.key}"
manage_snippets = false
}

# NGWAF Edge Deployment on SignalSciences.net
resource "sigsci_edge_deployment" "ngwaf_edge_site_service" {
count = var.activate_ngwaf_service ? 1 : 0
provider = sigsci.firewall
site_short_name = var.ngwaf_site_name
}

resource "sigsci_edge_deployment_service" "ngwaf_edge_service_link" {
count = var.activate_ngwaf_service ? 1 : 0
provider = sigsci.firewall
site_short_name = var.ngwaf_site_name
fastly_sid = fastly_service_vcl.python_org.id
activate_version = var.activate_ngwaf_service
percent_enabled = 100
depends_on = [
sigsci_edge_deployment.ngwaf_edge_site_service,
fastly_service_vcl.python_org,
fastly_service_dictionary_items.edge_security_dictionary_items,
fastly_service_dynamic_snippet_content.ngwaf_config_snippets,
]
}

resource "sigsci_edge_deployment_service_backend" "ngwaf_edge_service_backend_sync" {
count = var.activate_ngwaf_service ? 1 : 0
provider = sigsci.firewall
site_short_name = var.ngwaf_site_name
fastly_sid = fastly_service_vcl.python_org.id
fastly_service_vcl_active_version = fastly_service_vcl.python_org.active_version
depends_on = [
sigsci_edge_deployment_service.ngwaf_edge_service_link,
]
}
8 changes: 8 additions & 0 deletions infra/cdn/providers.tf
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,11 @@ provider "fastly" {
alias = "cdn"
api_key = var.fastly_key
}

provider "sigsci" {
alias = "firewall"
corp = var.ngwaf_corp_name
email = var.ngwaf_email
auth_token = var.ngwaf_token
fastly_api_key = var.fastly_key
}
36 changes: 35 additions & 1 deletion infra/cdn/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,38 @@ variable "backend_address" {
variable "default_ttl" {
type = number
description = "The default TTL for the service."
}
}

## NGWAF
variable "activate_ngwaf_service" {
type = bool
description = "Whether to activate the NGWAF service."
}
variable "edge_security_dictionary" {
type = string
description = "The dictionary name for the Edge Security product."
default = ""
}
variable "ngwaf_corp_name" {
type = string
description = "Corp name for NGWAF"
default = "python"
}
variable "ngwaf_site_name" {
type = string
description = "Site SHORT name for NGWAF"

validation {
condition = can(regex("^(test|stage|prod)$", var.ngwaf_site_name))
error_message = "'ngwaf_site_name' must be one of the following: test, stage, or prod"
}
}
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
}
4 changes: 4 additions & 0 deletions infra/cdn/versions.tf
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,9 @@ terraform {
source = "fastly/fastly"
version = "5.13.0"
}
sigsci = {
source = "signalsciences/sigsci"
version = "3.3.0"
}
}
}
18 changes: 14 additions & 4 deletions infra/main.tf
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,20 @@ module "fastly_production" {
fastly_key = var.FASTLY_API_KEY
fastly_header_token = var.FASTLY_HEADER_TOKEN
s3_logging_keys = var.fastly_s3_logging

ngwaf_site_name = "prod"
ngwaf_email = "infrastructure-staff@python.org"
ngwaf_token = var.ngwaf_token
activate_ngwaf_service = false
}

module "fastly_staging" {
source = "./cdn"

name = "test.python.org"
domain = "test.python.org"
subdomain = "www.test.python.org"
extra_domains = ["www.test.python.org"]
name = "test.python.org"
domain = "test.python.org"
subdomain = "www.test.python.org"
extra_domains = ["www.test.python.org"]
# TODO: adjust to test-pythondotorg when done testing NGWAF
backend_address = "pythondotorg.ingress.us-east-2.psfhosted.computer"
default_ttl = 3600
Expand All @@ -29,4 +34,9 @@ module "fastly_staging" {
fastly_key = var.FASTLY_API_KEY
fastly_header_token = var.FASTLY_HEADER_TOKEN
s3_logging_keys = var.fastly_s3_logging

ngwaf_site_name = "test"
ngwaf_email = "infrastructure-staff@python.org"
ngwaf_token = var.ngwaf_token
activate_ngwaf_service = true
}
7 changes: 6 additions & 1 deletion infra/variables.tf
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,9 @@ variable "fastly_s3_logging" {
type = map(string)
description = "S3 bucket keys for Fastly logging"
sensitive = true
}
}
variable "ngwaf_token" {
type = string
description = "Secret token for the NGWAF API."
sensitive = true
}