The following module will spin up the infrastructure needed to simplify Azure network routing and allow routing towards the VNETs to be controlled by an NVA of your choice. This could be a Firewall/Router/SDWAN device.
-> As of initial release the only NVA option is a pair of Fortigates.
- The NVA must support BGP
- The NVA must support eBGP multihop
- Traffic between VNETs will route via the NVA
- Traffic within the VNET will not route via the NVA
- The NVA pair should be able to deal with asymmetric routing
- Suggest keeping the advertisement interval short so routing changes are pushed down to the VNETs quickly or add summary routes to the NVA. An alternative is to have the NVA advertise the routes with the ILB frontend IP as the next hop.
- If routing is required between more than one RouteServer this must be done via the NVAs.
- You may also need to manipulate the AS path of routes from the RouteServers so that the same ASN doesn't appear twice from separate AS.
- This is due to BGPs route-loop prevention and Azure's inflexibility in setting the ASN used by the RouteServer.
terraform {
required_version = "~> 1.0"
required_providers {
azurerm = {
source = "hashicorp/azurerm"
version = ">= 3.2.0"
}
}
}
provider "azurerm" {
features {}
}
module "azure_fortigate_pair" {
source = "poroping/secure-vnet-nva/azurerm"
version = "~> 0.0.1"
location = "North Europe"
name_prefix = "advpn-hub-01"
resource_group_name = "rsg-dev-transit"
vnet_prefix = "10.255.0.0/22"
nva_bgp_asn = 64420
nva_type = "fortigate"
username = "fortinet"
password = "Fortin3t!"
trusted_hosts = [
{
prefix = "185.62.0.0/32",
name = "MGMT-JUMPER",
priority = 150
}
]
# module options
create_sslvpn = false
assign_global_reader = false
use_ilb = false
}
output "fortigate_public_ips" {
value = module.azure_fortigate_pair.nva_public_ips
}
## Dev vnet
## Create another vnet to connect to this transit vnet
resource "azurerm_resource_group" "devrsg" {
name = "rsg-dev-workload"
location = "North Europe"
}
locals {
dev_prefix = "192.168.8.0/22"
dev_subnets = cidrsubnets(local.dev_prefix, 2, 2, 2, 2)
}
resource "azurerm_virtual_network" "dev" {
name = "dev-vnet"
address_space = [local.dev_prefix]
location = azurerm_resource_group.devrsg.location
resource_group_name = azurerm_resource_group.devrsg.name
}
resource "azurerm_subnet" "dev01" {
name = "dev-snet-01"
resource_group_name = azurerm_resource_group.devrsg.name
virtual_network_name = azurerm_virtual_network.dev.name
address_prefixes = [local.dev_subnets[0]]
}
## Connections
resource "azurerm_virtual_network_peering" "dev2transit" {
name = "vnet-conn-dev2transit"
resource_group_name = azurerm_resource_group.devrsg.name
virtual_network_name = azurerm_virtual_network.dev.name
remote_virtual_network_id = module.azure_fortigate_pair.transit_vnet_id
allow_virtual_network_access = true
allow_forwarded_traffic = true
use_remote_gateways = true # secret sauce
depends_on = [
module.azure_fortigate_pair # explicitly depend on module finishing to avoid no gateway error
]
}
# Sometimes this VNET connection seems to fail and not propogate the routes from the Route Server.
# This only appears to be an issue when creating a new Route Server, an existing one will always rx the correct routes.
# Suspect it's race condition with the Route Server completing setup and the peering being completed too early.
resource "azurerm_virtual_network_peering" "transit2dev" {
name = "vnet-conn-transit2dev"
resource_group_name = module.azure_fortigate_pair.rsg_name
virtual_network_name = module.azure_fortigate_pair.transit_vnet_name
remote_virtual_network_id = azurerm_virtual_network.dev.id
allow_virtual_network_access = true
allow_forwarded_traffic = true
allow_gateway_transit = true # secret sauce
}
# To validate we should now see this VNET prefix advertised to the NVA via BGP.
# NVA MGMT interface will be accessible on port 10443 from list of trusted hosts.
Name | Version |
---|---|
azurerm | >= 3.0.2 |
random | n/a |
template | n/a |
Name | Description | Type | Default | Required |
---|---|---|---|---|
nva_type | Type of NVA. Valid options: fortigate |
string |
n/a | yes |
api_key | API key to cloudinit NVA with. | string |
null |
no |
assign_global_reader | Assign NVA system identity Global Reader in the subscription NVA is created in. Useful for NVAs that can access Azure metadata to create dynamic objects etc. Requires the Service Principal permissions to modify Roles. | bool |
false |
no |
availability_zone_support | Availability zone support. Ensure region supports this feature. | bool |
false |
no |
create_sslvpn | Create an ELB and required resources to bootstrap an SSLVPN service. | bool |
true |
no |
fortigate_licenses | Licenses for BYOL Fortigates. If not set will use PAYG. | list(string) |
null |
no |
fortigate_sku | If SKU changes can overwrite here otherwise can leave as default. | object({ |
{ |
no |
fortios_version | Target FortiOS version image. | string |
"latest" |
no |
location | Azure region for resources. | string |
"North Europe" |
no |
name_prefix | Prefix for Azure resource names. | string |
"secure-transit" |
no |
nva_bgp_asn | BGP ASN for NVA. | number |
65514 |
no |
password | Initial password for admin access. | string |
"Solarwinds123!" |
no |
resource_group_name | Full name for Azure resource group. | string |
"rsg-fortigate-testing" |
no |
trusted_hosts | Set of CIDRs to add to NSG for management access to NVA. | set(object({ |
[] |
no |
use_ilb | Create an internal load balancer. Intention is to allow the NVA to advertise BGP routes towards the Route Server with the ILB frontend address as the next-hop. ILB should deal with probe loss failovers within 10 secs. | bool |
false |
no |
username | Initial username for admin access. | string |
"fortinet" |
no |
vm_size | Azure VM SKU | string |
"Standard_D2ds_v5" |
no |
vnet_prefix | Prefix for transit VNET. Will be broken into 4 equal subnets. | string |
"10.255.0.0/22" |
no |
Name | Description |
---|---|
elb_public_ip | Public IP assigned to ELB for SSLVPN. |
external_interfaces | n/a |
external_subnet_prefix | Transit VNET external subnet prefix. |
internal_interfaces | n/a |
internal_subnet_prefix | Transit VNET internal subnet prefix. |
nva_public_ips | NVA public IP addresses |
resource_group | n/a |
route_server_ips | RouteServer IP addresses. |
rsg_name | Name of created resource group. |
transit_vnet_id | Transit VNET ID. |
transit_vnet_name | Transit VNET name. |