From 2a0e24b6087f754ead20a2291081395bc709bcfe Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Wed, 30 Nov 2022 16:37:36 +0000 Subject: [PATCH 01/13] feat(terraform): create front door and origin group --- terraform/front_door.tf | 51 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 terraform/front_door.tf diff --git a/terraform/front_door.tf b/terraform/front_door.tf new file mode 100644 index 00000000..7e3d56c4 --- /dev/null +++ b/terraform/front_door.tf @@ -0,0 +1,51 @@ +locals { + front_door_origin_group_name = azurerm_linux_web_app.main.name + front_door_origin_name = azurerm_linux_web_app.main.name + front_door_route_name = azurerm_linux_web_app.main.name +} + +resource "azurerm_cdn_frontdoor_profile" "main" { + name = "mst-courtesy-cards-${local.env_name}" + resource_group_name = data.azurerm_resource_group.main.name + sku_name = "Standard_AzureFrontDoor" +} + +resource "azurerm_cdn_frontdoor_endpoint" "main" { + name = "frontdoor-endpoint-${local.env_name}" + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id +} + +resource "azurerm_cdn_frontdoor_origin_group" "main" { + name = local.front_door_origin_group_name + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id + session_affinity_enabled = true + + load_balancing {} +} + +resource "azurerm_cdn_frontdoor_origin" "main" { + name = local.front_door_origin_name + cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.main.id + + enabled = true + host_name = azurerm_linux_web_app.main.default_hostname + http_port = 80 + https_port = 443 + origin_host_header = azurerm_linux_web_app.main.default_hostname + certificate_name_check_enabled = true + priority = 1 + weight = 1000 +} + +resource "azurerm_cdn_frontdoor_route" "main" { + name = local.front_door_route_name + cdn_frontdoor_endpoint_id = azurerm_cdn_frontdoor_endpoint.main.id + cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.main.id + cdn_frontdoor_origin_ids = [azurerm_cdn_frontdoor_origin.main.id] + + https_redirect_enabled = true + supported_protocols = ["Http", "Https"] + patterns_to_match = ["/*"] + forwarding_protocol = "HttpsOnly" + link_to_default_domain = true +} From c4a87475c4e365f6eedf2766e685911df95e3280 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Wed, 30 Nov 2022 21:31:24 +0000 Subject: [PATCH 02/13] refactor(terraform): simplify naming of front door resources make endpoint name more unique --- terraform/front_door.tf | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/terraform/front_door.tf b/terraform/front_door.tf index 7e3d56c4..cdec1b89 100644 --- a/terraform/front_door.tf +++ b/terraform/front_door.tf @@ -1,22 +1,21 @@ locals { - front_door_origin_group_name = azurerm_linux_web_app.main.name - front_door_origin_name = azurerm_linux_web_app.main.name - front_door_route_name = azurerm_linux_web_app.main.name + front_door_name = "eligibility-server-${local.env_name}" } resource "azurerm_cdn_frontdoor_profile" "main" { - name = "mst-courtesy-cards-${local.env_name}" + name = local.front_door_name resource_group_name = data.azurerm_resource_group.main.name sku_name = "Standard_AzureFrontDoor" } resource "azurerm_cdn_frontdoor_endpoint" "main" { - name = "frontdoor-endpoint-${local.env_name}" + # used in the front door URL + name = "mst-courtesy-cards-eligibility-server-${local.env_name}" cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id } resource "azurerm_cdn_frontdoor_origin_group" "main" { - name = local.front_door_origin_group_name + name = local.front_door_name cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id session_affinity_enabled = true @@ -24,7 +23,7 @@ resource "azurerm_cdn_frontdoor_origin_group" "main" { } resource "azurerm_cdn_frontdoor_origin" "main" { - name = local.front_door_origin_name + name = local.front_door_name cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.main.id enabled = true @@ -38,7 +37,7 @@ resource "azurerm_cdn_frontdoor_origin" "main" { } resource "azurerm_cdn_frontdoor_route" "main" { - name = local.front_door_route_name + name = local.front_door_name cdn_frontdoor_endpoint_id = azurerm_cdn_frontdoor_endpoint.main.id cdn_frontdoor_origin_group_id = azurerm_cdn_frontdoor_origin_group.main.id cdn_frontdoor_origin_ids = [azurerm_cdn_frontdoor_origin.main.id] From 0d74b6a2707599c7b86b1d8d76c8a5a33dd0fca8 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Wed, 30 Nov 2022 21:51:11 +0000 Subject: [PATCH 03/13] feat(terraform): create front door security policy remove ip restriction blocks from app service because for app services that integrate with a virtual network using service endpoints (such as the CDT-hosted Benefits client), requests will be routed through Azure's optimized backbone and will not use the app's list of outbound IP addresses. --- terraform/app_service.tf | 6 ------ terraform/front_door.tf | 42 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/terraform/app_service.tf b/terraform/app_service.tf index b82f56cb..b0917e21 100644 --- a/terraform/app_service.tf +++ b/terraform/app_service.tf @@ -22,12 +22,6 @@ resource "azurerm_linux_web_app" "main" { ftps_state = "Disabled" http2_enabled = true - dynamic "ip_restriction" { - for_each = var.IP_ADDRESS_WHITELIST - content { - ip_address = ip_restriction.value - } - } vnet_route_all_enabled = true application_stack { docker_image = "ghcr.io/cal-itp/eligibility-server" diff --git a/terraform/front_door.tf b/terraform/front_door.tf index cdec1b89..132b6e59 100644 --- a/terraform/front_door.tf +++ b/terraform/front_door.tf @@ -48,3 +48,45 @@ resource "azurerm_cdn_frontdoor_route" "main" { forwarding_protocol = "HttpsOnly" link_to_default_domain = true } + +resource "azurerm_cdn_frontdoor_security_policy" "main" { + name = local.front_door_name + cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id + + security_policies { + firewall { + cdn_frontdoor_firewall_policy_id = azurerm_cdn_frontdoor_firewall_policy.main.id + association { + patterns_to_match = ["/*"] + domain { + cdn_frontdoor_domain_id = azurerm_cdn_frontdoor_endpoint.main.host_name + } + } + } + } +} + +resource "azurerm_cdn_frontdoor_firewall_policy" "main" { + name = "${local.env_name}waf" + resource_group_name = data.azurerm_resource_group.main.name + sku_name = azurerm_cdn_frontdoor_profile.main.sku_name + enabled = true + mode = "Prevention" + custom_block_response_status_code = 403 + custom_block_response_body = base64encode("Blocked") + + custom_rule { + name = "iprestriction${local.env_name}" + enabled = true + type = "MatchRule" + priority = 1 + action = "Block" + + match_condition { + match_variable = "SocketAddr" + operator = "Contains" + negation_condition = true + match_values = var.IP_ADDRESS_WHITELIST + } + } +} From 3052d6f43ff3b75f7ea0b2011f95f073885a872b Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Wed, 30 Nov 2022 22:26:58 +0000 Subject: [PATCH 04/13] feat(terraform): add service-tag based access restrictions only allow our Front Door and availability tests. --- terraform/app_service.tf | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/terraform/app_service.tf b/terraform/app_service.tf index b0917e21..5fce0c67 100644 --- a/terraform/app_service.tf +++ b/terraform/app_service.tf @@ -23,6 +23,24 @@ resource "azurerm_linux_web_app" "main" { http2_enabled = true vnet_route_all_enabled = true + + ip_restriction { + name = "Front Door" + priority = 100 + action = "Allow" + service_tag = "AzureFrontDoor.Backend" + headers { + x_azure_fdid = [azurerm_cdn_frontdoor_profile.main.resource_guid] + } + } + + ip_restriction { + name = "Availability Test" + priority = 200 + action = "Allow" + service_tag = "ApplicationInsightsAvailability" + } + application_stack { docker_image = "ghcr.io/cal-itp/eligibility-server" docker_image_tag = local.env_name From 3ec759ab2435d5bdd1fd63439624c0af37cd28e0 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Wed, 30 Nov 2022 22:39:51 +0000 Subject: [PATCH 05/13] refactor(terraform): allow environment-specific ip whitelists --- terraform/environment.tf | 1 + terraform/front_door.tf | 2 +- terraform/variables.tf | 14 +++++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/terraform/environment.tf b/terraform/environment.tf index f3264b27..6daa1a61 100644 --- a/terraform/environment.tf +++ b/terraform/environment.tf @@ -1,5 +1,6 @@ locals { is_prod = terraform.workspace == "default" + is_test = terraform.workspace == "test" env_name = local.is_prod ? "prod" : terraform.workspace } diff --git a/terraform/front_door.tf b/terraform/front_door.tf index 132b6e59..4430c856 100644 --- a/terraform/front_door.tf +++ b/terraform/front_door.tf @@ -86,7 +86,7 @@ resource "azurerm_cdn_frontdoor_firewall_policy" "main" { match_variable = "SocketAddr" operator = "Contains" negation_condition = true - match_values = var.IP_ADDRESS_WHITELIST + match_values = local.is_prod ? var.IP_ADDRESS_WHITELIST_PROD : local.is_test ? var.IP_ADDRESS_WHITELIST_TEST : var.IP_ADDRESS_WHITELIST_DEV } } } diff --git a/terraform/variables.tf b/terraform/variables.tf index d3332c98..f253713c 100644 --- a/terraform/variables.tf +++ b/terraform/variables.tf @@ -16,7 +16,19 @@ variable "VELOCITY_ETL_APP_OBJECT_ID" { type = string } -variable "IP_ADDRESS_WHITELIST" { +variable "IP_ADDRESS_WHITELIST_DEV" { + description = "List of IP addresses allowed to connect to the app service, in CIDR notation: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/linux_web_app#ip_address. By default, all IP addresses are allowed." + type = list(string) + default = [] +} + +variable "IP_ADDRESS_WHITELIST_TEST" { + description = "List of IP addresses allowed to connect to the app service, in CIDR notation: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/linux_web_app#ip_address. By default, all IP addresses are allowed." + type = list(string) + default = [] +} + +variable "IP_ADDRESS_WHITELIST_PROD" { description = "List of IP addresses allowed to connect to the app service, in CIDR notation: https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/linux_web_app#ip_address. By default, all IP addresses are allowed." type = list(string) default = [] From f1bd673da090fbd303eb6636ff1899f81501114e Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Mon, 5 Dec 2022 17:41:46 +0000 Subject: [PATCH 06/13] chore(terraform): remove unneeded setting on origin group --- terraform/front_door.tf | 1 - 1 file changed, 1 deletion(-) diff --git a/terraform/front_door.tf b/terraform/front_door.tf index 4430c856..ea20716d 100644 --- a/terraform/front_door.tf +++ b/terraform/front_door.tf @@ -17,7 +17,6 @@ resource "azurerm_cdn_frontdoor_endpoint" "main" { resource "azurerm_cdn_frontdoor_origin_group" "main" { name = local.front_door_name cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id - session_affinity_enabled = true load_balancing {} } From 0c160c72a731e6565dc64b3e86ac2aba4354040f Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Mon, 5 Dec 2022 18:01:08 +0000 Subject: [PATCH 07/13] chore(terraform): remove origin settings that are just using defaults --- terraform/front_door.tf | 3 --- 1 file changed, 3 deletions(-) diff --git a/terraform/front_door.tf b/terraform/front_door.tf index ea20716d..bdc698b4 100644 --- a/terraform/front_door.tf +++ b/terraform/front_door.tf @@ -27,11 +27,8 @@ resource "azurerm_cdn_frontdoor_origin" "main" { enabled = true host_name = azurerm_linux_web_app.main.default_hostname - http_port = 80 - https_port = 443 origin_host_header = azurerm_linux_web_app.main.default_hostname certificate_name_check_enabled = true - priority = 1 weight = 1000 } From 5a28a9bcece6cb5084dbbde702768bd1c030a774 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Mon, 5 Dec 2022 18:42:46 +0000 Subject: [PATCH 08/13] feat(terraform): allow any IP address to access /healthcheck --- terraform/front_door.tf | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/terraform/front_door.tf b/terraform/front_door.tf index bdc698b4..59d25120 100644 --- a/terraform/front_door.tf +++ b/terraform/front_door.tf @@ -72,10 +72,24 @@ resource "azurerm_cdn_frontdoor_firewall_policy" "main" { custom_block_response_body = base64encode("Blocked") custom_rule { - name = "iprestriction${local.env_name}" + name = "healthcheck" enabled = true type = "MatchRule" priority = 1 + action = "Allow" + + match_condition { + match_variable = "RequestUri" + operator = "EndsWith" + match_values = ["/healthcheck"] + } + } + + custom_rule { + name = "iprestriction${local.env_name}" + enabled = true + type = "MatchRule" + priority = 2 action = "Block" match_condition { From 17af4066c724a7fad0174cc771696b285eff8ddd Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Mon, 5 Dec 2022 19:07:15 +0000 Subject: [PATCH 09/13] docs(uptime): add a comment to uptime module about access restrictions --- terraform/uptime.tf | 1 + 1 file changed, 1 insertion(+) diff --git a/terraform/uptime.tf b/terraform/uptime.tf index 703901cb..b613e9c3 100644 --- a/terraform/uptime.tf +++ b/terraform/uptime.tf @@ -1,3 +1,4 @@ +# when setting up access restrictions, make sure to allow the ApplicationInsightsAvailability service tag module "healthcheck" { source = "./uptime" From 5e6f74e68084f4b5c47019325b489525876c8796 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Mon, 5 Dec 2022 20:11:25 +0000 Subject: [PATCH 10/13] docs(terraform): add docs on access restrictions / front door --- terraform/README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/terraform/README.md b/terraform/README.md index cf1c045b..401950e8 100644 --- a/terraform/README.md +++ b/terraform/README.md @@ -28,6 +28,12 @@ All resources in these Resource Groups should be reflected in Terraform in this For browsing the [Azure portal](https://portal.azure.com), you can [switch your `Default subscription filter`](https://docs.microsoft.com/en-us/azure/azure-portal/set-preferences). +## Access restrictions + +We restrict which IP addresses that can access the app service by using a Web Application Firewall (WAF) configured on a Front Door. There is an exception for the `/healthcheck` path, which can be accessed by any IP address. + +The app service itself gives access only to our Front Door and to Azure availability tests. + ## Monitoring We have [ping tests](https://docs.microsoft.com/en-us/azure/azure-monitor/app/monitor-web-app-availability) set up to notify about availability of each environment. Alerts go to [#benefits-notify](https://cal-itp.slack.com/archives/C022HHSEE3F). From 14a548b94a5f65479e3cb2f172834207a4df6e54 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Tue, 6 Dec 2022 21:47:10 +0000 Subject: [PATCH 11/13] chore(terraform): change custom block response body to match status code --- terraform/front_door.tf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/terraform/front_door.tf b/terraform/front_door.tf index 59d25120..77188bfc 100644 --- a/terraform/front_door.tf +++ b/terraform/front_door.tf @@ -69,7 +69,7 @@ resource "azurerm_cdn_frontdoor_firewall_policy" "main" { enabled = true mode = "Prevention" custom_block_response_status_code = 403 - custom_block_response_body = base64encode("Blocked") + custom_block_response_body = base64encode("Forbidden") custom_rule { name = "healthcheck" From 8ed904ee58246672abc6deeaf262ba237096e7b0 Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Tue, 6 Dec 2022 22:16:04 +0000 Subject: [PATCH 12/13] chore(terraform): add comment about empty load balancing block --- terraform/front_door.tf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/terraform/front_door.tf b/terraform/front_door.tf index 77188bfc..4d5e2153 100644 --- a/terraform/front_door.tf +++ b/terraform/front_door.tf @@ -18,6 +18,8 @@ resource "azurerm_cdn_frontdoor_origin_group" "main" { name = local.front_door_name cdn_frontdoor_profile_id = azurerm_cdn_frontdoor_profile.main.id + # this block is required, and it's empty because we are fine with using the default values + # https://registry.terraform.io/providers/hashicorp/azurerm/latest/docs/resources/cdn_frontdoor_origin_group#load_balancing load_balancing {} } From f9b039d31aef260bdc9fce1633817a2f43a0193d Mon Sep 17 00:00:00 2001 From: Angela Tran Date: Tue, 6 Dec 2022 22:26:09 +0000 Subject: [PATCH 13/13] refactor(terraform): make healthcheck custom rule allow exact path --- terraform/front_door.tf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/terraform/front_door.tf b/terraform/front_door.tf index 4d5e2153..f47431b6 100644 --- a/terraform/front_door.tf +++ b/terraform/front_door.tf @@ -82,8 +82,8 @@ resource "azurerm_cdn_frontdoor_firewall_policy" "main" { match_condition { match_variable = "RequestUri" - operator = "EndsWith" - match_values = ["/healthcheck"] + operator = "Equals" + match_values = ["https://${azurerm_cdn_frontdoor_endpoint.main.host_name}:443/healthcheck"] } }