Add option for forced tunneling through TRE's Firewall (#4238)

* Add option for forced tunneling through TRE's Firewall

* fix linting issues

* refine doc

* rename force tunnel route

* add variables to schema

* fix github actions

* add warning to fw force tunnel doc

* refine doc

* fix linting errors

* send firewall props through makefile command

* update doc

* update config.yaml.sample

* remove typo

* shorten comment

* Update docs/tre-admins/configure-firewall-force-tunneling.md

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* fix typo in docs

* fix linting issues

* fix linting error

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Yuval Yaron 2025-01-07 20:43:09 +02:00 коммит произвёл GitHub
Родитель 8c1cea9c7e
Коммит 97debdc237
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: B5690EEEBB952194
13 изменённых файлов: 415 добавлений и 326 удалений

Просмотреть файл

@ -1,11 +1,11 @@
---
name: Build Validation
on: # yamllint disable-line rule:truthy
on: # yamllint disable-line rule:truthy
pull_request:
branches:
- main
- 'feature/**'
- "feature/**"
# for each ref (branch/pr) run just the most recent,
# cancel other pending/running ones
@ -57,6 +57,11 @@ jobs:
with:
terraform_version: "1.9.8"
- uses: hashicorp/setup-terraform@v3
if: ${{ steps.filter.outputs.terraform == 'true' }}
with:
terraform_version: "1.9.8"
- name: Terraform format check
if: ${{ steps.filter.outputs.terraform == 'true' }}
run: terraform fmt -check -recursive
@ -112,7 +117,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_TERRAFORM_TFLINT: true
TERRAFORM_TFLINT_CONFIG_FILE: .tflint_core.hcl
FILTER_REGEX_INCLUDE: './core/.*'
FILTER_REGEX_INCLUDE: "./core/.*"
- name: Workspace Tags
if: ${{ steps.filter.outputs.terraform_workspaces == 'true' }}
@ -123,7 +128,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_TERRAFORM_TFLINT: true
TERRAFORM_TFLINT_CONFIG_FILE: .tflint_workspaces.hcl
FILTER_REGEX_INCLUDE: './templates/workspaces/.*'
FILTER_REGEX_INCLUDE: "./templates/workspaces/.*"
- name: Workspace Services Tags
if: ${{ steps.filter.outputs.terraform_workspace_services == 'true' }}
@ -134,8 +139,8 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_TERRAFORM_TFLINT: true
TERRAFORM_TFLINT_CONFIG_FILE: .tflint_workspace_services.hcl
FILTER_REGEX_INCLUDE: './templates/workspaces/.*'
FILTER_REGEX_EXCLUDE: '.*user_resource.*'
FILTER_REGEX_INCLUDE: "./templates/workspaces/.*"
FILTER_REGEX_EXCLUDE: ".*user_resource.*"
- name: User Resources Tags
if: ${{ steps.filter.outputs.terraform_workspace_services == 'true' }}
@ -146,7 +151,7 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_TERRAFORM_TFLINT: true
TERRAFORM_TFLINT_CONFIG_FILE: .tflint_user_resources.hcl
FILTER_REGEX_INCLUDE: './templates/workspace_services/.*/user_resources/.*'
FILTER_REGEX_INCLUDE: "./templates/workspace_services/.*/user_resources/.*"
- name: Shared Services Tags
if: ${{ steps.filter.outputs.terraform_shared_services == 'true' }}
@ -157,4 +162,4 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
VALIDATE_TERRAFORM_TFLINT: true
TERRAFORM_TFLINT_CONFIG_FILE: .tflint_shared_services.hcl
FILTER_REGEX_INCLUDE: './templates/shared_services/.*'
FILTER_REGEX_INCLUDE: "./templates/shared_services/.*"

6
.github/workflows/codeql-analysis.yml поставляемый
Просмотреть файл

@ -12,13 +12,13 @@
#
name: "CodeQL"
on: # yamllint disable-line rule:truthy
on: # yamllint disable-line rule:truthy
push:
branches: [main]
pull_request:
branches: [main]
schedule:
- cron: '41 3 * * 5'
- cron: "41 3 * * 5"
# for each ref (branch/pr) run just the most recent,
# cancel other pending/running ones
@ -38,7 +38,7 @@ jobs:
strategy:
fail-fast: false
matrix:
language: ['python', 'java', 'javascript', 'typescript']
language: ["python", "java", "javascript", "typescript"]
steps:
- name: Checkout repository

Просмотреть файл

@ -49,6 +49,7 @@ BUG FIXES:
* Fix VM actions where Workspace shared storage doesn't allow shared key access ([#4222](https://github.com/microsoft/AzureTRE/issues/4222))
* Fix public exposure in Guacamole service ([[#4199](https://github.com/microsoft/AzureTRE/issues/4199)])
* Fix Azure ML network tags to use name rather than ID ([[#4151](https://github.com/microsoft/AzureTRE/issues/4151)])
* Add option to force tunnel TRE's Firewall ([#4237](https://github.com/microsoft/AzureTRE/issues/4237))
COMPONENTS:

Просмотреть файл

@ -309,8 +309,10 @@ deploy-shared-service:
&& ${MAKEFILE_DIR}/devops/scripts/deploy_shared_service.sh $${PROPS}
firewall-install:
$(MAKE) bundle-build bundle-publish bundle-register deploy-shared-service \
DIR=${MAKEFILE_DIR}/templates/shared_services/firewall/ BUNDLE_TYPE=shared_service
. ${MAKEFILE_DIR}/devops/scripts/check_dependencies.sh env \
&& $(MAKE) bundle-build bundle-publish bundle-register deploy-shared-service \
DIR=${MAKEFILE_DIR}/templates/shared_services/firewall/ BUNDLE_TYPE=shared_service \
PROPS="$${FIREWALL_SKU+--firewall_sku $${FIREWALL_SKU} }$${FIREWALL_FORCE_TUNNEL_IP+--firewall_force_tunnel_ip $${FIREWALL_FORCE_TUNNEL_IP} }"
static-web-upload:
$(call target_title, "Uploading to static website") \

Просмотреть файл

@ -10,7 +10,7 @@ management:
acr_name: __CHANGE_ME__
# ID of external Key Vault to store CMKs in (only required if enable_cmk_encryption is true)
# external_key_store_id: __CHANGE_ME__
# Name of Key Vault for encryption keys, required only if enable_cmk_encryption is true and not using external_key_store_id
# Name of Key Vault for encryption, required if enable_cmk_encryption is true and external_key_store_id is not set
# encryption_kv_name: __CHANGE_ME__
# Azure Resource Manager credentials used for CI/CD pipelines
arm_subscription_id: __CHANGE_ME__
@ -46,6 +46,7 @@ tre:
# The TRE Web UI is deployed by default.
# Uncomment the following to disable deployment of the Web UI.
# deploy_ui: false
# firewall_force_tunnel_ip: __CHANGE_ME__
firewall_sku: Standard
app_gateway_sku: Standard_v2

Просмотреть файл

@ -0,0 +1,21 @@
# Forced Tunneling to External Firewall in TRE
Azure TRE deploys and manages an Azure firewall to ensure creation of workspace level rules can be automated when TRE workspaces and other services are created without manual intervention.
It is highly recommended leaving the Azure TRE firewall in place. If there is still the requirement to send all traffic through a centralized enterprise firewall, such as that deployed as part of an Azure landing zone, then forced tunnelling should be used. The centralized firewall will need a superset of rules used by the TRE.
To setup forced tunneling to an external firewall, follow these steps:
## 1. Set the firewall_force_tunnel_ip parameter in the config.yaml file
Provide the external firewall's IP address:
```json
firewall_force_tunnel_ip: 192.168.0.4
```
This automatically creates a route table to direct TREs traffic to the specified IP.
## 2. Manually Connect TRE to Your Firewall
Configure connectivity between TREs VNet and your external firewall using one of the following methods:
1. **VNet Peering**: Peer the TRE VNet with your firewalls VNet.
1. **ExpressRoute**: Use a private connection for firewalls located on-premises.
1. **Site-to-Site VPN**: Establish a VPN connection as an alternative.

Просмотреть файл

@ -140,6 +140,7 @@ nav:
- Supported Clouds: tre-admins/supported-clouds.md
- Customer Managed Keys: tre-admins/customer-managed-keys.md
- Custom Domain Name: tre-admins/custom-domain.md
- Firewall Force Tunneling: tre-admins/configure-firewall-force-tunneling.md
- Development: # Docs related to the developing code for the AzureTRE
- Local Development: using-tre/local-development/local-development.md

Просмотреть файл

@ -63,6 +63,12 @@
"source": {
"env": "ARM_ENVIRONMENT"
}
},
{
"name": "firewall_force_tunnel_ip",
"source": {
"env": "FIREWALL_FORCE_TUNNEL_IP"
}
}
]
}

Просмотреть файл

@ -1,7 +1,7 @@
---
schemaVersion: 1.0.0
name: tre-shared-service-firewall
version: 1.2.8
version: 1.3.0
description: "An Azure TRE Firewall shared service"
dockerfile: Dockerfile.tmpl
registry: azuretre
@ -54,6 +54,9 @@ parameters:
default: "graph.microsoft.com"
- name: arm_environment
type: string
- name: firewall_force_tunnel_ip
type: string
default: ""
mixins:
- terraform:
@ -69,6 +72,7 @@ install:
api_driven_network_rule_collections_b64: ${ bundle.parameters.network_rule_collections }
firewall_sku: ${ bundle.parameters.firewall_sku }
microsoft_graph_fqdn: ${ bundle.parameters.microsoft_graph_fqdn }
firewall_force_tunnel_ip: ${ bundle.parameters.firewall_force_tunnel_ip }
backendConfig:
use_azuread_auth: "true"
use_oidc: "true"
@ -87,6 +91,7 @@ upgrade:
api_driven_network_rule_collections_b64: ${ bundle.parameters.network_rule_collections }
firewall_sku: ${ bundle.parameters.firewall_sku }
microsoft_graph_fqdn: ${ bundle.parameters.microsoft_graph_fqdn }
firewall_force_tunnel_ip: ${ bundle.parameters.firewall_force_tunnel_ip }
backendConfig:
use_azuread_auth: "true"
use_oidc: "true"
@ -105,6 +110,7 @@ uninstall:
api_driven_network_rule_collections_b64: ${ bundle.parameters.network_rule_collections }
firewall_sku: ${ bundle.parameters.firewall_sku }
microsoft_graph_fqdn: ${ bundle.parameters.microsoft_graph_fqdn }
firewall_force_tunnel_ip: ${ bundle.parameters.firewall_force_tunnel_ip }
backendConfig:
use_azuread_auth: "true"
use_oidc: "true"

Просмотреть файл

@ -6,111 +6,127 @@
"description": "Provides Firewall shared service",
"required": [],
"properties": {
"firewall_sku": {
"type": "string",
"title": "Firewall SKU",
"description": "The SKU that will be used when deploying The Firewall.",
"default": "Standard",
"enum": [
"Basic",
"Standard",
"Premium"
]
},
"firewall_force_tunnel_ip": {
"type": "string",
"title": "Force Tunnel IP",
"description": "Optionally specify an IP address to forward all traffic to"
},
"rule_collections": {
"$id": "#properties/rule_collections",
"title": "application rule collections",
"type": "array",
"default": [],
"$id": "#properties/rule_collections",
"title": "application rule collections",
"type": "array",
"default": [],
"updateable": true,
"items":{
"title": "items",
"type": "object",
"required": [
"name",
"rules"
],
"properties": {
"name": {
"title": "name",
"type": "string",
"examples": [
"my-rule"
],
"pattern": "^.*$"
},
"action": {
"title": "action DEPRECATED",
"type": "string",
"examples": [
"Allow"
],
"items": {
"title": "items",
"type": "object",
"required": [
"name",
"rules"
],
"properties": {
"name": {
"title": "name",
"type": "string",
"examples": [
"my-rule"
],
"pattern": "^.*$"
},
"action": {
"title": "action DEPRECATED",
"type": "string",
"examples": [
"Allow"
],
"enum": [
"Allow",
"Deny"
]
},
"rules": {
"title": "rules",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"title": "name",
"type": "string",
"examples": [
"rule 1"
],
"pattern": "^.*$"
},
"description": {
"title": "description",
"type": "string",
"default": "",
"examples": [
"My rule description here"
],
"pattern": "^.*$"
},
"protocols": {
"title": "protocols",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "object",
"required": [
"port",
"type"
],
"properties": {
"port": {
"title": "port",
"type": "string",
"examples": [
"1234"
],
"pattern": "^.*$"
},
"type": {
"title": "type",
"type": "string",
"rules": {
"title": "rules",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"title": "name",
"type": "string",
"examples": [
"rule 1"
],
"pattern": "^.*$"
},
"description": {
"title": "description",
"type": "string",
"default": "",
"examples": [
"My rule description here"
],
"pattern": "^.*$"
},
"protocols": {
"title": "protocols",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "object",
"required": [
"port",
"type"
],
"properties": {
"port": {
"title": "port",
"type": "string",
"examples": [
"1234"
],
"pattern": "^.*$"
},
"type": {
"title": "type",
"type": "string",
"enum": [
"Http",
"Https",
"Mssql"
],
"examples": [
"Http"
]
}
}
}
},
"examples": [
"Http"
]
}
}
}
},
"fqdn_tags": {
"title": "fqdn tags",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "string",
"default": "",
"enum":[
"title": "fqdn tags",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "string",
"default": "",
"enum": [
"AppServiceEnvironment",
"AzureBackup",
"AzureKubernetesService",
@ -120,246 +136,246 @@
"WindowsUpdate",
"WindowsVirtualDesktop"
],
"examples": [
"AzureKubernetesService"
]
}
},
"target_fqdns": {
"title": "destination fqdns",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "string",
"default": "",
"examples": [
"one.two.three.com"
]
}
},
"source_addresses": {
"title": "source addresses",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "string",
"default": "",
"examples": [
"172.196.0.0"
]
}
},
"examples": [
"AzureKubernetesService"
]
}
},
"target_fqdns": {
"title": "destination fqdns",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "string",
"default": "",
"examples": [
"one.two.three.com"
]
}
},
"source_addresses": {
"title": "source addresses",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "string",
"default": "",
"examples": [
"172.196.0.0"
]
}
},
"source_ip_group_ids": {
"title": "source ip group ids",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "string",
"default": "",
"examples": [
"some_ip_group_id"
]
}
},
"title": "source ip group ids",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "string",
"default": "",
"examples": [
"some_ip_group_id"
]
}
},
"source_ip_groups_in_core": {
"title": "source ip group names in core",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "string",
"default": "",
"examples": [
"ip_group_name_in_core_resource_group"
]
}
}
}
}
}
}
}
"title": "source ip group names in core",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "string",
"default": "",
"examples": [
"ip_group_name_in_core_resource_group"
]
}
}
}
}
}
}
}
},
"network_rule_collections": {
"$id": "#properties/network_rule_collections",
"title": "network rule collections",
"type": "array",
"default": [],
"$id": "#properties/network_rule_collections",
"title": "network rule collections",
"type": "array",
"default": [],
"updateable": true,
"items":{
"title": "items",
"type": "object",
"required": [
"name",
"rules"
],
"properties": {
"name": {
"title": "name",
"type": "string",
"examples": [
"my-rule"
],
"pattern": "^.*$"
},
"action": {
"title": "action DEPRECATED",
"type": "string",
"examples": [
"Allow"
],
"items": {
"title": "items",
"type": "object",
"required": [
"name",
"rules"
],
"properties": {
"name": {
"title": "name",
"type": "string",
"examples": [
"my-rule"
],
"pattern": "^.*$"
},
"action": {
"title": "action DEPRECATED",
"type": "string",
"examples": [
"Allow"
],
"enum": [
"Allow",
"Deny"
]
},
"rules": {
"title": "rules",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"title": "name",
"type": "string",
"examples": [
"rule 1"
],
"pattern": "^.{5,80}$"
},
"description": {
"title": "description DEPRECATED",
"type": "string",
"default": "",
"examples": [
"My rule description here"
],
"pattern": "^.*$"
},
"source_addresses": {
"title": "source addresses",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "string",
"default": "",
"examples": [
"172.196.0.0"
]
}
},
"rules": {
"title": "rules",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "object",
"required": [
"name"
],
"properties": {
"name": {
"title": "name",
"type": "string",
"examples": [
"rule 1"
],
"pattern": "^.{5,80}$"
},
"description": {
"title": "description DEPRECATED",
"type": "string",
"default": "",
"examples": [
"My rule description here"
],
"pattern": "^.*$"
},
"source_addresses": {
"title": "source addresses",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "string",
"default": "",
"examples": [
"172.196.0.0"
]
}
},
"source_ip_group_ids": {
"title": "source ip group ids",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "string",
"default": "",
"examples": [
"some_ip_group_id"
]
}
},
"title": "source ip group ids",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "string",
"default": "",
"examples": [
"some_ip_group_id"
]
}
},
"source_ip_groups_in_core": {
"title": "source ip group names in core",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "string",
"default": "",
"examples": [
"some_ip_group_name"
]
}
},
"destination_addresses": {
"title": "destination addresses",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "string",
"default": "",
"examples": [
"172.196.0.0"
]
}
},
"title": "source ip group names in core",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "string",
"default": "",
"examples": [
"some_ip_group_name"
]
}
},
"destination_addresses": {
"title": "destination addresses",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "string",
"default": "",
"examples": [
"172.196.0.0"
]
}
},
"destination_ip_group_ids": {
"title": "destination ip group ids",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "string",
"default": "",
"examples": [
"some_ip_group_id"
]
}
},
"title": "destination ip group ids",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "string",
"default": "",
"examples": [
"some_ip_group_id"
]
}
},
"destination_fqdns": {
"title": "destination fqdns",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "string",
"default": "",
"examples": [
"one.two.three.com"
]
}
},
"title": "destination fqdns",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "string",
"default": "",
"examples": [
"one.two.three.com"
]
}
},
"destination_ports": {
"title": "destination ports",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "string",
"default": "",
"examples": [
"80",
"title": "destination ports",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "string",
"default": "",
"examples": [
"80",
"443",
"*"
]
}
},
"protocols": {
"title": "protocols",
"type": "array",
"default": [],
"items":{
"title": "items",
"type": "string",
]
}
},
"protocols": {
"title": "protocols",
"type": "array",
"default": [],
"items": {
"title": "items",
"type": "string",
"enum": [
"Any",
"ICMP",
"TCP",
"UDP"
],
"examples": [
"TCP"
]
}
}
}
}
}
}
}
"examples": [
"TCP"
]
}
}
}
}
}
}
}
}
}
}

Просмотреть файл

@ -15,7 +15,7 @@ moved {
}
resource "azurerm_public_ip" "fwmanagement" {
count = local.effective_firewall_sku == "Basic" ? 1 : 0
count = (var.firewall_force_tunnel_ip != "" || local.effective_firewall_sku == "Basic") ? 1 : 0
name = "pip-fw-management-${var.tre_id}"
resource_group_name = local.core_resource_group_name
location = data.azurerm_resource_group.rg.location
@ -42,7 +42,7 @@ resource "azurerm_firewall" "fw" {
}
dynamic "management_ip_configuration" {
for_each = local.effective_firewall_sku == "Basic" ? [1] : []
for_each = (var.firewall_force_tunnel_ip != "" || local.effective_firewall_sku == "Basic") ? [1] : []
content {
name = "mgmtconfig"
subnet_id = data.azurerm_subnet.firewall_management.id

Просмотреть файл

@ -87,3 +87,28 @@ resource "azurerm_subnet_route_table_association" "rt_airlock_events_subnet_asso
azurerm_firewall_policy_rule_collection_group.dynamic_application
]
}
resource "azurerm_route_table" "fw_tunnel_rt" {
count = var.firewall_force_tunnel_ip != "" ? 1 : 0
name = "rt-fw-tunnel-${var.tre_id}"
resource_group_name = local.core_resource_group_name
location = data.azurerm_resource_group.rg.location
bgp_route_propagation_enabled = true
tags = local.tre_shared_service_tags
lifecycle { ignore_changes = [tags] }
route {
name = "ForceTunnelRoute"
address_prefix = "0.0.0.0/0"
next_hop_type = "VirtualAppliance"
next_hop_in_ip_address = var.firewall_force_tunnel_ip
}
}
resource "azurerm_subnet_route_table_association" "rt_fw_tunnel_subnet_association" {
count = var.firewall_force_tunnel_ip != "" ? 1 : 0
subnet_id = data.azurerm_subnet.firewall.id
route_table_id = azurerm_route_table.fw_tunnel_rt[0].id
}

Просмотреть файл

@ -27,3 +27,8 @@ variable "firewall_sku" {
type = string
default = ""
}
variable "firewall_force_tunnel_ip" {
type = string
default = ""
}