├── .github └── funding.yml ├── hub-01-bicep └── bicepconfig.json ├── images ├── dns.png ├── ipsec.png ├── sftp.png ├── waf-fw.png ├── ipsec-aa.png ├── ipsec-dr.png ├── elastic-san.png ├── frontdoor.png ├── ipsec-bgp.png ├── architecture.png ├── aoai-deployment.png ├── app-gateway-01.png ├── hl-architecture.png ├── routing-with-bgp.png ├── architecture-hub-01.png ├── architecture-hub-02.png ├── any-to-any-routing-avnm.png ├── routing-without-bgp-fw.png ├── any-to-any-routing-gateway.png ├── aoai-deployment-apim-view.png ├── architecture-on-premises-2.png ├── architecture-on-premises.png ├── cross-on-premise-routing.png ├── north-south-inspection-01.png ├── north-south-inspection-02.png ├── any-to-any-routing-firewall.png ├── hub-and-spoke-arc-drawing(s).vsdx ├── name-resolution-with-dns-resolver.png ├── aoai-deployment-apim-configuration.png ├── outbound-traffic-internet-firewall.png ├── cross-on-premise-routing.drawio ├── routing-with-bgp.drawio ├── waf-fw.drawio ├── routing-without-bgp-fw.drawio ├── dns.drawio └── elastic-san.drawio ├── CODE_OF_CONDUCT.md ├── scenarios ├── web.md ├── dns.md ├── vnet-to-vnet.md ├── dnat-01-02.md ├── vnet-to-vnet-2.md ├── spoke-01-inet.md ├── ipsec.md ├── logs.md ├── ping-any-to-any-avnm.md ├── ipsec-bgp.md ├── solution-north-south-inspection.md ├── name-resolution-with-azure-firewall.md ├── network-watcher.md ├── ipsec-active-active.md ├── ping-any-to-any-gateway.md ├── ipsec-multiple-vpn-device.md ├── sftp.md ├── routing-with-bgp.md ├── ipsec-dual-redundancy.md ├── cross-on-premise-routing.md ├── routing-without-bgp-fw.md ├── outbound-traffic-to-internet-firewall.md ├── elastic-san.md ├── ping-any-to-any-firewall.md ├── p2s-vpn-certificate.md ├── frontdoor.md ├── name-resolution-with-dns-private-resolver.md ├── publish-waf-fw.md ├── p2s-vpn-certificate-always-on.md ├── app-gateway-01.md └── apim-stv1.md ├── .gitignore ├── LICENSE ├── CONTRIBUTING ├── s2s-vpn-bicep ├── connect-on-prem.bicep ├── connect-gateways-bgp-to-be-verified.bicep └── connect-on-prem.json ├── any-to-any-bicep ├── fw-policy.bicep └── any-to-any.bicep ├── on-prem-bicep ├── on-prem.bicep └── on-prem.json └── on-prem-2-bicep └── on-prem-2.bicep /.github/funding.yml: -------------------------------------------------------------------------------- 1 | buy_me_a_coffee: nicolcoffee 2 | -------------------------------------------------------------------------------- /hub-01-bicep/bicepconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "experimentalFeaturesEnabled": { 3 | } 4 | } -------------------------------------------------------------------------------- /images/dns.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/dns.png -------------------------------------------------------------------------------- /images/ipsec.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/ipsec.png -------------------------------------------------------------------------------- /images/sftp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/sftp.png -------------------------------------------------------------------------------- /images/waf-fw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/waf-fw.png -------------------------------------------------------------------------------- /images/ipsec-aa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/ipsec-aa.png -------------------------------------------------------------------------------- /images/ipsec-dr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/ipsec-dr.png -------------------------------------------------------------------------------- /images/elastic-san.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/elastic-san.png -------------------------------------------------------------------------------- /images/frontdoor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/frontdoor.png -------------------------------------------------------------------------------- /images/ipsec-bgp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/ipsec-bgp.png -------------------------------------------------------------------------------- /images/architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/architecture.png -------------------------------------------------------------------------------- /images/aoai-deployment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/aoai-deployment.png -------------------------------------------------------------------------------- /images/app-gateway-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/app-gateway-01.png -------------------------------------------------------------------------------- /images/hl-architecture.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/hl-architecture.png -------------------------------------------------------------------------------- /images/routing-with-bgp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/routing-with-bgp.png -------------------------------------------------------------------------------- /images/architecture-hub-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/architecture-hub-01.png -------------------------------------------------------------------------------- /images/architecture-hub-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/architecture-hub-02.png -------------------------------------------------------------------------------- /images/any-to-any-routing-avnm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/any-to-any-routing-avnm.png -------------------------------------------------------------------------------- /images/routing-without-bgp-fw.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/routing-without-bgp-fw.png -------------------------------------------------------------------------------- /images/any-to-any-routing-gateway.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/any-to-any-routing-gateway.png -------------------------------------------------------------------------------- /images/aoai-deployment-apim-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/aoai-deployment-apim-view.png -------------------------------------------------------------------------------- /images/architecture-on-premises-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/architecture-on-premises-2.png -------------------------------------------------------------------------------- /images/architecture-on-premises.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/architecture-on-premises.png -------------------------------------------------------------------------------- /images/cross-on-premise-routing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/cross-on-premise-routing.png -------------------------------------------------------------------------------- /images/north-south-inspection-01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/north-south-inspection-01.png -------------------------------------------------------------------------------- /images/north-south-inspection-02.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/north-south-inspection-02.png -------------------------------------------------------------------------------- /images/any-to-any-routing-firewall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/any-to-any-routing-firewall.png -------------------------------------------------------------------------------- /images/hub-and-spoke-arc-drawing(s).vsdx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/hub-and-spoke-arc-drawing(s).vsdx -------------------------------------------------------------------------------- /images/name-resolution-with-dns-resolver.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/name-resolution-with-dns-resolver.png -------------------------------------------------------------------------------- /images/aoai-deployment-apim-configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/aoai-deployment-apim-configuration.png -------------------------------------------------------------------------------- /images/outbound-traffic-internet-firewall.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nicolgit/hub-and-spoke-playground/HEAD/images/outbound-traffic-internet-firewall.png -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 2 | 3 | Resources: 4 | 5 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 6 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 7 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns -------------------------------------------------------------------------------- /scenarios/web.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: 2 | 3 | # Install a web server on spoke-03-vm 4 | 5 | ``` 6 | sudo apt-get update 7 | sudo apt-get upgrade 8 | sudo apt install nginx -y 9 | 10 | sudo rm /usr/share/nginx/html/index.html 11 | sudo git clone https://github.com/nicolgit/html-resume /usr/share/nginx/html 12 | ``` 13 | 14 | 15 | to check web server status 16 | 17 | ``` 18 | systemctl status nginx.service 19 | ``` 20 | 21 | ## web test pages 22 | 23 | * http://**web-server-url**/ 24 | * http://**web-server-url**/resume/resume.html 25 | 26 | or from the machine itself 27 | 28 | ``` 29 | wget spoke-03-vm -O /dev/stdout 30 | ``` 31 | cd 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local .terraform directories 2 | **/.terraform/* 3 | 4 | # .tfstate files 5 | *.tfstate 6 | *.tfstate.* 7 | 8 | # temp visio files 9 | *.~vsdx 10 | 11 | # Crash log files 12 | crash.log 13 | 14 | # Ignore any .tfvars files that are generated automatically for each Terraform run. Most 15 | # .tfvars files are managed as part of configuration and so should be included in 16 | # version control. 17 | # 18 | # example.tfvars 19 | 20 | # Ignore override files as they are usually used to override resources locally and so 21 | # are not checked in 22 | override.tf 23 | override.tf.json 24 | *_override.tf 25 | *_override.tf.json 26 | 27 | # Include override files you do wish to add to version control using negated pattern 28 | # 29 | # !example_override.tf 30 | 31 | # Include tfplan files to ignore the plan output of command: terraform plan -out=tfplan 32 | # example: *tfplan* 33 | 34 | *.bkp 35 | *.dtmp 36 | *.vscode 37 | 38 | /.vs -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 NicolD 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /scenarios/dns.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: configure a DNS on the cloud, so that all machines are reachable via FQDN 2 | 3 | ## Pre-requisites 4 | 5 | In order to apply this solution you have to deploy hub playground only. 6 | 7 | ## Solution 8 | 9 | From Azure Portal create a Private DNS zone named `cloudasset.interal`, with the following Network links: 10 | 11 | | Name | Network name | 12 | |---|---| 13 | |hub | hub-lab-net | 14 | |spoke01 | spoke-01 | 15 | |spoke02 | spoke-02 | 16 | |spoke03 | spoke-03 | 17 | 18 | Enable `auto-registration` on all network. The result is: 19 | 20 | | Link Name | Link status | Virtual Network | Auto-registration | 21 | |---|---|---|---| 22 | | hub | Completed | hub-lab-net | Enabled | 23 | | spoke01 | Completed | spoke-01 | Enabled | 24 | | spoke02 | Completed | spoke-02 | Enabled | 25 | | spoke03 | Completed | spoke-03 | Enabled | 26 | 27 | in order to have an additiona ALIAS for `spoke-01-vm` add the following record: 28 | 29 | Name: vm01.spoke01[.cloudasset.internal] 30 | Type: A 31 | IP: `10.13.1.4` 32 | 33 | ## Test solution 34 | Connect via RDP from hub-vm-01 machine to spoke-01 VM using the following Names: 35 | * spoke-01-vm.cloudasset.internal 36 | * vm01.spoke01.cloudasset.intenal -------------------------------------------------------------------------------- /scenarios/vnet-to-vnet.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: connect on-prem and hub with a VNet-to-VNet connection 2 | 3 | ## Pre-requisites 4 | In order to apply this solution you have to deploy `hub` and `on-premise` playgrounds. 5 | 6 | ## Solution 7 | in order to make this connection, you have to create 2 connections one from on-prem to cloud and another from cloud to onprem 8 | 9 | ### Step 1: connection onprem-to-cloud 10 | Open `on-prem-gateway`, go to Connections and add the following object 11 | * Connection Name: onprem-to-cloud 12 | * Type: VNet-to-VNet 13 | * First virtual Network Gateway: `on-prem-gateway` 14 | * Second virtual Network Gateway: `lab-gateway` 15 | * Shared Key: `password.123` 16 | * IKE: IKEv2 17 | 18 | ### Step 2: connection cloud-to-onprem 19 | Open `lab-gateway`, go to Connections and add the following object 20 | * Connection Name: cloud-to-onprem 21 | * Type: VNet-to-VNet 22 | * First virtual Network Gateway: `lab-gateway` 23 | * Second virtual Network Gateway: `on-prem-gateway` 24 | * Shared Key: `password.123` 25 | * IKE: IKEv2 26 | 27 | after a couple of minutes you will have the following connections: 28 | 29 | | Name | Status | Connection Type | Peer | 30 | |---|---|---|---| 31 | |cloud-to-onprem | connected |VNet-toVNet| lab-gateway | 32 | |onprem-to-cloud | connected |VNet-toVNet| lab-gateway | 33 | 34 | ## Test solution 35 | Via bastion go to W10onprem (192.168.1.4) and from there open RDP to hub-vm-01 (10.12.1.4). 36 | Ddthe same in the opposite direction -------------------------------------------------------------------------------- /scenarios/dnat-01-02.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: DNAT - expose on public IP, via RDP (port 3389) machines spoke-01-vm and spoke-02-vm 2 | 3 | ## Pre-requisites 4 | 5 | In order to apply this solution you have to deploy hub playground only. 6 | 7 | ## Solution 8 | When you install Azure Firewall, it has only one public IP. In order to access via Remote Virtual Desktop, on the same port 2 machines, as first step an additional public IP is required. 9 | 10 | So, from Azure Portal: 11 | * create a new public IP. 12 | * in `lab-firewall` select Public IP Configuration 13 | * add a public IP configuration 14 | * take note of the 2 public IPs 15 | 16 | | Name | Public IP | 17 | |--------|-----------| 18 | |IpConf | `x.x.x.x` | 19 | |IpConf2 | `y.y.y.y` | 20 | 21 | Create the following Firewall Policy: `hub-fw-policy` 22 | 23 | **DNAT rules** 24 | 25 | |priority | collection name | rule name | src | port | protocol | destination | translated address| translated port | action | 26 | |---|---|---|---|---|---|---|---|---|---| 27 | |1000 | coll01 |rdp-spk-01 | * | 3389 | TCP | `x.x.x.x` | 10.13.1.4 | 3389 | Dnat | 28 | |1000 | coll01 |rdp-spk-02 | * | 3389 | TCP | `y.y.y.y` | 10.13.2.4 | 3389 | Dnat | 29 | 30 | Associate the policy `hub-fw-policy` to `lab-firewall` via Firewall Manager 31 | 32 | ## Test Solution 33 | 34 | Test connection using RRP client from a machine on internet to `x.x.x.x` and `y.y.y.y`. 35 | -------------------------------------------------------------------------------- /scenarios/vnet-to-vnet-2.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: connect on-prem-2 and hub with a VNet-to-VNet connection 2 | 3 | ## Pre-requisites 4 | In order to apply this solution you have to deploy `hub` and `on-premise-2` playgrounds. 5 | 6 | ## Solution 7 | In order to make this connection, you have to create 2 connections one from on-prem to cloud and another from cloud to onprem 8 | 9 | ### Step 1: connection onprem-to-cloud 10 | Open `on-prem-2-gateway`, go to Connections and add the following object 11 | * Connection Name: `onprem2-to-cloud` 12 | * Type: VNet-to-VNet 13 | * First virtual Network Gateway: `on-prem-2-gateway` 14 | * Second virtual Network Gateway: `lab-gateway` 15 | * Shared Key: `password.123` 16 | * IKE: IKEv2 17 | 18 | ### Step 2: connection cloud-to-onprem 19 | Open `lab-gateway`, go to Connections and add the following object 20 | * Connection Name: `cloud-to-onprem2` 21 | * Type: VNet-to-VNet 22 | * First virtual Network Gateway: `lab-gateway` 23 | * Second virtual Network Gateway: `on-prem-gateway` 24 | * Shared Key: `password.123` 25 | * IKE: IKEv2 26 | 27 | after a couple of minutes you will have the following connections: 28 | 29 | | Name | Status | Connection Type | Peer | 30 | |---|---|---|---| 31 | |cloud-to-onprem2 | connected |VNet-toVNet| lab-gateway | 32 | |onprem2-to-cloud | connected |VNet-toVNet| lab-gateway | 33 | 34 | ## Test solution 35 | Via bastion go to W10onprem (192.168.1.4) and from there open RDP to hub-vm-01 (10.12.1.4). 36 | Ddthe same in the opposite direction -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | # Contributing to Transcriptase 2 | We love your input! We want to make contributing to this project as easy and transparent as possible, whether it's: 3 | 4 | - Reporting a bug 5 | - Discussing the current state of the code 6 | - Submitting a fix 7 | - Proposing new features 8 | - Becoming a maintainer 9 | 10 | ## We Develop with Github 11 | We use github to host code, to track issues and feature requests, as well as accept pull requests. 12 | 13 | ## All Code Changes Happen Through Pull Requests 14 | Pull requests are the best way to propose changes to the codebase (we use [Github Flow](https://docs.github.com/en/get-started/quickstart/github-flow). We actively welcome your pull requests: 15 | 16 | 1. Fork the repo and create your branch from `master`. 17 | 2. update where needed. 18 | 3. Issue that pull request! 19 | 20 | ## Any contributions you make will be under the MIT Software License 21 | In short, when you submit code changes, your submissions are understood to be under the same [MIT License](http://choosealicense.com/licenses/mit/) that covers the project. Feel free to contact the maintainers if that's a concern. 22 | 23 | ## Report bugs using Github's issues 24 | We use GitHub issues to track public bugs. Report a bug by opening a new issue; it's that easy! 25 | 26 | ## Write bug reports with detail, background, and sample code 27 | **Great Bug Reports** tend to have: 28 | 29 | - A quick summary and/or background 30 | - Steps to reproduce 31 | - Be specific! 32 | - What you expected would happen 33 | - What actually happens 34 | - Notes (possibly including why you think this might be happening, or stuff you tried that didn't work) 35 | 36 | ## License 37 | By contributing, you agree that your contributions will be licensed under its MIT License. 38 | -------------------------------------------------------------------------------- /s2s-vpn-bicep/connect-on-prem.bicep: -------------------------------------------------------------------------------- 1 | @description('Select the on-premises location to connect with the hub') 2 | @allowed([ 'on-prem-gateway', 'on-prem-2-gateway' ]) 3 | param gatewayOnPremName string = 'on-prem-gateway' 4 | param gatewayOnPremLocation string = 'francecentral' 5 | param gatewayOnPremResourceGroup string = 'hub-and-spoke-playground' 6 | 7 | param hubGatewayName string = 'lab-gateway' 8 | param hubGatewayResourceGroup string = 'hub-and-spoke-playground' 9 | param hubGatewayLocation string = 'westeurope' 10 | 11 | @secure() 12 | param psk string = 'pAsSwOrD.123' 13 | param enableBgp bool = false 14 | 15 | resource vnetGateway1 'Microsoft.Network/virtualNetworkGateways@2019-09-01' existing = { 16 | name: gatewayOnPremName 17 | scope: resourceGroup(gatewayOnPremResourceGroup) 18 | } 19 | 20 | resource vnetGateway2 'Microsoft.Network/virtualNetworkGateways@2019-09-01' existing = { 21 | name: hubGatewayName 22 | scope: resourceGroup(hubGatewayResourceGroup) 23 | } 24 | 25 | resource connection1 'Microsoft.Network/connections@2022-07-01' = { 26 | name: '${gatewayOnPremName}-to-cloud' 27 | location: gatewayOnPremLocation 28 | properties: { 29 | connectionType: 'Vnet2Vnet' 30 | virtualNetworkGateway1: { 31 | id: vnetGateway1.id 32 | properties: {} 33 | } 34 | virtualNetworkGateway2: { 35 | id: vnetGateway2.id 36 | properties: {} 37 | } 38 | sharedKey: psk 39 | connectionProtocol: 'IKEv2' 40 | enableBgp: enableBgp 41 | } 42 | } 43 | 44 | resource connection2 'Microsoft.Network/connections@2022-07-01' = { 45 | name: 'cloud-to-${gatewayOnPremName}' 46 | location: hubGatewayLocation 47 | properties: { 48 | connectionType: 'Vnet2Vnet' 49 | virtualNetworkGateway1: { 50 | id: vnetGateway2.id 51 | properties: {} 52 | } 53 | virtualNetworkGateway2: { 54 | id: vnetGateway1.id 55 | properties: {} 56 | } 57 | sharedKey: psk 58 | connectionProtocol: 'IKEv2' 59 | enableBgp: enableBgp 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /scenarios/spoke-01-inet.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: filter internet traffic and spokes connectivity 2 | 3 | ## Pre-requisites 4 | 5 | In order to apply this solution you have to deploy hub playground only. 6 | 7 | ## Solution 8 | This configuration allows `spoke-01` to: 9 | * communicate with `spoke-02` 10 | * allow HTTP/S internet traffic avoiding access to `*.google.com` and `*.microsoft.com` 11 | 12 | 13 | Create the following route tables in `west europe`: 14 | 15 | Route: `route-01` 16 | 17 | | Name | Address Prefix | Next hop type | Next hop IP addr | 18 | |---|---|---|---| 19 | | to-all | 0.0.0.0/0 | Virtual Appliance | 10.12.3.4 | 20 | 21 | | subnet Name | Virtual Network | 22 | |---|---| 23 | | default | spoke-01 | 24 | 25 | Route: `route-02` 26 | 27 | | Name | Address Prefix | Next hop type | Next hop IP addr | 28 | |---|---|---|---| 29 | | to-spoke-02 | 10.13.1.0/24 | Virtual Appliance | 10.12.3.4 | 30 | 31 | | subnet Name | Virtual Network | 32 | |---|---| 33 | | default | spoke-02 | 34 | 35 | 36 | Create the following `IP Groups` in `west europe`: 37 | * `group-spoke-01`: 10.13.1.0/24 38 | * `group-spoke-02`: 10.13.2.0/24 39 | 40 | Create the following Firewall Policy: `internet-policy` 41 | 42 | **Network Rules**: 43 | 44 | | priority | collection name | rule name | source | port | protocol | destination | Action | 45 | |---|---|---|---|---|---|---|---| 46 | | 1000 | net-coll-01| to-spoke-02 | group-spoke-01 | * | Any | group-spoke-02 | Allow | 47 | | 1000 | net-coll-01| to-spoke-01 | group-spoke-02 | * | Any | group-spoke-01 | Allow | 48 | 49 | **Application Rules**: 50 | 51 | | priority | collection name | rule name | source | protocol | destination| action | 52 | |---|---|---|---|---|---|---| 53 | |1000|filtered-collection| no-goog-msft| * | HTTP,HTTPS | *.google.com,
*.microsoft.com | Deny | 54 | |2000|allow-inet-coll| allow-inet| * | HTTP,HTTPS | * | Allow | 55 | 56 | Associate the policy `hub-fw-policy` to `lab-firewall` via Firewall Manager. 57 | 58 | ## Test solution 59 | * Verify mutual reachability via remote desktop client 60 | 61 | * Open browser and verify that google.com and microsoft.com are not available. -------------------------------------------------------------------------------- /images/cross-on-premise-routing.drawio: -------------------------------------------------------------------------------- 1 | 7Vpdb6M4FP01eSzCX0Aem3ba3dXOqFKlncfKCS6xBnDWmEk6v36vA04Ak7aZJp2RtnkJvtc2cM+5x9dOJuSq2Nxqvlp+VqnIJzhMNxNyPcEYoRDDl7U8tZYQs8aSaZm2tr3hXv4QrmNrrWUqql5Ho1Ru5KpvXKiyFAvTs3Gt1brf7VHl/buueCY8w/2C5771q0zNsrEmON7b/xAyW7o7o2jaeAruOrdvUi15qtYdE/k0IVdaKdNcFZsrkdvoubg0424OeHcPpkVpXjPgdnZ5w/5iOImJ+co/b/LrfxcX7SzfeV63L7zIVZ1OcJTDpLO5hqvMXi3r+UXO577jGYszoDBAOAgDeL4bFPkDrJ8EaOvH9JAfv+CnAz805vtH2iJgnhysEBpgEDRmAMvKGpv3JrP1Uhpxv+ILa1wDq+3LmyKHFtrN1I26C6HQRmw6phaFW6EKYfQTdGm9JGwZ0eYEWJr2ek8wzNo+yw65sBvIW1Jnu7n3uMNFC/0RNKAeDSCmOOxgdqr42TBJSK/LXGYl2ObKGFXYeWCILLNZ277GCRgfVWnu27uOEP7o0Cf9yEcYByTyg49Ggo/icwWfecH3M2iKAxQlXg6dh+OvxoiQ02QEo9GrcMHvigv2cFHlxUqLwlegG81LCOYQgZWSpdk+FptN2DV05NWqWaUe5Uak/bDzNtwLCKTQDWzNihFCI+XV0o7YNmSxXbbc97UsMnjFXAIZbjjALtIHI3JRycoypJZ5CrDZ688ilXXxMKsrWYqqgovGF1Tfs9NgOUgxxixzPSgZDSIfyxiEfNr9nAlZcgjZC+xjeyt0wcunD3C3AA3QpeFvBm10ENqMG7HmTz7Al/dfJuQyYgwR3zm7vbNBE4AZDv+8g47w3SyOUJKAdEe8sPJZzqvVmAir2uSAxtWuPPXgdmr7N5+L/E5V0kjVU92hHBu1GiNUh2xO74tNZsvygK8rEizqCuYT+sEFApZXmedXKld6+6zkhiWMULDDqFTCvM5XqlKchkA7tXZKT3wCkbHSJw4ieibKxB5loNB9iS4wwDJmpBo9xBgogSkwhnoUeU42RkRmiLuTi22t1OyfED4kHz9qLWy4SmHWSn8DcYDGP1KbmucPXxrjw23z6tXpdIPiPuwkGYM9QNRHnp5reZ8eXgReoxUjK8U48m3pRkax/5CHHk8iOizQ4yDBPlVQQHymEHwmpiCfKh6QmVa1jbtWhrcIJdOjgrI7w+BzN2n4bLBYCNuBkCaMxUmM43jaL6JpPO7uxBEx4CULcZJQICel4UhUERRw3T7nivHYLmi4oSnTS3uks93J8KqSiz6vAYIy3SXOgL2PjyJa2AGV0eqb6HjSeDoP7YjcpthM6VToF6n9LFFE2jtU8hHslksjq52zaZEDmb73j6LGQt/e4c6uFp3Ftp9MA+gqVeuFaId0z42GszDcmwbRwYMYrjNhvIm2NNi98xuY4Zd0IGAacMbhN/HUqKy3Y14BP2AxS2HZJa/aLUMymoFM9ojSMqHLqtbkSe1QkQuZpvY2o/vuPmk78kHp8cQ7YpvWp8bU14ZohJjkXIsx8ouwj/R/a/o7sd4lbvJzCjCY5p3TP/lI/9On/8VgbYj9I+j3FQC3QXi5xjpfQYVfKKgiuwntuNlJCirX5/Sa+oqQ/vaa6njxm2gqC1gMu36KEY6BL+6Xm53C0gB1/ewnSy46vE+fiP2buMPz9xJk/zfD/4UgX1B2PG2PUWQMW92OuiR9btlz9DHUf13J5p+if8jLW0s2QPmZxEcHOHC0vgzvMpAxFj4rY+cWGP/X6A+BebvAINQH1W25XAU4jvkZ5AWa+3+9NKTZ/3mIfPoP -------------------------------------------------------------------------------- /s2s-vpn-bicep/connect-gateways-bgp-to-be-verified.bicep: -------------------------------------------------------------------------------- 1 | param vnetGateway1Name string = 'on-prem-gateway' 2 | param vnetGateway1Rg string = 'rg-playground-onprem' 3 | param vnetGateway2Name string = 'lab-gateway' 4 | param vnetGateway2Rg string = 'rg-playground-hub' 5 | param gateway1Location string = 'francecentral' 6 | param gateway2Location string = 'westeurope' 7 | @secure() 8 | param psk string = 'password.123' 9 | 10 | resource vnetGateway1 'Microsoft.Network/virtualNetworkGateways@2019-09-01' existing = { 11 | name: vnetGateway1Name 12 | scope: resourceGroup(vnetGateway1Rg) 13 | } 14 | 15 | resource vnetGateway2 'Microsoft.Network/virtualNetworkGateways@2019-09-01' existing = { 16 | name: vnetGateway2Name 17 | scope: resourceGroup(vnetGateway2Rg) 18 | } 19 | 20 | // represents azure networks 21 | resource localGateway1 'Microsoft.Network/localNetworkGateways@2022-09-01' existing = { 22 | name: 'lab-local-gateway' 23 | scope: resourceGroup(vnetGateway1Rg) 24 | } 25 | 26 | // represents onprem networks 27 | resource localGateway2 'Microsoft.Network/localNetworkGateways@2022-09-01' existing = { 28 | name: 'lab-local-gateway' 29 | scope: resourceGroup(vnetGateway2Rg) 30 | } 31 | 32 | resource connection1 'Microsoft.Network/connections@2022-07-01' = { 33 | name: 'onprem-to-cloud' 34 | location: gateway1Location 35 | properties: { 36 | connectionType: 'IPsec' 37 | virtualNetworkGateway1: { 38 | id: vnetGateway1.id 39 | properties: {} 40 | } 41 | localNetworkGateway2: { 42 | id: localGateway1.id 43 | properties: {} 44 | } 45 | sharedKey: psk 46 | connectionProtocol: 'IKEv2' 47 | enableBgp: true 48 | } 49 | } 50 | 51 | resource connection2 'Microsoft.Network/connections@2022-07-01' = { 52 | name: 'cloud-to-onprem' 53 | location: gateway2Location 54 | properties: { 55 | connectionType: 'IPsec' 56 | virtualNetworkGateway1: { 57 | id: vnetGateway2.id 58 | properties: {} 59 | } 60 | localNetworkGateway2: { 61 | id: localGateway2.id 62 | properties: {} 63 | } 64 | sharedKey: psk 65 | connectionProtocol: 'IKEv2' 66 | enableBgp: true 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /scenarios/ipsec.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: connect on-prem and hub with a Site-to-Site IPSec connection 2 | 3 | In this solution I show how to configure an IPSec tunnel between an on-premises (simulated on Azure) site and a hub and spoke, using static routing without BGP protocol. 4 | 5 | Using static routing instead of BGP in this IPSec connection provides several advantages in certain scenarios like: 6 | 7 | - **Simplicity**: Static routing is easier to understand and configure, making it ideal for simple network topologies 8 | - **Predictability**: Routes are explicitly defined and don't change automatically, providing complete control over traffic flow 9 | - **Lower Resource Usage**: No BGP protocol overhead, resulting in slightly lower CPU and memory usage on network devices 10 | - **Better for Small Networks**: Ideal for environments with few subnets and stable network topology 11 | 12 | ## Pre-requisites 13 | 14 | In order to apply this solution you have to deploy `hub` and `on-premises` playgrounds. 15 | 16 | ## Solution 17 | 18 | In order to make this connection, you have to create 2 connections: one from on-prem to cloud and another from cloud to on-prem. 19 | 20 | ![ipsec schema](/images/ipsec.png) 21 | 22 | # Create Local Network Gateways 23 | Create the following gateways: 24 | 25 | | Name | IP Address | Address Space | Region | 26 | |---|---|---|---| 27 | |`cloud-net` | `lab-gateway-ip` (hub gateway public ip) | 10.0.0.0/8| West Europe | 28 | |`onprem-net`| `onprem-gateway-virtualip` (onprem gateway public ip) | 192.168.0.0/16 | France Central | 29 | 30 | # Connection onprem-to-cloud 31 | Open `on-prem-gateway`, go to Connections and add the following object: 32 | * Connection Name: `onprem-to-cloud` 33 | * Type: Site-to-Site (IPsec) 34 | * Virtual Network Gateway: `on-prem-gateway` 35 | * Local Network Gateway: `cloud-net` 36 | * Shared Key: `password.123` 37 | * IKE: IKEv2 38 | 39 | 40 | # Connection cloud-to-onprem 41 | Open `lab-gateway`, go to Connections and add the following object: 42 | * Connection Name: `cloud-to-onprem` 43 | * Type: Site-to-Site (IPsec) 44 | * Virtual Network Gateway: `lab-gateway` 45 | * Local Network Gateway: `onprem-net` 46 | * Shared Key: `password.123` 47 | * IKE: IKEv2 48 | 49 | After **few minutes**, you will see, on `on-prem-gateway` connections: 50 | 51 | | Name | Status | Connection Type | Peer | 52 | |---|---|---|---| 53 | |onprem-to-cloud | connected |Site-to-Site (IPsec)| cloud-net| 54 | 55 | and on `lab-gateway` connections: 56 | 57 | | Name | Status | Connection Type | Peer | 58 | |---|---|---|---| 59 | |cloud-to-onprem | connected |Site-to-Site (IPsec)| onprem-net | 60 | 61 | ## Test solution 62 | Via Bastion go to `W11-onprem` (`192.168.1.4`) and open an RDP connection to hub-vm-01 (`10.12.1.4`). 63 | 64 | Do the same test also in the opposite direction. -------------------------------------------------------------------------------- /scenarios/logs.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: troubleshooting firewall rules with log Analytics 2 | 3 | ## Pre-requisites 4 | 5 | In order to apply this solution you have to deploy hub playground only. 6 | 7 | ## Solution 8 | First step is to create a Log Analytics Workspace named `playground-workspace` in `west-europe`. 9 | In order to analyze firewall logs go to `lab-firewall` -> Diagnostic Settings -> Add Diagnostic Settings: 10 | * Name: firewall-diagnostics 11 | * Logs: Category Group -> All logs 12 | * Metrics: All Metrics 13 | * Destination details: Send to Log Analytics Workspace -> `playground-workspace` 14 | * SAVE 15 | 16 | Prepare routing and firewall rules as described in [ping-any-to-any-firewall](./ping-any-to-any-firewall.md): when all these steps will be completed each machine will be able to communicate with any other via Firewall. 17 | 18 | ## Test solution 19 | Connect via RDP from `spoke-01-vm` to `spoke-02-vm` (`10.13.2.4`). 20 | After that the connection is estabilished, go to `lab-firewall` -> Logs -> click on `Run` button in **Network Rule Log Data** Box. 21 | 22 | In the Result pane, the first row should be something like: 23 | 24 | | Time | msg | Protocol | SourceIP | srcport| TargetIp | target port | Action | 25 | |---|---|----|---|---|---|---|---| 26 | |...|...|TCP|10.13.1.4|xxx|10.13.2.4|3389|**Allow**| 27 | 28 | > Log ingestion from Azure Firewall can require from 60 seconds to 15 minutes, so if nothing is displayed, wait some minute and try again. 29 | 30 | now to test the **deny** logging, change the firewall network policy as follow: 31 | 32 | | rule name | source | port | protocol | destination | 33 | |---|---|---|---|---| 34 | | all-to-all | group-spoke-01
group-spoke-03 | * | Any | group-spoke-01
group-spoke-02
group-spoke-03 | 35 | 36 | go back to `spoke-01-vm` and repeat the RDP connection. The connection will be **allowed**. This is **correct** because we have denied the traffic that starts from spoke-02 to any other spokes, but because our connection begins from spoke-01 it will be still possible. 37 | 38 | Now change the firewall network policy again as follow: 39 | 40 | | rule name | source | port | protocol | destination | 41 | |---|---|---|---|---| 42 | | all-to-all | group-spoke-02
group-spoke-03 | * | Any | group-spoke-01
group-spoke-02
group-spoke-03 | 43 | 44 | go back to `spoke-01-vm` and repeat the RDP connection. The connection will be **denied**. Infact now we have denied all the connections that begin from spoke-01. 45 | 46 | Now go to `lab-firewall` -> Logs -> click on `Run` button in **Network Rule Log Data** Box. 47 | 48 | In the Result pane, the first row should be something like: 49 | 50 | | Time | msg | Protocol | SourceIP | srcport| TargetIp | target port | Action | 51 | |---|---|----|---|---|---|---|---| 52 | |...|...|TCP|10.13.1.4|xxx|10.13.2.4|3389|**Deny**| 53 | -------------------------------------------------------------------------------- /scenarios/ping-any-to-any-avnm.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: Allows machines in ANY spoke to communicate with ANY machine in ANY other spoke (Azure Virtual Network Manager) 2 | 3 | Azure Virtual Network Manager is a management service that enables you to group, configure, deploy, and manage virtual networks globally across subscriptions. With Virtual Network Manager, you can define network groups to identify and logically segment your virtual networks. Then you can determine the connectivity and security configurations you want and apply them across all the selected virtual networks in network groups at once. 4 | 5 | In the Hub and Spoke model, the **routing between different Spokes is a significant element**. Each spoke often identifies an application or a workload, and these enterprise elements have to interact with each other. In this scenario, Azure Virtual Network Manager (AVNM) can simplify and automate the deployment of connectivity between them. 6 | 7 | In this solution, I show you how configure the routing between spokes using the **Azure Vitual Network Manager**. 8 | 9 | The resulting overall architecture is shown in the following schema. 10 | 11 | ![any to any routing via direct peering](/images/any-to-any-routing-avnm.png) 12 | 13 | _Download a [draw.io file](../images/any-to-any-routing.drawio) of this architecture._ 14 | 15 | ## Pre-requisites 16 | 17 | In order to apply this solution you have to deploy hub playground only. 18 | 19 | ## Solution 20 | 21 | Go to Azure Portal > Virtual Network Manager > Create 22 | 23 | Basics 24 | * Region: `westeurope` 25 | * Name `avnm-lab` 26 | * Features: Connectivity 27 | 28 | Management Scopes 29 | 30 | * the subscription where you deployed the hub playground 31 | 32 | Click Create. 33 | 34 | Once the service is deployed, we can deploy the connectivity between all spokes: 35 | 36 | Go to Azure Portal > Virtual Networks Manager > `avnm-lab` > Network groups > Create 37 | * Name `all-spokes` 38 | * click Create 39 | 40 | Once created go to `avnm-lab` > Network Groups > `all-spokes` > Add Virtual Networks: 41 | * `spoke-01` 42 | * `spoke-02` 43 | * `spoke-03` 44 | * click add 45 | 46 | go to `avnm-lab` > Configurations > Create connectivity configuration: 47 | 48 | * Name: `hub-conn-configuration` 49 | * Topology: `Mesh` 50 | * Mesh connectivity between region: `Enable` 51 | * Network groups: `all-spokes` 52 | * click Create 53 | 54 | go to `avnm-lab` > Deployments > Deploy Configuration > Deploy Connectivity configuration: 55 | * Connectivity Configuration: `hub-conn-configuration` 56 | * Target Regions: `westeurope` and `northeurope` 57 | * click Deploy 58 | 59 | ## Test Solution 60 | Test connections using remote desktop client and ssh from one machine to another. 61 | 62 | ## More Information 63 | * Azure [Virtual Network Manager](https://learn.microsoft.com/en-us/azure/virtual-network-manager/overview) -------------------------------------------------------------------------------- /images/routing-with-bgp.drawio: -------------------------------------------------------------------------------- 1 | 7Vxtd6I6EP41ftQDCSB+rG3t3r1tt3ttu3v2S0/UqLkiuBCq9tffBCJvgfpSVHqX9pwWhiSEeZ6ZzEzQBrycr25ctJjeOSNsNYAyWjXgVQMAFUCV/eOSdSjpKEIwcclINIoFffKGhVARUp+MsJdqSB3HomSRFg4d28ZDmpIh13WW6WZjx0rfdYEmWBL0h8iSpT/IiE5DqQnasfwLJpPp5s6q0QmvzNGmsXgSb4pGzjIhgtcNeOk6Dg2P5qtLbHHlbfQS9usVXI0m5mKb7tJh/BV+N5yfs++3973eePxLt7rLpiYm94osXzzxCI+Rb1E+Y39gY3ZgsDPYHbjsaMKPVKWlwhZssb49oImno+uNylzHt0eY31VhjZdTQnF/gYb86pKRhMmmdG6xM5UdjpA3Ddryk7Fj00vHctxgIGgEP0zuUdeZ4dSVLuj1eA9iWQl5N/hlcofdkFDOOZ1P4hW7lDBQLywysZmQOnwasgaFUnlzvEqIhEZvsDPH1F2zJuKqqbRbethJEFzdqHQZ00XdcGCapMrGNJCg6CQaPUaRHQgg80H9rT+O/cXbw8vNTxtadDC8N56bUJNA9RZMgU1uezGYCcyM3z6nYQBA0wuM8II1UMFiFehpcz3NALUUBiQhtB0bZ0mBBGYWHtNCKN9hSJZTQu5xhtiTR979CsaC2+A2V2pb9BUuSQXlEEbTQYounRy2aJrMFqgrx2KLIbFFAhR5i9C5jsmKA5PEb+EQmwaT0rsN/SqB2JCpCTO9d8k88LKSPoX8iswnbOoWGbC/6M13MX8k5nqWjjtjkLCTZ+JSH1kv96HQa3mvkyxRLDTAVhcNZ5OAfxlOZXgQ/hzkW8pgwUbp640bkVgAdZkEQGl1Ps6C3IWgLZFg6g+aTKXNwiUAtJTAAajG/9YBlAA10NNQ5xm8cSyDz4XalKC+QRQv0br/znoP6vU+QLNtptDMhTN3tdeOBWendt+ndt9gA2Zl3Lcqx/Hcd09Cw5ZNunvzQBYiyAusW2sBXZPbXfTvw1aGrquy6XszTIdTYfknZNncGyLMefVw/yKcl6DUXiyKHE4JnIBq2s/DTXifIIWWQ4qjuXlVlSixW2oHSgrsP72rb6cAPW1ilw+p7Ot5lPY6z2R2aSwraLZiObhDwymxsTDdIoq8b9KlmG46J1NNTUJazwFaM46EM5C9uUjgIQO7uCBTQaTvXm6J7a/ODbGpqdk6zdlBlvPuHeJwXnVRgcnd8wfTrk/unjVDyyB61Fj8ycPut8G/3H6AEtA1ZZdqXqj1gQJbHJ8ZaM41Zg+8UHHh8CxXtYtvMEZzYq3DW6C3F4xeuLgB2EMp0YiiQx9PHGapytNf2Sthe9Y/7uytPYrnTZ+Ep020WFi4GUpDUdci9oz59X4g60U9/3EGDnXC42+r9QTb4fHTwLepHx5fIpsySnDTyZvptwXvpPSR7eVPNSv9gq1XzNnHrt1jZmS5vTw2XpOhS8aBSCmCSGHEpE3hFS8C5SOX5qIXGSpnZAgYo1OIWdiiJJqkwnNhQhwAwfPkNMBWf9RO2JtwYFVaSaoT6usKyLieIwb77DThe/ZZX+Q6z65bO3X8Hyww8KxbO/mgyhlAtAIVR4Z1DrAd7Y5ZtQARFmYBoNRtPFBv4+1NFwNsr+ofbRsvny1yuUcCtK4Dl1sHNtSq1YEhOCypBCKpVP/wrNKA2WWgAjs8jOBFC8HOFf4A4o5SlE8WVPxlF3L2sKE6aYDRNk6XBuzIFPnNn11j/nKigE9v/ud9nSsfVL3Y/ItjflDH/NsNOJvHnz/ml4vCm8p/qTF/OS9z/Fkxf7ujZ+hSgahffm+rjvqPHPW3zcpF/Qe90sW9QL2VFABYxaC/sNIH9wj64QFBP6xe3FCdoN/UKhf0a3J6+MxcLXgOLD8DJbZHF/xjMRwSC3keGQamiVwqixPApt1BRr+9XtdUlDwTj64Uah6PUp+/kfW+JRLbyFxsIUpecWrwPFWLOzxwpsawQinw17KW7Tm+O8SiXwyZNJSeKQpJAzFlTzCVBmL65yYdNRO2tM+UTWW/maXbs4NwDjEVIxw+wE45Ja3ZuSc7daiUxU6jA0/Czpwpb2GnNLOTsFPOrWt27snOtuTwMkPsyk1TyseloUpipzRlU913ZukeH2bn4y//64+78bV56/l/X3Q14+nLdVPe0uGve1KnGX+cbxeKFnKxmHPFLE2wXURbgaxH+KNdxXzNkDOHwGdb67VsuB8tjPsyVt41loYqYGxZFAESRaL3AfjrVgojzHskERAeiyEZNylIwm/D5vC5KQPLo0x2qCNTprCczCkTvFxQO5UDQrRsXniwUzHMrUMdmSGFRWgQMOTdZaf2KIfy5VCPksOXE3sUuQid4otcUKo9yi5hdXa74WCPYhpbhzoyQ+QCdVTM3Lrm1B7lUL4c6lFy+HJijyIXv8UX/BDH9iSyeFO04IdD37XWXRcNZ5hu37VIs+j9inKuw0qWsNWj+algYg+OR/izN6TN1NvM5TkZjbheosq7GzJN2j6JGgpVZ0vgO1G74BN0mikMPkH9vO0TQy9mefH78PF3JoV0i795Cl7/Bw== -------------------------------------------------------------------------------- /scenarios/ipsec-bgp.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: connect on-prem and hub with a Site-to-Site IPSec connection with BGP 2 | 3 | In this solution I show how to configure an IPSec tunnel between an on-premises (simulated on Azure) site and a hub and spoke, using BGP protocol. 4 | 5 | Using BGP (Border Gateway Protocol) in this IPSec connection provides several advantages like: 6 | 7 | - **Dynamic Routing**: BGP automatically exchanges routing information between on-premises and Azure, eliminating the need to manually configure static routes 8 | - **Simplified Management**: Reduces administrative overhead by automating route management and updates 9 | 10 | ## Pre-requisites 11 | 12 | In order to apply this solution you have to deploy `hub` and `on-premises` playgrounds. 13 | 14 | ## Solution 15 | 16 | In order to make this connection, you have to create 2 connections: one from on-prem to cloud and another from cloud to on-prem. 17 | 18 | ![ipsec schema](/images/ipsec-bgp.png) 19 | 20 | # Enable BGP on virtual network gateways 21 | Go to Azure Portal > Virtual network gateways > VPN Gateways > `lab-gateway` > Configuration > Configure BGP 22 | * ASN: `65050` 23 | * click SAVE 24 | 25 | Go to Azure Portal > Virtual network gateways > VPN Gateways > `onprem-gateway` > Configuration > Configure BGP 26 | * ASN: `65051` 27 | * click SAVE 28 | 29 | 30 | # Create Local Network Gateways 31 | Go to Azure Portal > Local Network Gateways and create the following gateways: 32 | 33 | | Name | pub ip | ip range | configure BGP | ASN | BGP peer IP | region | 34 | |---|---|---|---|---|---|---| 35 | |`cloud-net` | `lab-gateway-ip` (hub gateway public ip) | // | yes | 65050 | 10.12.4.254 | West Europe | 36 | |`onprem-net`| `onprem-gateway-virtualip` (onprem gateway public ip) | // | yes | 65051 | 192.168.3.254 | France Central | 37 | 38 | # Connection onprem-to-cloud 39 | Go to Azure Portal > Virtual Network Gateways > VPN Gateways > `on-prem-gateway` > Connections > Add: 40 | 41 | * Connection Name: `onprem-to-cloud` 42 | * Type: Site-to-Site (IPsec) 43 | * Virtual Network Gateway: `on-prem-gateway` 44 | * Local Network Gateway: `cloud-net` 45 | * Shared Key: `password.123` 46 | * IKE: IKEv2 47 | * Enable BGP: `true` 48 | 49 | 50 | # Connection cloud-to-onprem 51 | Go to Azure Portal > Virtual Network Gateways > VPN Gateways > `lab-gateway` > Connections > Add: 52 | 53 | * Connection Name: `cloud-to-onprem` 54 | * Type: Site-to-Site (IPsec) 55 | * Virtual Network Gateway: `lab-gateway` 56 | * Local Network Gateway: `onprem-net` 57 | * Shared Key: `password.123` 58 | * IKE: IKEv2 59 | * Enable BGP: `true` 60 | 61 | After **few minutes**, you will see, on `on-prem-gateway` connections: 62 | 63 | | Name | Status | Connection Type | Peer | 64 | |---|---|---|---| 65 | |onprem-to-cloud | connected |Site-to-Site (IPsec)| cloud-net| 66 | 67 | and on `lab-gateway` connections: 68 | 69 | | Name | Status | Connection Type | Peer | 70 | |---|---|---|---| 71 | |cloud-to-onprem | connected |Site-to-Site (IPsec)| onprem-net | 72 | 73 | ## Test solution 74 | Via Bastion go to `W11-onprem` (`192.168.1.4`) and open an RDP connection to hub-vm-01 (`10.12.1.4`). 75 | 76 | -------------------------------------------------------------------------------- /scenarios/solution-north-south-inspection.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: Use Azure Firewall for perimeter inspection (North/South Traffic) 2 | 3 | This solution shows how to inspect and eventually filter traffic with Azure Firewall between `spoke-01` and `on-prem-net` networks. 4 | 5 | ## Pre-requisites 6 | 7 | In order to apply this solution: 8 | 9 | 1. deploy **hub** playground 10 | 2. deploy **on-premise** playground 11 | 3. send firewall logs to a Log Analytics workspace, as [documented here](logs.md) 12 | 4. connect on-prem and hub with a Site-to-Site IPSec connection, as [documented here](ipsec.md) 13 | 14 | 15 | ## Solution 16 | When a S2S connection is established, routing between `on-prem-net` and `spoke-01` is possible thanks to the following configuration: 17 | 18 | ![north-south-01](../images/north-south-inspection-01.png) 19 | 20 | The configuration that allows to route all network traffic via Azure Firewall is the following: 21 | 22 | ![north-south-02](../images/north-south-inspection-02.png) 23 | 24 | ### Configure Firewall Policy 25 | With this policy Azure Firewall allows the communications bewteen `on-prem-net` and `spoke-01`. 26 | 27 | Create the following `IP Groups` in `west europe`: 28 | * `group-spoke-01`: 10.13.1.0/24 29 | * `group-on-prem`: 192.168.0.0/16 30 | 31 | Create the following Firewall Policy: `hub-fw-policy` 32 | 33 | **Network Rules**: 34 | * Rule Collection Name: `my-collection` 35 | * Rule Collection type: Network 36 | * Priority: `1000` 37 | * Rule Colletion action: `Allow` 38 | * Rule Collection group: `DefaultNetworkRuleCollectionGroup` 39 | 40 | 41 | | rule name | source | port | protocol | destination | 42 | |---|---|---|---|---| 43 | | on-prem-2-spoke-01 | group-on-prem | * | Any | group-spoke-01 | 44 | | spoke-01-2-on-prem | group-spoke-01 | * | Any | group-on-prem | 45 | 46 | Associate the policy `hub-fw-policy` to `lab-firewall` via Firewall Manager. 47 | 48 | ### Create User Defined Route for spoke-01 49 | Go to `spoke-01` -> peerings -> `spoke01-to-hub` and set Virtual network gateway to `None`. 50 | 51 | Create the following route table in `west europe`: `spokes-01-to-onprem` 52 | 53 | Propagate gateway routes: `NO` 54 | 55 | | Name | Address Prefix | Next hop type | Next hop IP addr | 56 | |---|---|---|---| 57 | | to-onprem | 192.168.0.0/16 | Virtual appliance | 10.12.3.4 | 58 | 59 | | subnet Name | Virtual Network | 60 | |---|---| 61 | | default | spoke-01 | 62 | 63 | ### Create User Defined Route for Azure Virtual Network Gateway's subnet 64 | Create the following route table in `west europe`: `gateway-routes` 65 | 66 | Propagate gateway routes: `NO` 67 | 68 | | Name | Address Prefix | Next hop type | Next hop IP addr | 69 | |---|---|---|---| 70 | | to-spoke-01 | 10.13.1.0/24 | Virtual Appliance | 10.12.3.4 | 71 | 72 | | subnet Name | Virtual Network | 73 | |---|---| 74 | | GatewaySubnet | hub-lab-net | 75 | 76 | ## Test Solution 77 | 78 | Connect via RDP from `w10-onprem` (`192.168.1.4`) to `spoke-01-vm` (`10.13.1.4`). 79 | After that the connection is estabilished, go to `lab-firewall` -> Logs -> click on `Run` button in **Network Rule Log Data** Box. 80 | 81 | In the Result pane, the first row should be something like: 82 | 83 | | Time | msg | Protocol | SourceIP | srcport| TargetIp | target port | Action | 84 | |---|---|----|---|---|---|---|---| 85 | |...|...|TCP|192.168.1.4|xxx|10.13.1.4|3389|**Allow**| 86 | |...|...|UDP|192.168.1.4|xxx|10.13.1.4|3389|**Allow**| 87 | 88 | > Log ingestion from Azure Firewall can require from 60 seconds to 15 minutes, so if nothing is displayed, wait some minute and try again. 89 | -------------------------------------------------------------------------------- /images/waf-fw.drawio: -------------------------------------------------------------------------------- 1 | 7V3tc6K6Gv9rnLn3gw4hgPixtnXPmdndcdve3bP3ixMlaqYIHMCq+9ffBBKEBBQtuvYeujNb80qS5/e85sF24P1q+ylEwfKL72C3o2vOtgMfOroOgGHRX6xml9bYcJBWLELi8E77imfyC/NKjdeuiYOjQsfY992YBMXKme95eBYX6lAY+ptit7nvFp8aoAVWKp5nyFVrfxAnXvJd6P19/R+YLJbiycDi+1sh0ZnvJFoix9/kquBjB96Hvh+nn1bbe+yywxPnko4bVbRmCwuxF9cZ8Oo92Z/Wo9HT3fO3b/Hj6mnz7aXLt/GG3DXf8HI97bpo2vVw3NEtl049nIZs+fGOn4n195qteTj3vbgbJRS7ox2AFmz3jfTTgv0OkUcPmLX2gN7TenSNI2A1MfPD1+dsXtgz+CFnc+mhv/YczDav0d6bJYnxc4BmrHVDsUrrlvHKpSXAnkhc9953/ZCWPd+jnYYOipbJcNaOXLLw6GcXz9mz33AYEwqSO14d+2y+KA79Vyym6ejQsob6aMQ3lKvXNON+MGAj6IKIt3hhwx/gvuJz8pgH0OdjOV8ALdm/THcOBbYovM1VcRx8wv4Kx+GOduGtusm5kjOlOeAY3ewhbgB+pMscvHWNd0ScrRbZ3Hvk0Q8cfCcA0VaA+AnFeIN2z+tpAsV3Ubfq+GWCZS15PND6YfKP1vuMPjE7NlOrxEETFLKKFLKMgUIhICRLnkIAggtRaKBQSCEKioJUBs/JltEmT4PAJ16crMkcdsyHHE/N6DFhetBDskqEcQHx+r7+gawWdOUumdL/0a91iNmOKDg2fvhKmYYWvpMwXiN38jWtjHrR20JmZSrcsDtEs9dFgiGJ6yWopD+HebsCKk2gwNCKfNo3FBRAs4xNe4MLoUDo5UMwiF5xPFty1rwiJlbRDGGGgvHXCZceHAAN0AJqRpEjy2RmCS3MS0lMABRKRAHFaHeRbr1p7U11bKK9dSM/c9qvVb7nK18IVSABo0T5gsHFoKQrULoLglYB56lkl1BJL+H3yylgAFsN/Ls1MNRVGFxbAxsKDEYkpJzquv9MVjW0Io1uwFYGpkKjakf10uaSdsRcumOMPBEY4gxbBYPDvNsENaHk+ZTYWWYJMS9nZ1kKLQOMw0TcSZTEnnPHYk6MIi6KIjJLGAeFsVqdo2uRP2szoDWz8XSuMqCDsD1nj6BECHd/sWl7pij+5M9MCg9b/sy0tBOlLYnTYaAPeZmN62o9DZi8Yj+WFfJDx/Rw6NEzPD4ctJkifx3O8IGj59EpeoALHNcwhbFTCOapGKuJoRC7KCZvxRBgGbD4E8aMMXOugm4XIGzaEjbTjfNR+ZCdPBGU/T9povRklIkSnGfbfgf01fCgg+do7bI1CmXTsC/w4dSPCeuon6tailC151PXUIPNe4Ww9QqbQBGQvUKooui6XiFU/Y2W+xW66bKfWEK3K3O/6iAoVGn9xGb9RFOT/UQVBlf2E6Hqg7R2ayN261F7VOjfG7FHTbvfs9jdHf8BRaNSViB1rdNsYGadwqtap1B1zISV031bNWHoCANH9dmFPvnMRNTYj0hMfCYhp34c+6sim5TrHkWmFvlECLkii2ja8HHEBG20RAFbymq7YHkXvUTI9t64UF2h2ZKUCcmabvwl7BsLFsBi6HbPVERkGRMYF7NvypMfsuuTWwvTBIFLYcSAJt1u/bZYDdSAIGJGVliTrBeL1wi8HDJ/cgqPn0ZB2/G6K6i65LEjwnbIFZpTKO0VoW3nVSGtyFRjlTZkpeZiMbBuMEYkeN2I8oOQKT8wyH7k1Jf+edoPWEXtZ8iAvrT2UzNmftyNbs/Q/4Gnk7zoEnHmydindQRHE7rs5q7pdckQN2BJykQmtfJ429c2r2nU3Jmcpun+Ob66sjmWQkEpNnnG4RuZ4cmf4zvHCXEUNUclAAYSlcrUBjR7fVBCqItpjqqMCk3VIG0oq5K4ln1zCQ6GGhBtQ1kK3QY3l/JgtCkPVw9lWX05lPXbUx4MNZTFBTNQ4NAK5krK2mbRWbsFwazGcFrBrNDNkuh2A4JZjZ20gvnCgjl7deqGBLPqhP5D7hhAjz68U7xj0EXFLeXGCKfmRsIx5uDQXYQlv19U/y5CSrmB172LMFQP/+MxgmEVYowU4idwQyKbBTfQuUz9ZGZoOGJZm0PMm+IQywY9y7QyDtGLwB6YPf08HulbMo9cLGIZTL/MR1rY343/o01enMmXH4Pv3Rq+XClvVDIBI9gzH+yH8dJf+B5yH/e1eXCDgV4MoEN7cE4EXeIjbAKWf1zCecNEyGUt4u3h5GI6i/KnDCbee4ZCEuTKBWulkhmOR+XhTYEcSElzBrDl6GtdWBuyvypnW1bAmgKN3fJl3bhJWrlkA8hLHhxel9TfsrWD/c139heR7j3Xpjs8l4f//v7oW6+j3eP4v8/xX6G/HP/81lUjpJQlY5YMeMZ9e3LRXnHlLnIK2bxoxRwp7vodeOP7ipey5Q7HEzuLyQuaujg6cjfbKTf+S5i7+vaV8ozI0OUYoDK+I7sE1Py3Sr2Cap6t6xOUQkSN1VwEIkYLkeMQMfvU2u5rFdb2LaClRhxB5LrE7NCKJCsL9eQVKKPNjB4vIl6iwEFSdl0URCSZLO2xJK7zGe0oacRjROnERJlLcrtIKhNfpKL1VeKJV0ubTrQoJZ3q+x8m3ROz64ZLPyS/GElEkoNMr2hDVi7y8B8YOVLV0HeEbZYE15JPaWw2+Siyr5JCyI9AK6W5E/rBizCaZLuOU7coEIb0HO9ZtICKBv2elsG+nEiLgNqg975HzT1EEppiFMUbHL0bRllwqRaMqtnsoLFXih3xrmHj2FHdZe7OVSGI7jAmyH2i0ht5izpygH8jENrTvITEp8HIp0c/dxP3ZEkcB3s3QFn7KGXtssjwAcLyyfZHffJsyKXq0kMxZVh6JJGCliYMUTXLy2Gs1uKnGfxsi9TOA+BEOXEETjVmuwacVMemhVKzUAIil+f/XjTprWH00Q2jXRE9R+2kfjVm32UnlX7nR+pFRwHySv3oWXpAzIcOF9N/ARZBZiRijnHfzj7rpvlvVkg85sT3nqMVcXfpyMyn5pMusfuG2fW23FCcQn6jotCWrpQ1en64Qm6xecPPk7UnX+nAG10cU5bt8hSJ0vGU6nGXO+usmfvrhWbiOQkSWHt+aUkjRaYXzemkYnoPZx2o7+4Un54fPs2g25XOXTdyZ83uh7Jz1/bn7pAocBE/c+K5JPdgKt5RnF/QsVhZigtaSKFRkSzRqrEjIiGV36eb1AdkwBl6S5rtGnqr7L3RvKwpD7+1qLso6k40xM8HYY3ZrgFCNXx89/VnC6eG4VTbGP/wQk1NMGyN8Y9pjEMVsuXGeAPZbuVYKrusOGKMK4b13pCuc7fVKtdmpKF1nkl3AElnSD9ptmtIv7I7mksgtnVCroTYE83B8wFcY7ZrAFi9KPqQ5mCipHH4+IZTXV0v9fy3Qqy2iXi7QpIW939fI+2+/ysl8PF/ -------------------------------------------------------------------------------- /s2s-vpn-bicep/connect-on-prem.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "metadata": { 5 | "_generator": { 6 | "name": "bicep", 7 | "version": "0.16.2.56959", 8 | "templateHash": "17768262978969656259" 9 | } 10 | }, 11 | "parameters": { 12 | "gatewayOnPremName": { 13 | "type": "string", 14 | "defaultValue": "on-prem-gateway", 15 | "allowedValues": [ 16 | "on-prem-gateway", 17 | "on-prem-2-gateway" 18 | ], 19 | "metadata": { 20 | "description": "Select the on-premises location to connect with the hub" 21 | } 22 | }, 23 | "gatewayOnPremLocation": { 24 | "type": "string", 25 | "defaultValue": "francecentral" 26 | }, 27 | "gatewayOnPremResourceGroup": { 28 | "type": "string", 29 | "defaultValue": "hub-and-spoke-playground" 30 | }, 31 | "hubGatewayName": { 32 | "type": "string", 33 | "defaultValue": "lab-gateway" 34 | }, 35 | "hubGatewayResourceGroup": { 36 | "type": "string", 37 | "defaultValue": "hub-and-spoke-playground" 38 | }, 39 | "hubGatewayLocation": { 40 | "type": "string", 41 | "defaultValue": "westeurope" 42 | }, 43 | "psk": { 44 | "type": "securestring", 45 | "defaultValue": "pAsSwOrD.123" 46 | }, 47 | "enableBgp": { 48 | "type": "bool", 49 | "defaultValue": false 50 | } 51 | }, 52 | "resources": [ 53 | { 54 | "type": "Microsoft.Network/connections", 55 | "apiVersion": "2022-07-01", 56 | "name": "[format('{0}-to-cloud', parameters('gatewayOnPremName'))]", 57 | "location": "[parameters('gatewayOnPremLocation')]", 58 | "properties": { 59 | "connectionType": "Vnet2Vnet", 60 | "virtualNetworkGateway1": { 61 | "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('gatewayOnPremResourceGroup')), 'Microsoft.Network/virtualNetworkGateways', parameters('gatewayOnPremName'))]", 62 | "properties": {} 63 | }, 64 | "virtualNetworkGateway2": { 65 | "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('hubGatewayResourceGroup')), 'Microsoft.Network/virtualNetworkGateways', parameters('hubGatewayName'))]", 66 | "properties": {} 67 | }, 68 | "sharedKey": "[parameters('psk')]", 69 | "connectionProtocol": "IKEv2", 70 | "enableBgp": "[parameters('enableBgp')]" 71 | } 72 | }, 73 | { 74 | "type": "Microsoft.Network/connections", 75 | "apiVersion": "2022-07-01", 76 | "name": "[format('cloud-to-{0}', parameters('gatewayOnPremName'))]", 77 | "location": "[parameters('hubGatewayLocation')]", 78 | "properties": { 79 | "connectionType": "Vnet2Vnet", 80 | "virtualNetworkGateway1": { 81 | "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('hubGatewayResourceGroup')), 'Microsoft.Network/virtualNetworkGateways', parameters('hubGatewayName'))]", 82 | "properties": {} 83 | }, 84 | "virtualNetworkGateway2": { 85 | "id": "[extensionResourceId(format('/subscriptions/{0}/resourceGroups/{1}', subscription().subscriptionId, parameters('gatewayOnPremResourceGroup')), 'Microsoft.Network/virtualNetworkGateways', parameters('gatewayOnPremName'))]", 86 | "properties": {} 87 | }, 88 | "sharedKey": "[parameters('psk')]", 89 | "connectionProtocol": "IKEv2", 90 | "enableBgp": "[parameters('enableBgp')]" 91 | } 92 | } 93 | ] 94 | } -------------------------------------------------------------------------------- /scenarios/name-resolution-with-azure-firewall.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: Configure a DNS on the cloud, and be sure that all machines are reachable via FQDN also from on-premise (Azure Firewall Version) 2 | 3 | read also [this blog post](https://nicolgit.github.io/dns-forwarding-azure-hub-and-spoke/) for more information on this solution. 4 | 5 | ## Pre-requisites 6 | 7 | In order to apply this solution: 8 | 9 | 1. deploy HUB playground 10 | 2. deploy ONPREMISE-2 playground 11 | 3. configure the DNS on the cloud as [documented here](dns.md) 12 | 4. configure a site-to-site VPN as [documented here](vnet-to-vnet-2.md) 13 | 14 | ## Solution 15 | 16 | The solution implemented is described in the following schema. 17 | 18 | ![DNS](/images/dns.png) 19 | 20 | ### Configure Azure Firewall for DNS resolution and proxy 21 | Create the following Firewall Policy: 22 | * Name: `hub-fw-policy` 23 | * DNS: `Enabled` 24 | * DNS Servers: `Default (Azure Provided)` 25 | * DNS Proxy: `Enabled` 26 | 27 | Associate the policy `hub-fw-policy` to `hub-lab-vnet`. 28 | 29 | On `spoke-01` virtual network go to DNS Servers and set as DNS Server custom `10.12.3.4` (the internal firewall IP). 30 | 31 | On `spoke-02` virtual network go to DNS Servers and set as DNS Server custom `10.12.3.4` (the internal firewall IP). 32 | 33 | On `spoke-03` virtual network go to DNS Servers and set as DNS Server custom `10.12.3.4` (the internal firewall IP). 34 | 35 | On `hub-lab-net` virtual network go to DNS Servers and set as DNS Server custom `10.12.3.4` (the internal firewall IP). 36 | 37 | ### Configure a DNS machine on prem 38 | Open ssh on `lin-onprem` machine and install [Bind9](https://www.isc.org/bind/): 39 | 40 | ``` 41 | sudo apt-get update 42 | sudo apt-get upgrade 43 | sudo apt-get install bind9 44 | ``` 45 | 46 | Edit `/etc/bind/named.conf.options` file (i.e. with `sudo nano /etc/bind/named.conf.options`) and fill it with the following: 47 | 48 | ``` 49 | options { 50 | directory "/var/cache/bind"; 51 | 52 | allow-query { localhost; 10.20.1.0/24; }; 53 | 54 | recursion yes; 55 | forwarders { 56 | 10.12.3.4; 57 | }; 58 | forward only; 59 | 60 | dnssec-validation no; # needed for private dns zones 61 | auth-nxdomain no; # conform to RFC1035 62 | listen-on-v6 { any; }; 63 | }; 64 | ``` 65 | 66 | > in this configuration, on-prem DNS (`10.20.1.4`) forwards all request to Azure Firewall (`10.12.3.4`) that acts as DNS proxy for the Azure managed DNS accessible from the hub. allow-query parameter allows machines on-prem to use this DNS. dnssec validation off is required to manage the private DNS zone. 67 | 68 | Restart Bind9 service to load the updated configuration. 69 | 70 | `sudo service bind9 restart` 71 | 72 | ### Configure Client machine on-prem-2 73 | 74 | Open ssh on `lin-onprem-2` and set `Lin-onprem` as DNS server. To do it type `sudo nano /etc/resolv.conf` and replace _nameserver_ row with the following: 75 | 76 | `nameserver 10.20.1.4` 77 | > this is `lin-onprem` machine IP 78 | 79 | # Test Solution 80 | From ssh on `Lin-onprem` machine type: 81 | 82 | `nslookup spoke-01-vm.cloudasset.internal 10.12.3.4` (direct query to Azure Firewall) 83 | 84 | or 85 | 86 | `nslookup spoke-01-vm.cloudasset.internal` (query to `lin-onprem` that forward the request to Azure Firewall) 87 | 88 | if the answer is 89 | 90 | ``` 91 | Server:10.12.3.4 92 | Address:10.12.3.4#53 93 | 94 | Non-authoritative answer: 95 | Name:spoke-01-vm.cloudasset.interal 96 | Address: 10.13.1.4 97 | ``` 98 | 99 | On premise machine `Lin-onprem-2` is able to resolve FQDN names for cloud machines (*.clouadasset.internal), with on premise DNS server - `Lin-onprem` - that forwards DNS queries to Azure Firewall on the cloud. Azure Firewall forwards the request to the Azure managed DNS. Azure managed DNS is able to resolve `cloudasset.internal` domain because `hub-lab-vnet` has been linked with the corresponding private zone. 100 | 101 | -------------------------------------------------------------------------------- /scenarios/network-watcher.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: Use Azure Network Watcher for network logging and troubleshooting 2 | 3 | In this solution, we will use Azure Network Watcher and NGS flow logs for logging and network troubleshooting on the following virtual networks/subnets. 4 | 5 | * `hub-lab-net` > `default` subnet 6 | * `spoke-01` > `default` subnet 7 | * `spoke-02` > `default` subnet 8 | 9 | Common NSG flow logs troubleshooting use cases include: 10 | 11 | * **Network Monitoring**: Identify unknown or undesired traffic. Monitor traffic levels and bandwidth consumption. Filter flow logs by IP and port to understand application behavior. Export Flow Logs to analytics and visualization tools of your choice to set up monitoring dashboards. 12 | * **Usage monitoring and optimization**: Identify top talkers in your network. Combine with GeoIP data to identify cross-region traffic. Understand traffic growth for capacity forecasting. Use data to remove overtly restrictive traffic rules. 13 | * **Compliance**: Use flow data to verify network isolation and compliance with enterprise access rules 14 | * **Network forensics & Security analysis**: Analyze network flows from compromised IPs and network interfaces. Export flow logs to any SIEM or IDS tool of your choice. 15 | 16 | ## Pre-requisites 17 | 18 | In order to apply this solution, deploy the **hub** playground first. 19 | 20 | ## Solution 21 | ### 01 - Create a Storage Account 22 | Create an Azure Storage Account to collect the NSG flow logs. 23 | 24 | * Name: `` 25 | * Region: `west europe` 26 | * Performance: `Standard` 27 | * Redundancy: `LRS` 28 | 29 | Under Data Protection TAB, **disable** all recovery options. 30 | 31 | 32 | ### 02 - Create Network Security Groups 33 | Create a Network Security Group with the following characteristics: 34 | 35 | * Name: `nsg-playground` 36 | * region: `west europe` 37 | * inbound/outbound rules: leave all on defaults 38 | 39 | Associate NSG to the following subnets: 40 | 41 | | virtual network | subnet | 42 | |---|---| 43 | | `hub-lab-net` | `default` | 44 | | `spoke-01` | `default` | 45 | | `spoke-02` | `default` | 46 | 47 | under NSG flow logs, click on **create a NSG Flow log**: 48 | * Select NSG: `nsg-playground` 49 | * Storage Accounts: `` 50 | * Retention days: `20` 51 | * Traffic Analytics: `Enable` 52 | * Traffic Analytics processing interval: `10 minutes` 53 | * Log Analytics Workspace: `` 54 | 55 | ## Test Solution 56 | Simulate a connection: 57 | 58 | * Connect via RDP/Bastion to `spoke-01-vm` 59 | * From the virtual desktop connect via RDP to 10.12.1.4 (hub-vm-01) 60 | 61 | check data in Flow logs: 62 | 63 | * open: `` > Containers > Insights-logs-networksecuritygroupflowevent 64 | * open the following folder: 65 | * resourceId= 66 | * SUBSCRIPTIONS 67 | * `` 68 | * RESOURCEGROUPS 69 | * `` 70 | * PROVIDERS 71 | * MICROSOFT.NETWORK 72 | * NETWORKSECURITYGROUPS 73 | * year > month > day > hour > minute > 74 | * macAddress of spoke-01-vm 75 | * _you can find it on azure vm page under > Network interface > properties_ 76 | * open file PT1H.json 77 | 78 | > Flow logs operate at Layer 4 and record all IP flows going in and out of an NSG. Logs are collected at 1-min interval through the Azure platform and do not affect customer resources or network performance in any way. Logs are written in the JSON format and show outbound and inbound flows on a per NSG rule basis. 79 | > here more information on the format and fields meaning: 80 | 81 | in json file, you will find FlowTuples to 10.12.1.4 on 3389 port 82 | 83 | > "1647878148,10.13.1.4, **10.12.1.4** ,51896, **3389** ,T,O,A,E,214,20643,278,36774", 84 | 85 | ### See Traffic Analytics 86 | Go to Azure search bar and type Network Watcher. 87 | Select Traffic Analytics. 88 | 89 | > remember that you have to wait at least 10 minutes before to see your data here. 90 | 91 | -------------------------------------------------------------------------------- /scenarios/ipsec-active-active.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: connect on-prem and hub with a Site-to-Site IPSec ACTIVE-ACTIVE connection 2 | 3 | ## Pre-requisites 4 | 5 | In order to apply this solution you have to deploy `hub` and `on-premises` playgrounds. 6 | 7 | ## Solution 8 | 9 | In this configuration, each Azure gateway instance have a unique public IP address, and each establishes an IPsec/IKE S2S VPN tunnel to on-premises VPN device specified in local network gateway and connection. Note that both VPN tunnels are actually part of the same connection. You will need to configure your on-premises VPN device to accept or establish two S2S VPN tunnels to those two Azure VPN gateway public IP addresses. 10 | 11 | Because the Azure gateway instances are in active-active configuration, the traffic from your Azure virtual network to your on-premises network will be routed through both tunnels simultaneously, even if your on-premises VPN device may favor one tunnel over the other. For a single TCP or UDP flow, Azure attempts to use the same tunnel when sending packets to your on-premises network. However, your on-premises network could use a different tunnel to send packets to Azure. 12 | 13 | When a planned maintenance or unplanned event happens to one gateway instance, the IPsec tunnel from that instance to your on-premises VPN device will be disconnected. The corresponding routes on your VPN devices should be removed or withdrawn automatically so that the traffic will be switched over to the other active IPsec tunnel. On the Azure side, the switch over will happen automatically from the affected instance to the active instance. 14 | 15 | ![ipsec-active-active](/images/ipsec-aa.png) 16 | 17 | More information on [Highly Available cross-premises and VNet-to-VNet connectivity](https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-highlyavailable#active-active-vpn-gateways). 18 | 19 | ### Enable Active-Active mode 20 | Go to Virtual Network Gateway `lab-gateway` in Configuration: 21 | * Active-active mode: Enables 22 | * Second Public IP Address: `hub-gateway-virtualip-2` 23 | * SKU: Standard 24 | * Configure BGP: disabled 25 | 26 | # create Local Network Gateways 27 | create the following gateways 28 | 29 | | Name | IP Address | Address Space | Region | 30 | |---|---|---|---| 31 | |cloud-net | (hub-gateway-virtualip) | 10.0.0.0/8| West Europe | 32 | |cloud-net-2 | (hub-gateway-virtualip-2) | 10.0.0.0/8| West Europe | 33 | |onprem-net| (onprem-gateway-virtualip) | 192.168.0.0/16 | France Central | 34 | 35 | # connection onprem-to-cloud (1) 36 | Open `on-prem-gateway`, go to Connections and add the following object 37 | * Connection Name: `onprem-to-cloud` 38 | * Type: Site-to-Site (IPsec) 39 | * virtual Network Gateway: `on-prem-gateway` 40 | * Local Network Gateway: `cloud-net` 41 | * Shared Key: `password.123` 42 | * IKE: IKEv2 43 | 44 | 45 | # connection cloud-to-onprem 46 | Open `lab-gateway`, go to Connections and add the following object 47 | * Connection Name: `cloud-to-onprem` 48 | * Type: Site-to-Site (IPsec) 49 | * virtual Network Gateway: `lab-gateway` 50 | * Local Network Gateway: `onprem-net` 51 | * Shared Key: `password.123` 52 | * IKE: IKEv2 53 | 54 | # connection onprem-to-cloud (2) 55 | Open `on-prem-gateway`, go to Connections and add the following object 56 | * Connection Name: `onprem-to-cloud-2` 57 | * Type: Site-to-Site (IPsec) 58 | * virtual Network Gateway: `on-prem-gateway` 59 | * Local Network Gateway: `cloud-net-2` 60 | * Shared Key: `password.123` 61 | * IKE: IKEv2 62 | 63 | 64 | 65 | after few minutes, you will see, on `on-prem-gateway` connections: 66 | 67 | | Name | Status | Connection Type | Peer | 68 | |---|---|---|---| 69 | |onprem-to-cloud | connected |Site-to-Site (IPsec)| cloud-net| 70 | |onprem-to-cloud-2 | connected |Site-to-Site (IPsec)| cloud-net-2| 71 | 72 | and on `lab-gateway` connections: 73 | 74 | | Name | Status | Connection Type | Peer | 75 | |---|---|---|---| 76 | |cloud-to-onprem | connected |Site-to-Site (IPsec)| onprem-net | 77 | 78 | ## Test solution 79 | Via bastion go to W10onprem (`192.168.1.4`) and open a RDP connection to hub-vm-01 (`10.12.1.4`). 80 | 81 | Do the same test also from hub-vm-01 to W10onprem. -------------------------------------------------------------------------------- /scenarios/ping-any-to-any-gateway.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: Allows machines in ANY spoke to communicate with ANY machine in ANY other spoke (Virtual Network Gateway) 2 | 3 | The Azure Virtual Network Gateway is a specific type of virtual network gateway that is used to send encrypted traffic between an Azure virtual network and an on-premises location over the public Internet. It can also send traffic between Azure virtual networks. Over a virtual network gateway, you can send traffic from one virtual network to another in a very secure manner. 4 | 5 | The main capabilities of Azure Virtual Network Gateway include: 6 | * VPN Connectivity: It provides VPN connectivity, enabling secure communication channels between different locations. 7 | * Cross-Premises Connectivity: It allows you to connect your on-premises network to Azure's network, essentially extending your on-premises network to the cloud. 8 | * Inter-VNET Connectivity: It can be used to connect different Azure Virtual Networks, enabling resources in different Virtual Networks to communicate with each other. 9 | * Point-to-Site VPN: It enables individual clients to connect to the Azure network from anywhere, making it ideal for remote workers or individual devices. 10 | * Advanced Routing: It has advanced routing capabilities, including Border Gateway Protocol (BGP) support and dynamic routing. 11 | 12 | In the context of a hub and spoke architecture in Azure, the Virtual Network Gateway can be used to enable routing between different spokes. Normally, peering relationships in Azure are non-transitive, meaning that if spoke 1 is connected to the hub, and spoke 2 is also connected to the hub, spoke 1 and spoke 2 cannot communicate with each other by default. 13 | 14 | However, by leveraging a Virtual Network Gateway in the hub network, you can configure it to allow traffic to flow between spokes. This is done by enabling 'Use Remote Gateways' and 'Allow Gateway Transit' settings in the peering settings. The Virtual Network Gateway essentially acts as a router, directing traffic between different virtual networks. This can be particularly useful in scenarios where you have resources in different spokes that need to communicate with each other. 15 | 16 | In this solution, I show you how configure the routing between spokes using a **Virtual Network Gateway** in the middle. 17 | 18 | The resulting overall architecture is shown in the following schema. 19 | 20 | ![any to any routing via Virtual Network Gateway](/images/any-to-any-routing-gateway.png) 21 | 22 | _Download a [draw.io file](../images/any-to-any-routing.drawio) of this architecture._ 23 | 24 | ## Pre-requisites 25 | 26 | In order to apply this solution you have to deploy hub playground only. 27 | 28 | ## Solution 29 | 30 | In order to allow this communication, via **Azure Virtual Gateway**: 31 | 32 | Verify that gateway transit is Enabled, on `hub-lab-net` > peerings 33 | 34 | | Name | Peering Status | Peer | Gateway Transit | 35 | |---|---|---|---| 36 | | hub-to-spoke01 | Connected | spoke-01 | Enabled | 37 | | hub-to-spoke02 | Connected | spoke-02 | Enabled | 38 | | hub-to-spoke03 | Connected | spoke-03 | Enabled | 39 | 40 | Create the following route table in `west europe`: `spokes-we-to-hub-routes` 41 | 42 | | Name | Address Prefix | Next hop type | Next hop IP addr | 43 | |---|---|---|---| 44 | | to-spoke-01 | 10.13.1.0/24 | Virtual network gateway | - | 45 | | to-spoke-02 | 10.13.2.0/24 | Virtual network gateway | - | 46 | | to-spoke-03 | 10.13.3.0/24 | Virtual network gateway | - | 47 | 48 | 49 | | subnet Name | Virtual Network | 50 | |---|---| 51 | | default | spoke-01 | 52 | | services | spoke-01 | 53 | | default | spoke-02 | 54 | | services | spoke-02 | 55 | 56 | Create the following route table in `north europe`: `spokes-ne-to-hub-routes` 57 | 58 | | Name | Address Prefix | Next hop type | Next hop IP addr | 59 | |---|---|---|---| 60 | | to-spoke-01 | 10.13.1.0/24 | Virtual network gateway | - | 61 | | to-spoke-02 | 10.13.2.0/24 | Virtual network gateway | - | 62 | 63 | | subnet Name | Virtual Network | 64 | |---|---| 65 | | default | spoke-03 | 66 | | services | spoke-03 | 67 | 68 | ## Test Solution 69 | Test connections using remote desktop client and ssh from one machine to another. 70 | 71 | ## More information 72 | 73 | * Azure [Virtual Network Gateway](https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-about-vpngateways) 74 | * Configure VPN [Gateway Transit](https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-peering-gateway-transit) for networks in peering 75 | -------------------------------------------------------------------------------- /scenarios/ipsec-multiple-vpn-device.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: connect on-prem and hub with a Site-to-Site IPSec ACTIVE-ACTIVE connection 2 | 3 | # *** STILL TO BE REVIEWED *** 4 | 5 | ## Pre-requisites 6 | 7 | In order to apply this solution you have to deploy `hub` and `on-premises` playgrounds. 8 | 9 | ## Solution 10 | 11 | This configuration provides multiple active tunnels from the same Azure VPN gateway to your on-premises devices in the same location. There are some requirements and constraints: 12 | 13 | * You need to create multiple S2S VPN connections from your VPN devices to Azure. When you connect multiple VPN devices from the same on-premises network to Azure, you need to create one local network gateway for each VPN device, and one connection from your Azure VPN gateway to each local network gateway. 14 | * The local network gateways corresponding to your VPN devices must have unique public IP addresses in the "GatewayIpAddress" property. 15 | * BGP is required for this configuration. Each local network gateway representing a VPN device must have a unique BGP peer IP address specified in the "BgpPeerIpAddress" property. 16 | * You should use BGP to advertise the same prefixes of the same on-premises network prefixes to your Azure VPN gateway, and the traffic will be forwarded through these tunnels simultaneously. 17 | * You must use Equal-cost multi-path routing (ECMP). 18 | * Each connection is counted against the maximum number of tunnels for your Azure VPN gateway, 10 for Basic and Standard SKUs, and 30 for HighPerformance SKU. 19 | 20 | In this configuration, the Azure VPN gateway is still in active-standby mode, so the same failover behavior and brief interruption will still happen as described above. But this setup guards against failures or interruptions on your on-premises network and VPN devices. 21 | 22 | 23 | ![ipsec-active-active](/images/ipsec-----) 24 | 25 | More information on [Highly Available cross-premises and VNet-to-VNet connectivity](https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-highlyavailable#active-active-vpn-gateways). 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | ### Enable Active-Active mode 44 | Go to Virtual Network Gateway `lab-gateway` in Configuration: 45 | * Active-active mode: Enables 46 | * Second Public IP Address: `hub-gateway-virtualip-2` 47 | * SKU: Standard 48 | * Configure BGP: disabled 49 | 50 | # create Local Network Gateways 51 | create the following gateways 52 | 53 | | Name | IP Address | Address Space | Region | 54 | |---|---|---|---| 55 | |cloud-net | (hub-gateway-virtualip) | 10.0.0.0/8| West Europe | 56 | |cloud-net-2 | (hub-gateway-virtualip-2) | 10.0.0.0/8| West Europe | 57 | |onprem-net| (onprem-gateway-virtualip) | 192.168.0.0/16 | France Central | 58 | 59 | # connection onprem-to-cloud (1) 60 | Open `on-prem-gateway`, go to Connections and add the following object 61 | * Connection Name: `onprem-to-cloud` 62 | * Type: Site-to-Site (IPsec) 63 | * virtual Network Gateway: `on-prem-gateway` 64 | * Local Network Gateway: `cloud-net` 65 | * Shared Key: `password.123` 66 | * IKE: IKEv2 67 | 68 | 69 | # connection cloud-to-onprem 70 | Open `lab-gateway`, go to Connections and add the following object 71 | * Connection Name: `cloud-to-onprem` 72 | * Type: Site-to-Site (IPsec) 73 | * virtual Network Gateway: `lab-gateway` 74 | * Local Network Gateway: `onprem-net` 75 | * Shared Key: `password.123` 76 | * IKE: IKEv2 77 | 78 | # connection onprem-to-cloud (2) 79 | Open `on-prem-gateway`, go to Connections and add the following object 80 | * Connection Name: `onprem-to-cloud-2` 81 | * Type: Site-to-Site (IPsec) 82 | * virtual Network Gateway: `on-prem-gateway` 83 | * Local Network Gateway: `cloud-net-2` 84 | * Shared Key: `password.123` 85 | * IKE: IKEv2 86 | 87 | 88 | 89 | after few minutes, you will see, on `on-prem-gateway` connections: 90 | 91 | | Name | Status | Connection Type | Peer | 92 | |---|---|---|---| 93 | |onprem-to-cloud | connected |Site-to-Site (IPsec)| cloud-net| 94 | |onprem-to-cloud-2 | connected |Site-to-Site (IPsec)| cloud-net-2| 95 | 96 | and on `lab-gateway` connections: 97 | 98 | | Name | Status | Connection Type | Peer | 99 | |---|---|---|---| 100 | |cloud-to-onprem | connected |Site-to-Site (IPsec)| onprem-net | 101 | 102 | ## Test solution 103 | Via bastion go to W10onprem (`192.168.1.4`) and open a RDP connection to hub-vm-01 (`10.12.1.4`). 104 | 105 | Do the same test also from hub-vm-01 to W10onprem. -------------------------------------------------------------------------------- /scenarios/sftp.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: Publish internal SFTP endpoint via Azure Firewall 2 | 3 | In this solution I show how to publish through Azure Firewall an SFTP endpoint delivered by an Azure Storage. 4 | 5 | The service is exposed by the Azure Firewall public IP. 6 | 7 | > Exposing an SFTP server through a firewall, rather than directly exposing it to the public, adds an additional layer of security. An Azure firewall can be configured to only allow certain types of traffic to pass through to the SFTP server, reducing the attack surface and making it more difficult for attackers to exploit vulnerabilities. Additionally, a firewall can monitor and log traffic, providing valuable information for detecting and responding to security incidents. It is generally considered a best practice to use a firewall to protect any publicly accessible server. 8 | 9 | The scenario is deployed into a Hub and Spoke network topology context. Specifically, the virtual network that hosts the Azure Storage is different from the virtual network that hosts the Azure Firewall. 10 | 11 | In this configuration, inbound SFTP traffic goes through both Azure Firewall, the peering, the spoke and finally the Azure Storage thanks to the private endpoint. 12 | 13 | The resulting overall architecture is shown in the following schema. 14 | 15 | ![sftp architecture](../images/sftp.png) 16 | 17 | _Download a [draw.io file](../images/sftp.drawio) of this architecture._ 18 | 19 | ## Pre-requisites 20 | 21 | In order to apply this solution you have to deploy HUB playground only. 22 | 23 | ## Solution 24 | 25 | ### Create Storage Account 26 | 27 | From Azure Portal go to Storage Accounts > Create: 28 | 29 | Basic 30 | 31 | * Storage account Name: `storage01` 32 | * Region: `west europe` 33 | * Redundancy: `LRS` 34 | 35 | Advanced 36 | 37 | * Hierarchical namespace: `Enable` 38 | * SFTP: `Enable` 39 | 40 | Networking 41 | 42 | * Networking access: `disable public access` 43 | * Private endpoint > Add private endpoint 44 | * Location: `West Europe` 45 | * Name: `storage-01-pe` 46 | * Storage sub resource: `blob` 47 | * Virtual network: `spoke-01` 48 | * Subnet: `Services` 49 | * Private DNS zone: `No` 50 | 51 | * click [Create] 52 | 53 | ### Configure Storage Account 54 | 55 | From Azure Portal go to Storage accounts > `storage01` > Containers > Create 56 | 57 | * Name: `test-container-01` 58 | 59 | Click [Create]. 60 | 61 | From Azure Portal go to Storage accounts > `storage01` > SFTP > Add local user 62 | 63 | * username: `user01` 64 | * Authentication method: `SSH Password` 65 | * Container permissiona 66 | * Container: `test-container-01` 67 | * Permissions: `All` 68 | 69 | click [Add] and copy the generated password. 70 | 71 | ### Configure Azure Firewall 72 | 73 | Go to Azure Portal > Firewall Policies > Create: 74 | 75 | Basic Tab 76 | 77 | * Name: `hub-fw-policy` 78 | * Region: `West Europe` 79 | * Parent Policy: 80 | 81 | Rules Tab 82 | 83 | **DNAT rules** 84 | 85 | |priority | collection name | rule name | src | port | protocol | destination | translated address| translated port | action | 86 | |---|---|---|---|---|---|---|---|---|---| 87 | |1000 | coll01 |sftp | * | 22 | TCP UDP | `x.x.x.x` | `10.13.1.68` | 22 | Dnat | 88 | 89 | Where: 90 | 91 | * `x.x.x.x` is `lab-firewall` public IP 92 | * `10.13.1.68` is `storage-01-pe`'s private IP. 93 | 94 | Click [Create]. 95 | 96 | Associate the policy `hub-fw-policy` to `lab-firewall` via Firewall Manager 97 | 98 | 99 | ## Test solution 100 | from internet, from your laptop: 101 | 102 | ``` 103 | C:>sftp storage01.test-container-01.user01@x.x.x.x 104 | ECDSA key fingerprint is SHA256:0WNMHmCNJE1YFBpHNeADuT5h+PfJ/jJPtUDHCxCSrO0. 105 | This key is not known by any other names 106 | Are you sure you want to continue connecting (yes/no/[fingerprint])? 107 | Warning: Permanently added 'x.x.x.x' (ECDSA) to the list of known hosts. 108 | storage01.test-container@x.x.x.x's password: 109 | Connected to storage01.test-container-01.user01@x.x.x.x. 110 | sftp> 111 | 112 | ``` 113 | 114 | You can successfully connect with the sftp server. 115 | 116 | ## More information 117 | 118 | * Azure Firewall [DNAT rules](https://learn.microsoft.com/en-us/azure/firewall/tutorial-firewall-dnat). 119 | * Azure Storage [SFTP Support](https://learn.microsoft.com/en-us/azure/storage/blobs/secure-file-transfer-protocol-support). 120 | * Azure [Private Endpont](https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview). -------------------------------------------------------------------------------- /any-to-any-bicep/fw-policy.bicep: -------------------------------------------------------------------------------- 1 | param allowIpAddresses array = [] 2 | param locationWE string = 'westeurope' 3 | 4 | @description('Basic, Standard or Premium tier') 5 | @allowed([ 'Basic', 'Standard', 'Premium' ]) 6 | param firewallTier string = 'Premium' 7 | 8 | var ipGroups_all_spokes_subnets_name = 'all-spokes-subnets' 9 | var firewallPolicyName = 'my-firewall-policy' 10 | 11 | var ipGroupAddresses = concat([ 12 | '10.13.1.0/24' 13 | '10.13.2.0/24' 14 | '10.13.3.0/24' 15 | ], allowIpAddresses) 16 | 17 | resource ipGroup 'Microsoft.Network/ipGroups@2020-05-01' = { 18 | name: ipGroups_all_spokes_subnets_name 19 | location: locationWE 20 | properties: { 21 | ipAddresses: ipGroupAddresses 22 | } 23 | } 24 | 25 | resource myFirewallPolicy 'Microsoft.Network/firewallPolicies@2020-05-01' = { 26 | name: firewallPolicyName 27 | location: locationWE 28 | properties: { 29 | threatIntelMode: 'Alert' 30 | sku: { 31 | tier: 'Premium' 32 | } 33 | } 34 | } 35 | 36 | resource toInternetCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2022-07-01' = { 37 | parent: myFirewallPolicy 38 | name: 'DefaultApplicationRuleCollectionGroup' 39 | properties: { 40 | priority: 300 41 | ruleCollections: [ 42 | { 43 | ruleCollectionType: 'FirewallPolicyFilterRuleCollection' 44 | action: { 45 | type: 'Allow' 46 | } 47 | rules: [ 48 | { 49 | ruleType: 'ApplicationRule' 50 | name: 'allow-internet-traffic-out' 51 | protocols: [ 52 | { 53 | protocolType: 'Http' 54 | port: 80 55 | } 56 | { 57 | protocolType: 'Https' 58 | port: 443 59 | } 60 | ] 61 | fqdnTags: [] 62 | webCategories: [] 63 | targetFqdns: [ 64 | '*' 65 | ] 66 | targetUrls: [] 67 | terminateTLS: false 68 | sourceAddresses: [] 69 | destinationAddresses: [] 70 | sourceIpGroups: [ ipGroup.id ] 71 | } 72 | ] 73 | name: 'internet-out-collection' 74 | priority: 200 75 | } 76 | { 77 | ruleCollectionType: 'FirewallPolicyFilterRuleCollection' 78 | action: { 79 | type: 'Deny' 80 | } 81 | // Basic tier does not allow use of webCategories 82 | rules: firewallTier == 'Basic' ? null : [ 83 | { 84 | ruleType: 'ApplicationRule' 85 | name: 'block-porn-sites' 86 | protocols: [ 87 | { 88 | protocolType: 'Http' 89 | port: 80 90 | } 91 | { 92 | protocolType: 'Https' 93 | port: 443 94 | } 95 | ] 96 | fqdnTags: [] 97 | webCategories: [ 98 | 'Nudity' 99 | 'PornographyAndSexuallyExplicit' 100 | 'ChildInappropriate' 101 | ] 102 | targetFqdns: [] 103 | targetUrls: [] 104 | terminateTLS: false 105 | sourceAddresses: [] 106 | destinationAddresses: [] 107 | sourceIpGroups: [ ipGroup.id ] 108 | } 109 | ] 110 | name: 'block-some-stuff' 111 | priority: 150 112 | } 113 | ] 114 | } 115 | } 116 | 117 | resource anyToAnyCollectionGroup 'Microsoft.Network/firewallPolicies/ruleCollectionGroups@2020-05-01' = { 118 | parent: myFirewallPolicy 119 | name: 'DefaultNetworkRuleCollectionGroup' 120 | dependsOn: [ toInternetCollectionGroup ] // RM deploys all the ruleCollectionGroups in parallel or at least not sequentially - https://learn.microsoft.com/en-us/answers/questions/673917/update-of-azure-firewall-policies-failes-faulted-r 121 | properties: { 122 | priority: 300 123 | ruleCollections: [ 124 | { 125 | ruleCollectionType: 'FirewallPolicyFilterRuleCollection' 126 | name: 'any-to-any-collection' 127 | priority: 1000 128 | action: { 129 | type: 'Allow' 130 | } 131 | rules: [ 132 | { 133 | ruleType: 'NetworkRule' 134 | name: 'allow-spoke-to-spoke-traffic' 135 | ipProtocols: [ 'Any' ] 136 | sourceIpGroups: [ 137 | ipGroup.id 138 | ] 139 | destinationPorts: [ 140 | '*' 141 | ] 142 | destinationIpGroups: [ 143 | ipGroup.id 144 | ] 145 | } 146 | ] 147 | } 148 | ] 149 | } 150 | } 151 | 152 | output policyid string = myFirewallPolicy.id 153 | 154 | -------------------------------------------------------------------------------- /images/routing-without-bgp-fw.drawio: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /scenarios/routing-with-bgp.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: Routing cross hub with BGP 2 | 3 | This solution shows how to configure the routing across 4 network hubs, using Virtual Network Gateways, Vnet2Net Connections and the BGP Protocol, as shown in the following schema. 4 | 5 | ![routing with BGP](../images/routing-with-bgp.png) 6 | 7 | * an Azure [Virtual Network Gateway](https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-about-vpngateways) is composed of two or more VMs that are automatically configured and deployed to a specific subnet you create called the gateway subnet. The gateway VMs contain routing tables and run specific gateway services. 8 | * [VNet-to-VNet connections](https://learn.microsoft.com/en-us/azure/vpn-gateway/design#V2V) use a VPN gateway to provide a secure tunnel using IPsec/IKE to connect 2 virtual networks in a way similar to connecting a VNet to an on-premises site location. 9 | * [BGP is the standard routing protocol](https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-bgp-overview) commonly used in the Internet to exchange routing and reachability information between two or more networks. When used in the context of Azure Virtual Networks, BGP enables the Azure VPN Gateways called BGP peers or neighbors, to exchange "routes" that will inform both gateways on the availability and reachability for those prefixes to go through the gateways or routers involved. BGP can also enable transit routing among multiple networks by propagating routes a BGP gateway learns from one BGP peer to all other BGP peers. 10 | 11 | ## Pre-requisites 12 | 13 | In order to apply this solution: 14 | 15 | * deploy the `hub playground` 16 | * remove all peerings 17 | 18 | ## Solution 19 | 20 | Once all peering are removed, to allow routing across all hubs are required: 21 | 22 | 1. a Virtual Network Gateway in each spoke network 23 | 2. a Vnet-to-Vnet connection with BGP enabled between `hub-lab-net` and `Spoke01` 24 | 3. a Vnet-to-Vnet connection with BGP enabled between `spoke-01` and `spoke-02` 25 | 4. a Vnet-to-Vnet connection with BGP enabled between `spoke-02` and `spoke-03` 26 | 27 | ### Create a VPN Gateway in each spoke network 28 | 29 | Create the following Azure Virtual Network Gateways: 30 | 31 | | Name | Region | Type | VPN Type | SKU | Virtual Network | gw subnet range | IP | BGP | ANS 32 | |---|---|---|---|---|---|---|---|---|---| 33 | |spoke-01-gateway| west-europe | VPN | route based | VpnGw1 | spoke-01 | 10.13.1.128/26 | spoke-01-ip | Enabled | 65511 34 | |spoke-02-gateway| west-europe | VPN | route based | VpnGw1 | spoke-02 | 10.13.2.128/26 | spoke-02-ip | Enabled | 65512 35 | |spoke-03-gateway| north-europe | VPN | route based | VpnGw1 | spoke-03 | 10.13.3.128/26 | spoke-03-ip | Enabled | 65513 36 | 37 | On `lab-gateway` also **enable BGP** and set ANS=`65514` 38 | 39 | ### Create all the VNet2-VNet connections 40 | 2 VPN gateways to communicate need 2 connections: one from the first to the second, and another from the second to the first. Therefore, for our topologym the following connections are needed: 41 | 42 | | Connection Name | Type | From | To | PSK | Enable BGP | 43 | |---|---|---|---|---|---| 44 | | hub-to-spoke-01 | Vnet-2-Vnet | lab-gateway | spoke-01-gateway | password.123 | YES | 45 | | spoke-01-to-hub | Vnet-2-Vnet | spoke-01-gateway | lab-gateway | password.123 | YES | 46 | | spoke-01-to-02 | Vnet-2-Vnet | spoke-01-gateway | spoke-02-gateway | password.123 | YES | 47 | | spoke-02-to-01 | Vnet-2-Vnet | spoke-02-gateway | spoke-01-gateway | password.123 | YES | 48 | | spoke-02-to-03 | Vnet-2-Vnet | spoke-02-gateway | spoke-03-gateway | password.123 | YES | 49 | | spoke-03-to-02 | Vnet-2-Vnet | spoke-03-gateway | spoke-02-gateway | password.123 | YES | 50 | 51 | ## Test Solution 52 | Wait a few minutes, until all previously created connections have a `connected` status. 53 | 54 | Go to `lab-gateways` > bgp peers > learned routes to verify that all route are propagated. YOu will see the following: 55 | 56 | | network CIDR | next hop | local address | source peer | origin | AS path | 57 | |---|---|---|---|---|---| 58 | | 10.12.0.0/16 | - | 10.12.4.254 | 10.12.4.254 | Network | - | 59 | | 10.13.1.190/32 | - | 10.12.4.254 | 10.12.4.254 | Network | - | 60 | | 10.13.1.0/24 | 10.13.1.190 | 10.12.4.254 | 10.13.1.190 | EBgp | 65511 | 61 | | 10.13.2.0/24 | 10.13.1.190 | 10.12.4.254 | 10.13.1.190 | EBgp | 65511-65512 | 62 | | 10.13.3.0/24 | 10.13.1.190 | 10.12.4.254 |10.13.1.190 | EBgp | 65511-65512-65513 | 63 | 64 | Each Virtual Network Gateway in this context is an Autonomous System (AS), in this table, the `AS path` lists all the ASes that need to be traversed to reach the location where the `network` that the path is attached to is advertised from. As such, a traceroute should encounter those same ASes. 65 | 66 | From the table above, you can see that a machine in lab-hub-net to arrive to a machine in spoke-03 net (10.13.3.0/24), have to cross `65511` (`spoke-01-gateway`), `65512` (`spoke-02-gateway`) and `65513` (`spoke-03-gateway`). 67 | 68 | ### RDP connection test 69 | 1. connect to `hub-01-vm` via RDP/bastion 70 | 2. once on the vm, open remote desktop connection and connect to `10.13.2.4` (a Windows VM) 71 | -------------------------------------------------------------------------------- /scenarios/ipsec-dual-redundancy.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: connect on-prem and hub with a Site-to-Site VPN with dual-redundancy, ACTIVE-ACTIVE connection 2 | 3 | ## Pre-requisites 4 | 5 | In order to apply this solution you have to deploy `hub` and `on-premises` playgrounds. 6 | 7 | ## Solution 8 | Here you create and set up the Azure VPN gateway in an active-active configuration, and create two local network gateways and two connections for your two on-premises VPN devices as described above. The result is a full mesh connectivity of 4 IPsec tunnels between your Azure virtual network and your on-premises network. 9 | 10 | All gateways and tunnels are active from the Azure side, so the traffic will be spread among all 4 tunnels simultaneously, although each TCP or UDP flow will again follow the same tunnel or path from the Azure side. Even though by spreading the traffic, you may see slightly better throughput over the IPsec tunnels, the primary goal of this configuration is for high availability. And due to the statistical nature of the spreading, it is difficult to provide the measurement on how different application traffic conditions will affect the aggregate throughput. 11 | 12 | This topology will require **two local network gateways** and **two connections** to support the pair of on-premises VPN devices, and **BGP is required** to allow the two connections to the same on-premises network. 13 | 14 | ![S2S VPN with dual redundacy configuration](../images/ipsec-dr.png) 15 | 16 | More information on [dual-redundancy, active-active VPN gateways for both Azure and on-premises networks](https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-highlyavailable#dual-redundancy-active-active-vpn-gateways-for-both-azure-and-on-premises-networks). 17 | 18 | ### Enable Active-Active mode 19 | Go to Virtual Network Gateway `lab-gateway` in Configuration: 20 | * Active-active mode: Enables 21 | * Second Public IP Address: `hub-gateway-virtualip-2` 22 | * SKU: Basic 23 | * Configure BGP: enabled 24 | * ASN: 65513 25 | * Click *Save* 26 | 27 | Go to Virtual Network Gateway `on-prem-gateway` in Configuration: 28 | * Active-active mode: Enables 29 | * Second Public IP Address: `onprem-gateway-virtualip-2` 30 | * SKU: Basic 31 | * Configure BGP: enabled 32 | * ASN: 65514 33 | * Click *Save* 34 | 35 | # create Local Network Gateways 36 | create the following gateways 37 | 38 | | Name | IP Address | Address Space | Region | Configure BGP | ASN | BGP peer IP addr | 39 | |---|---|---|---|---|---|---| 40 | |cloud-net | (hub-gateway-virtualip) | 10.0.0.0/8| West Europe | yes | 65513 | 10.12.4.4 | 41 | |cloud-net-2 | (hub-gateway-virtualip-2) | 10.0.0.0/8| West Europe | yes | 65513 | 10.12.4.5 | 42 | |onprem-net| (onprem-gateway-virtualip) | 192.168.0.0/16 | France Central | yes | 65514 | 192.168.3.4 | 43 | |onprem-net-2| (onprem-gateway-virtualip-2) | 192.168.0.0/16 | France Central | yes | 65514 | 192.168.3.5 | 44 | 45 | > BGP peer IP addr is th "[default|second] Azure BGP peer IP address" you can see in Azure Virtual Network Gateway > Configuration pane 46 | 47 | 48 | # connection onprem-to-cloud 49 | Open `on-prem-gateway`, go to Connections and add the following object: 50 | 51 | * Connection Name: `onprem-to-cloud` 52 | * Type: Site-to-Site (IPsec) 53 | * virtual Network Gateway: `on-prem-gateway` 54 | * Local Network Gateway: `cloud-net` 55 | * Shared Key: `password.123` 56 | * Enable BGP: true 57 | * IKE: IKEv2 58 | 59 | 60 | # connection cloud-to-onprem 61 | Open `lab-gateway`, go to Connections and add the following object: 62 | 63 | * Connection Name: `cloud-to-onprem` 64 | * Type: Site-to-Site (IPsec) 65 | * virtual Network Gateway: `lab-gateway` 66 | * Local Network Gateway: `onprem-net` 67 | * Shared Key: `password.123` 68 | * Enable BGP: true 69 | * IKE: IKEv2 70 | 71 | # connection onprem-to-cloud-2 72 | Open `on-prem-gateway`, go to Connections and add the following object: 73 | 74 | * Connection Name: `onprem-to-cloud-2` 75 | * Type: Site-to-Site (IPsec) 76 | * virtual Network Gateway: `on-prem-gateway` 77 | * Local Network Gateway: `cloud-net-2` 78 | * Shared Key: `password.123` 79 | * Enable BGP: true 80 | * IKE: IKEv2 81 | 82 | # connection cloud-to-onprem-2 83 | Open `lab-gateway`, go to Connections and add the following object: 84 | 85 | * Connection Name: `cloud-to-onprem-2` 86 | * Type: Site-to-Site (IPsec) 87 | * virtual Network Gateway: `lab-gateway` 88 | * Local Network Gateway: `onprem-net-2` 89 | * Shared Key: `password.123` 90 | * Enable BGP: true 91 | * IKE: IKEv2 92 | 93 | 94 | 95 | after few minutes, you will see, on `on-prem-gateway` connections: 96 | 97 | | Name | Status | Connection Type | Peer | 98 | |---|---|---|---| 99 | |onprem-to-cloud | connected |Site-to-Site (IPsec)| cloud-net| 100 | |onprem-to-cloud-2 | connected |Site-to-Site (IPsec)| cloud-net-2| 101 | 102 | and on `lab-gateway` connections: 103 | 104 | | Name | Status | Connection Type | Peer | 105 | |---|---|---|---| 106 | |cloud-to-onprem | connected |Site-to-Site (IPsec)| onprem-net | 107 | |cloud-to-onprem-2 | connected |Site-to-Site (IPsec)| onprem-net | 108 | 109 | ## Test solution 110 | Via bastion go to W10onprem (`192.168.1.4`) and open a RDP connection to hub-vm-01 (`10.12.1.4`). 111 | 112 | Do the same test also from hub-vm-01 to W10onprem. 113 | -------------------------------------------------------------------------------- /images/dns.drawio: -------------------------------------------------------------------------------- 1 | 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 -------------------------------------------------------------------------------- /scenarios/cross-on-premise-routing.md: -------------------------------------------------------------------------------- 1 | # SOLUTION - enable cross on-premise communication 2 | 3 | ## Pre-requisites 4 | 5 | in order to apply this solution you have to deploy hub, on prem and on prem 2 playgrounds. 6 | 7 | ## Solution 8 | 9 | ### Step 1 - enable BGP 10 | In resource Group `hub-and-spoke-playground` > `lab-gateway` > Configuration: 11 | * Configure BGP: **ENABLE** 12 | * ASN: **65514** 13 | * BGP peer IP: **10.12.4.254** 14 | * Save (if the portals shows an ERROR on save, just refresh the page, probably the configuration is ok anyway) 15 | 16 | In resource Group `on-premise-playground` > `on-prem-gateway` > Configuration: 17 | * Configure BGP: **ENABLE** 18 | * ASN: **65513** 19 | * BGP peer IP: **10.20.3.254** 20 | * Save (if the portals shows an ERROR on save, just refresh the page, probably the configuration is ok anyway) 21 | 22 | In resource Group `on-premise-2-playground` > `on-prem-2-gateway` > Configuration: 23 | * Configure BGP: **ENABLE** 24 | * ASN: **65512** 25 | * BGP peer IP: **192.168.3.254** 26 | * Save (if the portals shows an ERROR on save, just refresh the page, probably the configuration is ok anyway) 27 | 28 | ### Step 2: connecttion onprem-to-cloud 29 | Open `on-prem-gateway`, go to Connections and add the following object 30 | * Connection Name: `onprem-to-cloud` 31 | * Type: **VNet-to-VNet** 32 | * First virtual Network Gateway: `on-prem-gateway` 33 | * Second virtual Network Gateway: `lab-gateway` 34 | * Shared Key: `password.123` 35 | * BGP: **Enable** 36 | * IKE: **IKEv2** 37 | 38 | ### Step 3: connection cloud-to-onprem 39 | Open `lab-gateway`, go to Connections and add the following object 40 | * Connection Name: `cloud-to-onprem` 41 | * Type: VNet-to-VNet 42 | * First virtual Network Gateway: `lab-gateway` 43 | * Second virtual Network Gateway: `on-prem-gateway` 44 | * Shared Key: `password.123` 45 | * BGP: **Enable** 46 | * IKE: IKEv2 47 | 48 | ### Step 4: connection onprem-2-to-cloud 49 | Open `on-prem-2-gateway`, go to Connections and add the following object 50 | * Connection Name: `onprem-2-to-cloud` 51 | * Type: **VNet-to-VNet** 52 | * First virtual Network Gateway: `on-prem-2-gateway` 53 | * Second virtual Network Gateway: `lab-gateway` 54 | * Shared Key: `password.123` 55 | * BGP: **Enable** 56 | * IKE: **IKEv2** 57 | 58 | ### Step 5: connection cloud-to-onprem-2 59 | Open `lab-gateway`, go to Connections and add the following object 60 | * Connection Name: `cloud-to-onprem-2` 61 | * Type: **VNet-to-VNet** 62 | * First virtual Network Gateway: `lab-gateway` 63 | * Second virtual Network Gateway: `on-prem-gateway` 64 | * Shared Key: `password.123` 65 | * BGP: **Enable** 66 | * IKE: **IKEv2** 67 | 68 | 69 | ...after few minutes you will have the following connections estabilished in `lab-gateway` > connections: 70 | 71 | | Name | Status | Connection Type | Peer | 72 | |---|---|---|---| 73 | |cloud-to-onprem | connected |VNet-toVNet| on-prem-gateway | 74 | |cloud-to-onprem-2 | connected |VNet-toVNet| on-prem-2-gateway | 75 | |onprem-to-cloud | connected |VNet-toVNet| on-prem-gateway | 76 | |onprem-2-to-cloud | connected |VNet-toVNet| on-prem-2-gateway | 77 | 78 | ![solution diagram](../images/cross-on-premise-routing.png) 79 | 80 | 81 | ## Test solution 82 | Verify in `lab-gateway` > BGP peers: 83 | 84 | | peer addr | local addr | ASN | Status | 85 | |---|---|---|---| 86 | | 10.20.3.254 | 10.12.4.254 | 65512 | connected | 87 | | 192.168.3.254 | 10.12.4.254 | 65513 | connected | 88 | 89 | and learned routes 90 | 91 | | Network | next hop | src peer | origin | AS path | 92 | |---|---|---|---|---| 93 | | 10.12.0.0/16 | - | 10.12.4.254 | network | | 94 | | 10.13.2.0/16 | - | 10.12.4.254 | network | | 95 | | 10.13.1.0/16 | - | 10.12.4.254 | network | | 96 | | 10.13.3.0/16 | - | 10.12.4.254 | network | | 97 | | 192.168.3.254/32 | - | 10.12.4.254 | network | | 98 | | 192.168.0.0/16 | 192.168.3.254 | 192.168.3.254 | Ebgp | 65513 | 99 | | 10.20.3.254/32 | - | 10.12.4.254 | network | | 100 | | 10.20.0.0/16 | 10.20.3.254 | 10.20.3.254 | Ebgp | 65512 | 101 | 102 | Verify in `on-premise-gateway` > BGP peers: 103 | 104 | | peer addr | local addr | ASN | Status | 105 | |---|---|---|---| 106 | | 10.12.4.254 | 192.168.3.254 | 65514 | connected | 107 | 108 | and learned routes 109 | 110 | | Network | next hop | src peer | origin | AS path | 111 | |---|---|---|---|---| 112 | | 192.168.0.0/16 | - | 192.168.3.254 | network | - | 113 | | 10.12.4.254/32 | - | 192.168.3.254 | network | - | 114 | | 10.12.0.0/16 | 10.12.4.254 | 10.12.4.254 | Ebgp | 65514 | 115 | | 10.13.2.0/16 | 10.12.4.254 | 10.12.4.254 | Ebgp | 65514 | 116 | | 10.13.1.0/16 | 10.12.4.254 | 10.12.4.254 | Ebgp | 65514 | 117 | | 10.13.3.0/16 | 10.12.4.254 | 10.12.4.254 | Ebgp | 65514 | 118 | | 10.20.0.0/16 | 10.12.4.254 | 10.20.3.254 | Ebgp | 65514-65512 | 119 | 120 | connect via SSH to `lin-onprem` machine onprem-2 and check RDP connection to `W10-onprem` machine: 121 | 122 | ``` 123 | nicola@lin-onprem:~$ telnet 192.168.1.4 3389 124 | ``` 125 | 126 | if the routing works you will receive the following message: 127 | 128 | ``` 129 | Trying 192.168.1.4... 130 | Connected to 192.168.1.4. 131 | Escape character is '^]'. 132 | ^CConnection closed by foreign host. 133 | ``` 134 | 135 | ## More information 136 | * BGP in Azure VPN Gateway Context: https://docs.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-bgp-overview 137 | * How to configure BGP: https://docs.microsoft.com/en-us/azure/vpn-gateway/bgp-howto 138 | -------------------------------------------------------------------------------- /scenarios/routing-without-bgp-fw.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: Routing cross hubs without BGP 2 | 3 | This solution shows how to configure routing between all spokes of two network hubs, using Azure Firewalls and virtual network peerings as shown in the following schema. 4 | 5 | ![Architecture](../images/routing-without-bgp-fw.png) 6 | _Download a [draw.io file](../images/routing-without-bgp-fw.drawio) of this architecture._ 7 | 8 | ## Pre-requisites 9 | 10 | In order to apply this solution: 11 | 12 | * deploy the `hub playground` 13 | * deploy the `hub 02 playground` 14 | 15 | ## Solution 16 | 17 | Once both deployments are completed, the following steps are required to enable routing between all spokes: 18 | 19 | 1. create a peering between `hub-lab-net` and `hub-lab-02-net` 20 | 2. forward all traffic from spokes to the corresponding Azure Firewall 21 | 3. Configure routing between the 2 firewalls 22 | 4. Configure a network policy on each firewall that allows the transit 23 | 24 | ### Create a peering between `hub-lab-net` and `hub-lab-02-net` 25 | 26 | Go to `hub-lab-net` > Peerings and click to **add**: 27 | 28 | * Peering Network Name: `hub-to-hub-02` 29 | * Traffic to remote virtual network: `allow` 30 | * Traffic forwarded from remote virtual network: `allow` 31 | * Virtual network gateway or Route Server `none` 32 | * Remote virtual network Peering link name: `hub-02-to-hub` 33 | * Virtual Network: `hub-lab-02-net` 34 | * Traffic to remote virtual network: `allow` 35 | * Traffic forwarded from remote virtual network: `allow` 36 | * Virtual network gateway or Route Server `none` 37 | 38 | click **add** 39 | 40 | ### Forward all traffic from spokes to the corresponding Azure Firewall 41 | 42 | Create the following route tables: 43 | 44 | * route table name: `all-to-fw-westeurope` 45 | * Region: `west europe` 46 | 47 | Routes 48 | 49 | | Name | Address prefix | Next hop type | Next hop IP address | 50 | |---|---|---|---| 51 | | all | 0.0.0.0/0 | Virtual appliance | 10.12.3.4 | 52 | 53 | Subnets 54 | 55 | | Network | subnet | 56 | |---|---| 57 | | spoke-01 | default | 58 | | spoke-01 | services| 59 | | spoke-02 | default | 60 | | spoke-02 | services| 61 | 62 | 63 | * route table name: `all-to-fw-northeurope` 64 | * region: `north europe` 65 | 66 | Routes 67 | 68 | | Name | Address prefix | Next hop type | Next hop IP address | 69 | |---|---|---|---| 70 | | all | 0.0.0.0/0 | Virtual appliance | 10.12.3.4 | 71 | 72 | Subnets 73 | 74 | | Network | subnet | 75 | |---|---| 76 | | spoke-03 | default | 77 | | spoke-03 | services| 78 | 79 | * route table name: `all-to-fw-northeurope-02` 80 | * region: `north europe` 81 | 82 | Routes 83 | 84 | | Name | Address prefix | Next hop type | Next hop IP address | 85 | |---|---|---|---| 86 | | all | 0.0.0.0/0 | Virtual appliance | 10.14.3.4 | 87 | 88 | Subnets 89 | 90 | | Network | subnet | 91 | |---|---| 92 | | spoke-04 | default | 93 | | spoke-04 | services| 94 | 95 | 96 | ### Configure routing between the 2 firewalls 97 | 98 | Create the following route tables: 99 | 100 | * route table name: `firewall-hub` 101 | * Region: `west europe` 102 | 103 | Routes 104 | 105 | | Name | Address prefix | Next hop type | Next hop IP address | 106 | |---|---|---|---| 107 | | all1 | 10.15.1.0/24 | Virtual appliance | 10.14.3.4 | 108 | | all | 0.0.0.0/0 | internet | | 109 | 110 | Subnets 111 | 112 | | Network | subnet | 113 | |---|---| 114 | | hub-lab-net | AzureFirewallSubnet | 115 | 116 | * route table name: `firewall-hub-02` 117 | * Region: `north europe` 118 | 119 | Routes 120 | 121 | | Name | Address prefix | Next hop type | Next hop IP address | 122 | |---|---|---|---| 123 | | all1 | 10.13.1.0/24 | Virtual appliance | 10.12.3.4 | 124 | | all2 | 10.13.2.0/24 | Virtual appliance | 10.12.3.4 | 125 | | all3 | 10.13.3.0/24 | Virtual appliance | 10.12.3.4 | 126 | | all | 0.0.0.0/0 | internet | | 127 | 128 | Subnets 129 | 130 | | Network | subnet | 131 | |---|---| 132 | | hub-lab-02-net | AzureFirewallSubnet | 133 | 134 | ### Configure a network policy on each firewall that allows the transit 135 | 136 | Create the following Azure IP Group: 137 | 138 | * Name: `all-spokes-ipg` 139 | * region: `west europe` 140 | * IP Addresses: 141 | * 10.13.1.0/24 142 | * 10.13.2.0/24 143 | * 10.14.3.0/24 144 | * 10.15.1.0/24 145 | 146 | Create the following Firewall Policy: 147 | 148 | * Name: `my-firewall-policy` 149 | * policy tier: `standard` 150 | * add rules collection: 151 | * name: `my-rule-collection` 152 | * type: `network` 153 | * priority: `1000` 154 | * action: `allow` 155 | 156 | Rules 157 | | Name | Source type | Source | Protocol | Destination Ports | Destination Type | Destination 158 | |---|---|---|---|---|---|---| 159 | | all | ipgroup | `all-spokes-ipg` | any | * | ip address | * | 160 | 161 | associate this policy to both firewalls: 162 | 163 | * Azure Firewall Manager -> Virtual Networks -> `hub-lab-net` -> Security Provider -> Add Policy -> `my-firewall-policy` 164 | * Azure Firewall Manager -> Virtual Networks -> `hub-lab-02-net` -> Security Provider -> Add Policy -> `my-firewall-policy` 165 | 166 | ## Test Solution 167 | Now a machine connected to the `spoke-01` virtual network can connect to a machine connected to the `spoke-04` networrk. 168 | 169 | 1. connect to `spoke-01-vm` via RDP/bastion 170 | 2. once on the vm, open remote desktop connection and connect to `10.11.1.4` (a Windows VM) 171 | 172 | As bonus, because we also enabled outbound internet traffic from all spokes via firewalls, open Microsoft Edge and be sure you are able to open a web site. -------------------------------------------------------------------------------- /scenarios/outbound-traffic-to-internet-firewall.md: -------------------------------------------------------------------------------- 1 | # Using Azure Firewall as a Gateway for All Outbound Traffic to the Internet 2 | 3 | Managing and securing outbound traffic is crucial for maintaining the integrity and performance of your network. Azure Firewall can be effectively used as a gateway for all outbound traffic to the internet. 4 | 5 | [Azure Firewall](https://learn.microsoft.com/en-us/azure/firewall/overview) is a managed, cloud-based network security service that protects your Azure Virtual Network resources. It is a stateful firewall as a service with built-in high availability and unrestricted cloud scalability. Azure Firewall provides both network and application-level protection across different subscriptions and virtual networks. 6 | 7 | Benefits of Using Azure Firewall for Outbound Traffic 8 | 9 | * Centralized Security Management: Azure Firewall allows you to manage and enforce [security policies](https://learn.microsoft.com/en-us/azure/firewall/policy-rule-sets) centrally, ensuring consistent security across your network. 10 | * Scalability: Azure Firewall [scales automatically](https://learn.microsoft.com/en-us/azure/firewall/firewall-performance) to meet your changing network traffic needs, providing high availability and resilience. 11 | * Advanced Threat Protection: With features like threat [intelligence-based filtering](https://learn.microsoft.com/en-us/azure/firewall/threat-intel), Azure Firewall can detect and block traffic from known malicious IP addresses and domains. 12 | * Integration with Other Azure Services: Azure Firewall integrates seamlessly with other Azure services like [Azure Monitor](https://learn.microsoft.com/en-us/azure/azure-monitor/fundamentals/overview), [Azure Sentinel](https://learn.microsoft.com/en-us/azure/sentinel/overview), and Azure Security Center, providing comprehensive security insights and management. 13 | 14 | In this walkthrough, I will show you how to set up Azure Firewall for this purpose. 15 | 16 | The target architecture will be the following: 17 | 18 | ![Azure Firewall as a Gateway for ll outbound Traffic to the Internet](../images/outbound-traffic-internet-firewall.png) 19 | 20 | _Download a [draw.io file](../images/outbound-traffic-internet-firewall.drawio) of this schema._ 21 | 22 | ## Pre-requisites 23 | 24 | In order to apply this solution, deploy the **hub** playground first. 25 | 26 | ## Solution 27 | 28 | ### Create routing table in westeurope and associate it to spokes 29 | 30 | Go to Azure portal > route tables > click on Create 31 | * Region: `westeurope` 32 | * Name: `spokes-we-to-hub-routes` 33 | * Click CREATE 34 | 35 | Go to Azure Portal > route tables > `spokes-we-to-hub-routes` > routes > ADD 36 | * Name: `to-firewall` 37 | * Destination type: IP Address 38 | * IP Address: `0.0.0.0/0` 39 | * Next hop type: virtual appliance 40 | * next hop address: `10.12.3.4` 41 | * click CREATE 42 | 43 | Go to Azure Portal > route tables > `spokes-we-to-hub-routes` > subnets > associate 44 | |subnet name | virtual network | 45 | |------------|-----------------| 46 | | default | spoke-01 | 47 | | default | spoke-02 | 48 | | services | spoke-01 | 49 | | services | spoke-02 | 50 | 51 | ### Create routing table in northeurope and associate it to spoke 52 | 53 | Go to Azure portal > route tables > click on Create 54 | * Region: `northeurope` 55 | * Name: `spokes-ne-to-hub-routes` 56 | * Click CREATE 57 | 58 | Go to Azure Portal > route tables > `spokes-ne-to-hub-routes` > routes > ADD 59 | * Name: `to-firewall` 60 | * Destination type: IP Address 61 | * IP Address: `0.0.0.0/0` 62 | * Next hop type: virtual appliance 63 | * next hop address: `10.12.3.4` 64 | * click CREATE 65 | 66 | Go to Azure Portal > route tables > `spokes-ne-to-hub-routes` > subnets > associate 67 | |subnet name | virtual network | 68 | |------------|-----------------| 69 | | default | spoke-03 | 70 | | services | spoke-03 | 71 | 72 | ### Configure azure firewall policy 73 | 74 | Go to Azure Portal > Firewall Policies > Create 75 | * Name: `my-firewall-policy` 76 | * policy tier: premium 77 | * parent policy: none 78 | * Rules > Add a Rule Collection 79 | * Name: `rfc1918-collection` 80 | * rule collection type: Network 81 | * priority: `1000` 82 | * rule collection action: deny 83 | * rule name: `block-intranet-traffic` 84 | * source type: ip address 85 | * source: `10.13.1.0/24,10.13.2.0/24,10.13.3.0/24` 86 | * protocol: TCP + UDP 87 | * destination ports: `*` 88 | * destination type: ip address 89 | * destination: `10.0.0.0/8,172.16.0.0/12,192.168.0.0/16` 90 | * add 91 | * Rules > Add a Rule Collection 92 | * Name: `internet-collection` 93 | * rule collection type: Network 94 | * priority: `10000` 95 | * rule collection action: allow 96 | * rule name: `to-internet-rule` 97 | * source type: ip address 98 | * source: `10.13.1.0/24,10.13.2.0/24,10.13.3.0/24` 99 | * protocol: TCP + UDP 100 | * destination ports: `*` 101 | * destination type: ip address 102 | * destination: `*` 103 | * add 104 | * create 105 | 106 | Go to Azure Portal > Firewall manager > Azure Firewall policies 107 | * select `my-firewall-policy` 108 | * manage association > associate vnets 109 | * select `hub-lab-net` 110 | * cick add 111 | 112 | ## Test solution 113 | 114 | Go to Azure portal > virtual machines > `spoke-01-vm` > connect via bastion 115 | 116 | Install and run Noisy as described in to generate traffic. 117 | 118 | Wait a couple of minutes then go to Azure Portal > firewall > `lab-firewall` > logs > query hub > network rule log data > run 119 | 120 | you will see the outbound traffic data to internet from `10.13.1.4` (`spoke-01-vm`) 121 | 122 | -------------------------------------------------------------------------------- /scenarios/elastic-san.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: create an Azure Elastic SAN and connect it to your Windows Server virtual machine 2 | 3 | Azure Elastic SAN is a fully integrated solution that simplifies deploying, scaling, managing, and configuring a storage area network (SAN), while also offering built-in cloud capabilities like high availability. Elastic SAN is interoperable with multiple types of compute resources such as Azure Virtual Machines, Azure VMware Solutions, and Azure Kubernetes Service. 4 | 5 | Azure Elastic SAN volumes can connect to a wide variety of compute resources using the Internet Small Computer Systems Interface (iSCSI) protocol. Because of this, rather than having to configure storage for each of your compute options, you can configure an Elastic SAN to serve as the storage solution for multiple compute options and manage it separately from each option. 6 | 7 | With an Elastic SAN, it's possible to scale your performance up to millions of IOPS, with double-digit GB/s throughput, and have single-digit millisecond latency. You can either increase your performance along with the storage capacity or increase the storage capacity without increasing the SAN's performance, potentially offering a lower total cost of ownership. 8 | 9 | One Elastic SAN can have one or more **Volume Groups**. 10 | One Volume Group can have one or more **Volumes**. A Volume Group is a management construct that you use to manage volumes at scale. Any settings or configurations applied to a Volume Group, such as virtual network rules, are inherited by any volumes associated with that Volume Group. 11 | 12 | A volume is a partition of the SAN's storage capacity. 13 | 14 | **In this page, I show how to connect an Azure Elastic SAN volume to a Windows Server 15 | virtual machine on Azure using the iSCSI protocol. ** 16 | The Elastic SAN and the Virtual Machine are in the context of a hub and spoke architecture, so all the communication between the VM and the virtual SAN will occur through a private virtual network. 17 | 18 | The target architecture will be the following: 19 | 20 | ![elastic SAN network architecture](../images/elastic-san.png) 21 | 22 | _Download a [draw.io file](../images/elastic-san.drawio) of this schema._ 23 | 24 | ## Pre-requisites 25 | 26 | In order to apply this solution you have to deploy the `hub-playground` only. 27 | FOr this sample I have installed all hub's`` resources in `northeurope`. 28 | 29 | ## Solution 30 | Go to Elastic SANs > Create. 31 | 32 | * Name: `elastic-01` 33 | * Region: `north europe` 34 | * Redundancy: `LRS` 35 | * Resource provisioning on Elastic SAN: 36 | * Base: `1TiB` 37 | * Additional: `10TiB` 38 | * click CREATE 39 | 40 | Go to Elastic SANs > `elastic-01` > Networking and `disable` the public network access. 41 | 42 | ### Create a Volume Group and Volume 43 | Go to Elastic SANs > `elastic-01` > Volume groups > create 44 | 45 | * Name: `group-01` 46 | * Networking > create private endpoint 47 | * Location: `north europe` 48 | * Name: `elastic-01-private-endp` 49 | * Volume Group: `group-01` 50 | * Virtual Network: `spoke-01` 51 | * Subnet: `services` 52 | * Integrate with privare DNS zone: `yes` (`privatelink.blob.core.windows.net`) 53 | * click CREATE 54 | 55 | Go to Elastic SANs > `elastic-01` > Volumes > Create Volume 56 | 57 | * Volume Group: `group-01` 58 | * Name: `my-volume-01` 59 | * Source Type: `None` 60 | * Size: `10 GiB` 61 | * click CREATE 62 | 63 | Go to Private DNS Zones > `privatelink.blob.core.windows.net` > Virtual Network Link > Add 64 | 65 | * Link Name: `spoke-01-network-link` 66 | * Virtual Network: `Spoke-01` 67 | * click **CREATE** 68 | 69 | ## Test solution 70 | 71 | Go to Virtual Machines > `spoke-01-vm` > connect via bastion 72 | 73 | Open a `run as administrator` powershell ISE 74 | 75 | Install Multipath I/O, enable multipath support for iSCSI devices, and set a default load balancing policy using the following script: 76 | 77 | ``` 78 | # Confirm iSCSI is running 79 | Get-Service -Name MSiSCSI 80 | 81 | # If it's not running, start it 82 | Start-Service -Name MSiSCSI 83 | 84 | # Set it to start automatically 85 | Set-Service -Name MSiSCSI -StartupType Automatic 86 | 87 | # Install Multipath-IO 88 | Add-WindowsFeature -Name 'Multipath-IO' 89 | 90 | # Verify if the installation was successful 91 | Get-WindowsFeature -Name 'Multipath-IO' 92 | 93 | # Enable multipath support for iSCSI devices 94 | Enable-MSDSMAutomaticClaim -BusType iSCSI 95 | 96 | # Set the default load balancing policy based on your requirements. In this example, we set it to round robin 97 | # which should be optimal for most workloads. 98 | mpclaim -L -M 2 99 | ``` 100 | 101 | Go to Elastic SANs > `elastic-01` > Volumes > `my-volume-01` > Connect > Windows tab > View the code > copy to clipboard. 102 | 103 | Paste the code on the same powershell in a new script page, save it as e `.ps1` file and execute it. 104 | 105 | output will be similar to the following: 106 | 107 | ``` 108 | my-volume-01 [iqn.xxxx-xx.net.windows.core.blob.ElasticSan.es-zzzzzzzzzz:my-volume-01]: Connecting to this volume 109 | Microsoft iSCSI Initiator Version 10.0 Build 17763 110 | 111 | The operation completed successfully. 112 | 113 | ... 114 | 115 | ``` 116 | 117 | `iSCSI initiator` app on the virtual machine can give you more information about the virtual SAN connected to the server. 118 | 119 | The disk from elastic SAN is now available as storage in Windows. 120 | 121 | Go to Start menu > Disk Management: a popup will show you that a new disk is available. Select OK. 122 | 123 | In the list of disks you will se a Basic 10Gb Disk available and unallocated. 124 | Now you can format it and use as local storage of the machine. 125 | 126 | # More information 127 | 128 | * 129 | * 130 | * 131 | 132 | -------------------------------------------------------------------------------- /on-prem-bicep/on-prem.bicep: -------------------------------------------------------------------------------- 1 | param location string = 'francecentral' 2 | param deployVM bool = true 3 | param username string = 'nicola' 4 | @secure() 5 | param password string = 'password.123' 6 | param virtualMachineSize string = 'Standard_D2_v5' 7 | param deployBastion bool = true 8 | param enableBgp bool = false 9 | param localGatewayFqdn string = '' 10 | 11 | var onPremNetworkName = 'on-prem-net' 12 | 13 | var bastionName = 'bastion-on-prem' 14 | var bastionIPName = 'bastion-on-prem-publicip' 15 | 16 | var vmOnPremDiskName = 'vm-w11-onprem-disk' 17 | var vmOnPremNicName = 'vm-w11-onprem-nic' 18 | var vmOnPremName = 'W11-onprem' 19 | var autoshutdownName = 'shutdown-computevm-${vmOnPremName}' 20 | 21 | var vnetGatewayIPName = 'onprem-gateway-virtualip' 22 | var vnetGatewayName = 'on-prem-gateway' 23 | 24 | var localGatewayName = 'lab-local-gateway' 25 | 26 | var subnets = concat( 27 | [ 28 | { name: 'GatewaySubnet', properties: { addressPrefix: '192.168.3.0/24' } } 29 | { name: 'DefaultSubnet', properties: { addressPrefix: '192.168.1.0/24' } } 30 | ], deployBastion ? [ 31 | { 32 | name: 'AzureBastionSubnet' 33 | properties: { 34 | addressPrefix: '192.168.2.0/24' 35 | } 36 | } 37 | ] : [] 38 | ) 39 | 40 | resource onpremvnet 'Microsoft.Network/virtualNetworks@2019-09-01' = { 41 | name: onPremNetworkName 42 | location: location 43 | properties: { addressSpace: { addressPrefixes: [ '192.168.0.0/16' ] } 44 | subnets: subnets 45 | } 46 | } 47 | 48 | resource bastionIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = if (deployBastion) { 49 | name: bastionIPName 50 | location: location 51 | sku: { name: 'Standard' } 52 | properties: { publicIPAllocationMethod: 'Static' } 53 | } 54 | 55 | resource bastion 'Microsoft.Network/bastionHosts@2019-09-01' = if (deployBastion) { 56 | name: bastionName 57 | location: location 58 | dependsOn: [ onpremvnet ] 59 | properties: { 60 | ipConfigurations: [ { 61 | name: 'ipconfig1' 62 | properties: { 63 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'AzureBastionSubnet') } 64 | publicIPAddress: { id: bastionIP.id } 65 | } 66 | } 67 | ] 68 | } 69 | } 70 | 71 | resource onpremdisk 'Microsoft.Compute/disks@2019-07-01' = if (deployVM) { 72 | name: vmOnPremDiskName 73 | location: location 74 | properties: { 75 | creationData: { createOption: 'Empty' } 76 | diskSizeGB: 128 77 | } 78 | } 79 | 80 | resource vmonpremnic 'Microsoft.Network/networkInterfaces@2019-09-01' = if (deployVM) { 81 | name: vmOnPremNicName 82 | location: location 83 | dependsOn: [ onpremvnet ] 84 | properties: { 85 | ipConfigurations: [ { 86 | name: 'ipconfig1' 87 | properties: { 88 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'DefaultSubnet') } 89 | privateIPAllocationMethod: 'Dynamic' 90 | } 91 | } 92 | ] 93 | } 94 | } 95 | 96 | resource vmonprem 'Microsoft.Compute/virtualMachines@2019-07-01' = if (deployVM) { 97 | name: vmOnPremName 98 | location: location 99 | dependsOn: [] 100 | properties: { 101 | hardwareProfile: { vmSize: virtualMachineSize } 102 | storageProfile: { 103 | imageReference: { publisher: 'MicrosoftWindowsDesktop', offer: 'windows-11', sku: 'win11-24h2-ent', version: 'latest' } 104 | dataDisks: [ { 105 | lun: 0 106 | name: vmOnPremDiskName 107 | createOption: 'Attach' 108 | managedDisk: { id: onpremdisk.id } 109 | } 110 | ] 111 | } 112 | osProfile: { 113 | computerName: vmOnPremName 114 | adminUsername: username 115 | adminPassword: password 116 | windowsConfiguration: { enableAutomaticUpdates: true } 117 | } 118 | networkProfile: { 119 | networkInterfaces: [ { 120 | id: vmonpremnic.id 121 | } 122 | ] 123 | } 124 | } 125 | } 126 | 127 | resource shutdownVm04 'microsoft.devtestlab/schedules@2018-09-15' = if (deployVM) { 128 | name: autoshutdownName 129 | location: location 130 | properties: { 131 | status: 'Enabled' 132 | taskType: 'ComputeVmShutdownTask' 133 | timeZoneId: 'UTC' 134 | dailyRecurrence: { time: '20:00' } 135 | notificationSettings: { status: 'Disabled' } 136 | targetResourceId: vmonprem.id 137 | } 138 | } 139 | 140 | resource vnetGatewayIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = { 141 | name: vnetGatewayIPName 142 | location: location 143 | sku: { name: 'Standard' } 144 | properties: { publicIPAllocationMethod: 'Static'} 145 | } 146 | 147 | resource vnetGateway1 'Microsoft.Network/virtualNetworkGateways@2022-09-01' = { 148 | name: vnetGatewayName 149 | location: location 150 | dependsOn: [ onpremvnet ] 151 | properties: { 152 | ipConfigurations: [ { 153 | name: 'ipconfig1' 154 | properties: { 155 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'GatewaySubnet') } 156 | publicIPAddress: { id: vnetGatewayIP.id } 157 | } 158 | } 159 | ] 160 | gatewayType: 'Vpn' 161 | vpnType: 'RouteBased' 162 | enableBgp: enableBgp 163 | bgpSettings: enableBgp ? { 164 | asn: 65510 165 | } : null 166 | sku: { name: 'VpnGw1', tier: 'VpnGw1' } 167 | } 168 | } 169 | 170 | // if we enable bgp we also need a local gateway for the connection 171 | resource localGateway 'Microsoft.Network/localNetworkGateways@2022-09-01' = if (enableBgp) { 172 | name: localGatewayName 173 | location: location 174 | properties: { 175 | bgpSettings: { 176 | asn: 65511 177 | bgpPeeringAddress: '192.168.3.254' 178 | } 179 | localNetworkAddressSpace: { 180 | addressPrefixes: [ 181 | '10.12.0.0/16' 182 | '10.13.1.0/24' 183 | '10.13.2.0/24' 184 | '10.13.3.0/24' 185 | ] 186 | } 187 | fqdn: localGatewayFqdn 188 | } 189 | } 190 | -------------------------------------------------------------------------------- /scenarios/ping-any-to-any-firewall.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: Allows machines in ANY spoke to communicate with ANY machine in ANY other spoke (Azure Firewall) 2 | 3 | In the dynamic realm of cloud computing, efficiently managing and scaling your networks are of paramount importance. 4 | An integral part of this network management is setting up a 'Hub and Spoke' topology and enabling connectivity between spokes. 5 | 6 | In the Hub and Spoke model, the **routing between different Spokes is a significant element**. Each spoke identify often an application or a workload, and there enterprise elements have to interact each other. 7 | 8 | There are 3 main approaches to allow this traffic: 9 | 10 | * Direct spokes peering (implemented by-hand or automated with a Virtual Networks Manager) 11 | * Virtual Network Gateway in the Hub 12 | * Firewall in the Hub 13 | 14 | each approach has pros and cons. 15 | 16 | ### Direct Spokes Peering 17 | Pros: 18 | * Direct Spokes peering allows more efficient data transfer as it reduces latency caused by passing through the hub. 19 | 20 | Cons: 21 | * It may create a complex network with multiple connection points, making it harder to monitor and manage. 22 | * Potential security risks can increase as each Spoke is exposed to the other, increasing the risk of vulnerability. 23 | 24 | ### Network Gateway in the Hub as Default Gateway 25 | 26 | Pros: 27 | 28 | * It is an appliance already used for S2S VPN so you do not need an additional element to manage. 29 | * The network topology is simple because only one peering for each spoke is required. 30 | * Streamlines operations by providing a uniform route for data traffic, potentially increasing efficiency 31 | 32 | Cons: 33 | 34 | * It might introduce additional latency as all traffic must pass through the hub before reaching the destination Spoke. 35 | * Potential for the hub add for the gateway **to become a bottleneck**, because it already is used also for S2S and P2S VPNs. 36 | * Potential security risks can increase as each Spoke is exposed to the other, increasing the risk of vulnerability. 37 | 38 | ### Firewall in the Hub as Network Appliance 39 | 40 | Pros: 41 | 42 | * Streamlines operations by providing a uniform route for data traffic, potentially increasing efficiency 43 | * Enhanced security measures as the firewall provides a protective layer preventing unauthorized access. 44 | * It ensures that all traffic can be inspected and sanitized before it reaches its destination. 45 | * Centralized network policy enforcement improves manageability and control. 46 | 47 | Cons 48 | 49 | * It might introduce additional latency as all traffic must pass through the hub before reaching the destination Spoke. 50 | * Increased costs due to the need for the firewall appliance and traffic logging 51 | * Potential for the firewall to be a single point of failure if not properly configured. 52 | 53 | 54 | In this solution, I show you how configure the routing between spokes using an **Azure Firewall** in the middle. 55 | 56 | Azure Firewall (PaaS) holds a significant edge over a traditional firewall on Azure. 57 | Here are a few reasons why: 58 | * **Fully Integrated**: Azure Firewall is natively integrated with Azure. This means that it works seamlessly with other Azure services and tools, including Azure Monitor, Azure Security Center and Azure Log Analytics. 59 | * **Scalability**: Unlike traditional firewalls which may need manual configuration to scale, Azure Firewall PaaS automatically scales up and down based on network traffic loads. 60 | * **Advanced Threat Intelligence**: Azure Firewall PaaS is backed by Microsoft's global threat intelligence, helping to identify and block known malicious IPs and domains. 61 | * **Built-in High Availability**: Azure Firewall PaaS offers built-in high availability, eliminating the need for additional load balancers or other devices to ensure constant uptime. 62 | * **Zero Maintenance**: As a fully managed service, Azure Firewall PaaS doesn't require any patching or maintenance. 63 | 64 | The resulting overall architecture is shown in the following schema. 65 | 66 | ![any to any routing via Azure Firewall](/images/any-to-any-routing-firewall.png) 67 | 68 | _Download a [draw.io file](../images/any-to-any-routing.drawio) of this architecture._ 69 | 70 | 71 | ## Pre-requisites 72 | 73 | In order to apply this solution you have to deploy hub playground only. 74 | 75 | ## Solution 76 | 77 | In order to allow this communication, via **Azure Firewall**: 78 | 79 | Create the following route table in `west europe`: `spokes-we-to-hub-routes` 80 | 81 | | Name | Address Prefix | Next hop type | Next hop IP addr | 82 | |---|---|---|---| 83 | | to-spoke-01 | 10.13.1.0/24 | Virtual Appliance | 10.12.3.4 | 84 | | to-spoke-02 | 10.13.2.0/24 | Virtual Appliance | 10.12.3.4 | 85 | | to-spoke-03 | 10.13.3.0/24 | Virtual Appliance | 10.12.3.4 | 86 | 87 | 88 | | subnet Name | Virtual Network | 89 | |---|---| 90 | | default | spoke-01 | 91 | | services | spoke-01 | 92 | | default | spoke-02 | 93 | | services | spoke-02 | 94 | 95 | Create the following route table in `north europe`: `spokes-ne-to-hub-routes` 96 | 97 | | Name | Address Prefix | Next hop type | Next hop IP addr | 98 | |---|---|---|---| 99 | | to-spoke-01 | 10.13.1.0/24 | Virtual Appliance | 10.12.3.4 | 100 | | to-spoke-02 | 10.13.2.0/24 | Virtual Appliance | 10.12.3.4 | 101 | 102 | | subnet Name | Virtual Network | 103 | |---|---| 104 | | default | spoke-03 | 105 | | services | spoke-03 | 106 | 107 | Create the following `IP Groups` in `west europe`: 108 | * `group-spoke-01`: 10.13.1.0/24 109 | * `group-spoke-02`: 10.13.2.0/24 110 | * `group-spoke-03`: 10.13.3.0/24 111 | 112 | Create the following Firewall Policy: `hub-fw-policy` 113 | 114 | **Network Rules**: 115 | * Rule Collection Name: `my-collection` 116 | * Rule Collection type: Network 117 | * Priority: `1000` 118 | * Rule Colletion action: `Allow` 119 | * Rule Collection group: `DefaultNetworkRuleCollectionGroup` 120 | 121 | 122 | | rule name | source | port | protocol | destination | 123 | |---|---|---|---|---| 124 | | all-to-all | group-spoke-01
group-spoke-02
group-spoke-03 | * | Any | group-spoke-01
group-spoke-02
group-spoke-03 | 125 | 126 | Associate the policy `hub-fw-policy` to `lab-firewall` via Firewall Manager. 127 | 128 | ## Test Solution 129 | Test connections using remote desktop client and ssh from one machine to another. 130 | 131 | # More information 132 | * Azure [Firewall](https://learn.microsoft.com/en-us/azure/firewall/overview) 133 | * Azure [route tables](https://learn.microsoft.com/en-us/azure/virtual-network/manage-route-table) 134 | * Azure [Firewall policy](https://learn.microsoft.com/en-us/azure/firewall-manager/policy-overview) -------------------------------------------------------------------------------- /any-to-any-bicep/any-to-any.bicep: -------------------------------------------------------------------------------- 1 | @description('Basic, Standard or Premium tier') 2 | @allowed([ 'Basic', 'Standard', 'Premium' ]) 3 | param firewallTier string = 'Premium' 4 | 5 | param disableBgpRoutePropagation bool = false 6 | 7 | @description('Additional IP addresses or subnets to add in the firewall rules') 8 | param allowIpAddresses array = [] 9 | 10 | var routeTables_all_to_firewall_we_name = 'all-to-firewall-we' 11 | var routeTables_all_to_firewall_ne_name = 'all-to-firewall-ne' 12 | 13 | var hubName = 'hub-lab-net' 14 | var spoke01Name = 'spoke-01' 15 | var spoke02Name = 'spoke-02' 16 | var spoke03Name = 'spoke-03' 17 | 18 | param locationWE string = 'westeurope' 19 | param locationNE string = 'northeurope' 20 | 21 | var firewallName = 'lab-firewall' 22 | var firewallIPName = 'lab-firewall-ip' 23 | var firewallIpAddress = '10.12.3.4' 24 | 25 | module fwPolicy './fw-policy.bicep' = { 26 | name: 'fwPolicyDeploy' 27 | params: { 28 | firewallTier: firewallTier 29 | locationWE: locationWE 30 | allowIpAddresses: allowIpAddresses 31 | } 32 | } 33 | 34 | resource routeTableWE 'Microsoft.Network/routeTables@2020-05-01' = { 35 | name: routeTables_all_to_firewall_we_name 36 | location: locationWE 37 | properties: { 38 | disableBgpRoutePropagation: disableBgpRoutePropagation 39 | routes: [ 40 | { 41 | name: 'all-to-firewall-we' 42 | properties: { 43 | addressPrefix: '0.0.0.0/0' 44 | nextHopType: 'VirtualAppliance' 45 | nextHopIpAddress: firewallIpAddress 46 | } 47 | } 48 | ] 49 | } 50 | } 51 | 52 | resource routeTableGateway 'Microsoft.Network/routeTables@2020-05-01' = { 53 | name: 'gateway-route' 54 | location: locationWE 55 | properties: { 56 | disableBgpRoutePropagation: disableBgpRoutePropagation 57 | routes: [ 58 | { 59 | name: 'spoke-01' 60 | properties: { 61 | addressPrefix: '10.13.1.0/24' 62 | nextHopType: 'VirtualAppliance' 63 | nextHopIpAddress: firewallIpAddress 64 | } 65 | } 66 | { 67 | name: 'spoke-02' 68 | properties: { 69 | addressPrefix: '10.13.2.0/24' 70 | nextHopType: 'VirtualAppliance' 71 | nextHopIpAddress: firewallIpAddress 72 | } 73 | } 74 | { 75 | name: 'spoke-03' 76 | properties: { 77 | addressPrefix: '10.13.3.0/24' 78 | nextHopType: 'VirtualAppliance' 79 | nextHopIpAddress: firewallIpAddress 80 | } 81 | } 82 | ] 83 | } 84 | } 85 | 86 | resource routeTableNE 'Microsoft.Network/routeTables@2020-05-01' = { 87 | name: routeTables_all_to_firewall_ne_name 88 | location: locationNE 89 | properties: { 90 | disableBgpRoutePropagation: disableBgpRoutePropagation 91 | routes: [ 92 | { 93 | name: 'all-to-firewall-ne' 94 | properties: { 95 | addressPrefix: '0.0.0.0/0' 96 | nextHopType: 'VirtualAppliance' 97 | nextHopIpAddress: firewallIpAddress 98 | } 99 | } 100 | ] 101 | } 102 | } 103 | 104 | resource subnetS01default 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = { 105 | name: '${spoke01Name}/default' 106 | properties: { 107 | addressPrefix: '10.13.1.0/26' 108 | routeTable: { 109 | id: routeTableWE.id 110 | } 111 | privateEndpointNetworkPolicies: 'Enabled' 112 | } 113 | } 114 | 115 | resource subnetS01services 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = { 116 | name: '${spoke01Name}/services' 117 | dependsOn: [ // possible race condition where the route table is being associated with two different subnets at the same time 118 | subnetS01default 119 | ] 120 | properties: { 121 | addressPrefix: '10.13.1.64/26' 122 | routeTable: { 123 | id: routeTableWE.id 124 | } 125 | privateEndpointNetworkPolicies: 'Enabled' 126 | } 127 | } 128 | 129 | resource subnetS02default 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = { 130 | name: '${spoke02Name}/default' 131 | properties: { 132 | addressPrefix: '10.13.2.0/26' 133 | routeTable: { 134 | id: routeTableWE.id 135 | } 136 | privateEndpointNetworkPolicies: 'Enabled' 137 | } 138 | } 139 | 140 | resource subnetS02services 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = { 141 | name: '${spoke02Name}/services' 142 | dependsOn: [ // possible race condition where the route table is being associated with two different subnets at the same time 143 | subnetS02default 144 | ] 145 | properties: { 146 | addressPrefix: '10.13.2.64/26' 147 | routeTable: { 148 | id: routeTableWE.id 149 | } 150 | privateEndpointNetworkPolicies: 'Enabled' 151 | } 152 | } 153 | 154 | resource subnetS03default 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = { 155 | name: '${spoke03Name}/default' 156 | properties: { 157 | addressPrefix: '10.13.3.0/26' 158 | routeTable: { 159 | id: routeTableNE.id 160 | } 161 | privateEndpointNetworkPolicies: 'Enabled' 162 | } 163 | } 164 | 165 | resource subnetS03services 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = { 166 | name: '${spoke03Name}/services' 167 | dependsOn: [ // possible race condition where the route table is being associated with two different subnets at the same time 168 | subnetS03default 169 | ] 170 | properties: { 171 | addressPrefix: '10.13.3.64/26' 172 | routeTable: { 173 | id: routeTableNE.id 174 | } 175 | privateEndpointNetworkPolicies: 'Enabled' 176 | } 177 | } 178 | 179 | resource subnetGateway 'Microsoft.Network/virtualNetworks/subnets@2020-05-01' = { 180 | name: '${hubName}/GatewaySubnet' 181 | properties: { 182 | addressPrefix: '10.12.4.0/24' 183 | routeTable: { 184 | id: routeTableGateway.id 185 | } 186 | } 187 | } 188 | 189 | resource firewallIP 'Microsoft.Network/publicIPAddresses@2019-09-01' = { 190 | name: firewallIPName 191 | location: locationWE 192 | sku: { name: 'Standard' } 193 | properties: { publicIPAllocationMethod: 'Static' } 194 | } 195 | 196 | resource azureFirewalls_lab_firewall_name_resource 'Microsoft.Network/azureFirewalls@2022-07-01' = { 197 | name: firewallName 198 | location: locationWE 199 | properties: { 200 | sku: { name: 'AZFW_VNet', tier: firewallTier } 201 | ipConfigurations: [ { 202 | name: 'ipconfig1' 203 | properties: { 204 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', hubName, 'AzureFirewallSubnet') } 205 | publicIPAddress: { id: firewallIP.id } 206 | } 207 | } 208 | ] 209 | firewallPolicy: { 210 | id: fwPolicy.outputs.policyid 211 | } 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /scenarios/p2s-vpn-certificate.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: Configure a P2S VPN with Certificate Authentication 2 | 3 | This solution shows how to configure a P2S VPN to allow individual clients running Windows, Linux, or macOS to an Azure Virtual Network using **Azure certificate authentication**. Point-to-site VPN connections are useful when you want to connect to your VNet from a remote location, such as when you're telecommuting from home or a conference. 4 | 5 | Point-to-site VPN can use one of the following protocols: 6 | 7 | * **OpenVPN® Protoco**l, an SSL/TLS based VPN protocol. A TLS VPN solution can penetrate firewalls, since most firewalls open TCP port 443 outbound, which TLS uses. OpenVPN can be used to connect from Android, iOS (versions 11.0 and above), Windows, Linux, and Mac devices (macOS versions 10.13 and above). 8 | 9 | * S**ecure Socket Tunneling Protocol** (SSTP), a proprietary TLS-based VPN protocol. A TLS VPN solution can penetrate firewalls, since most firewalls open TCP port 443 outbound, which TLS uses. SSTP is only supported on Windows devices. Azure supports all versions of Windows that have SSTP and support TLS 1.2 (Windows 8.1 and later). 10 | 11 | * **IKEv2 VPN**, a standards-based IPsec VPN solution. IKEv2 VPN can be used to connect from Mac devices (macOS versions 10.11 and above). 12 | 13 | Before Azure accepts a P2S VPN connection, the user has to be authenticated first. There are two mechanisms that Azure offers to authenticate a connecting user: 14 | 15 | * **Certificate Authentication**: When using the native Azure certificate authentication, a client certificate that is present on the device is used to authenticate the connecting user. 16 | * **Azure Active Directory Authentication**: Azure AD authentication allows users to connect to Azure using their Azure Active Directory credentials. Native Azure AD authentication is only supported for OpenVPN protocol and also requires the use of the Azure VPN Client 17 | 18 | More information: 19 | * on P2S VPN: https://learn.microsoft.com/en-us/azure/vpn-gateway/point-to-site-about 20 | * configure Certificate Authentication: https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-howto-point-to-site-resource-manager-portal 21 | 22 | 23 | ## Pre-requisites 24 | 25 | In order to apply this solution: 26 | 27 | 1. deploy the **hub** playground 28 | 2. deploy the **on-premise** playground 29 | 30 | ## Solution 31 | 32 | ### Generate Root Certificates 33 | Point-to-site connections use certificates to authenticate. The PowerShell cmdlets that you use will use to generate certificates are part of the operating system and don't work on other versions of Windows. The host operating system is only used to generate the certificates. Once the certificates are generated, you can upload them or install them on any supported client operating system. 34 | 35 | * Access to `W10-OnPrem` via RDP/Bastion 36 | * Create the self-signed root certificate `playground-certificate` with the following powershell command 37 | 38 | ``` 39 | $cert = New-SelfSignedCertificate -Type Custom -KeySpec Signature ` 40 | -Subject "CN=playground-certificate" -KeyExportPolicy Exportable ` 41 | -HashAlgorithm sha256 -KeyLength 2048 ` 42 | -CertStoreLocation "Cert:\CurrentUser\My" -KeyUsageProperty Sign -KeyUsage CertSign 43 | ``` 44 | 45 | * create a client certificate `playground-client-certificate` with the following powershell command 46 | 47 | ``` 48 | New-SelfSignedCertificate -Type Custom -DnsName MyP2SClientCert01 -KeySpec Signature ` 49 | -Subject "CN=playground-client-certificate" -KeyExportPolicy Exportable ` 50 | -HashAlgorithm sha256 -KeyLength 2048 ` 51 | -CertStoreLocation "Cert:\CurrentUser\My" ` 52 | -Signer $cert -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.2") 53 | 54 | ``` 55 | 56 | For more information, [this article shows](https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-certificates-point-to-site) you how to create a self-signed root certificate and generate client certificates using PowerShell on Windows 10 or later, or Windows Server 2016 or later. 57 | 58 | ### Export the root certificate 59 | 60 | * Open `certmgr.exe` and locate the self-signed root certificate, in "Certificates - **Current User**\Personal\Certificates\`playground-certificate`", and right-click. Click All Tasks -> Export. This opens the Certificate Export Wizard. 61 | * select **NO, do NOT export the private key** 62 | * select `Base-64 encoded (.CER)` 63 | * filename: `c:\root-cert-exported.cer` 64 | 65 | ### Export the client certificate 66 | Because we will test the P2S VPN from the client computer used to create the certificate, you do not need to export it. If you want to create a P2S connection from a client computer other than the one you used to generate the client certificates, you need to install a client certificate. When installing a client certificate, you need the password that was created when the client certificate was exported. 67 | 68 | Make sure the client certificate was exported as a .pfx along with the entire certificate chain (which is the default). Otherwise, the root certificate information isn't present on the client computer and the client won't be able to authenticate properly. 69 | 70 | more information: https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-certificates-point-to-site#clientexport 71 | 72 | 73 | ### Add the VPN client address pool 74 | The client address pool is a range of private IP addresses that you specify. The clients that connect over a point-to-site VPN dynamically receive an IP address from this range. Use a private IP address range that doesn't overlap with the on-premises location that you connect from, or the VNet that you want to connect to. If you configure multiple protocols and SSTP is one of the protocols, then the configured address pool is split between the configured protocols equally. 75 | 76 | Go to `lab-gateway` > Point 2 Site Configuration > Configure Now: 77 | * Address pool: `10.14.1.0/24` 78 | * Tunnel Type: `Open VPN` 79 | * Authentication Type: `Azure Certificate` 80 | * Root Certificate Name: `my-root-certificate` 81 | * open file `c:\root-cert-exported.cer` saved before with Notepad and copy all the text between ---BEGIN--- and ---END CERTIFICATE--- rows in the `public certificate data` field: 82 | 83 | ``` 84 | -----BEGIN CERTIFICATE----- 85 | MIIC/TCCAeWgAwIBAgIQF2MY6gVnqKRB9OqCSvetITANBgkqhkiG9w0BAQsFADAh 86 | MR8wHQYDVQQDDBZwbGF5Z3JvdW5kLWNlcnRpZmljYXRlMB4XDTIyMTAxOTA4MzE1 87 | NFoXDTIzMTAxOTA4NTE1NFowITEfMB0GA1UEAwwWcGxheWdyb3VuZC1jZXJ0aWZp 88 | 89 | ... ... 90 | 91 | jHX5Wtexk5abytL0fjBB4SMzPkzVC+bGmSBdsZHuYnsmYyw9KaflYAAob9WK3a8U 92 | gQ== 93 | -----END CERTIFICATE----- 94 | ``` 95 | 96 | * click **SAVE** 97 | 98 | ## Test Solution 99 | * Download VPN connection data from Azure Portal on `W10-OnPrem`. 100 | * Download and install the latest version of the Azure VPN Client install files using one of the following links: https://aka.ms/azvpnclientdownload 101 | * open Azure VPN Client and click on (+) > import 102 | * select azurevpnconfig.xml 103 | * Authentication Type: `Certificate` 104 | * Certificate Information: select `playground-client-certificate` 105 | * Click SAVE 106 | * Click CONNECT 107 | 108 | Once connected, open a `remote desktop connection` app and connect to `10.13.1.4` 109 | -------------------------------------------------------------------------------- /scenarios/frontdoor.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: Expose via Azure Front Door an internal web server located on a VM on a spoke 2 | 3 | This scenario demonstrates how to securely expose an internal web server using Azure Front Door without directly exposing the VM to the internet. 4 | 5 | > Azure Front Door is a modern cloud Content Delivery Network (CDN) service that provides fast, reliable, and secure access to your global applications. 6 | It operates at the edge of Microsoft's global network, directing web traffic to the fastest and most available application backend. 7 | Front Door provides your users with a global, scalable, and secure entry point to your web applications. 8 | 9 | Keeping the origin server (backend) private offers several security benefits: 10 | 11 | 1. **Reduced attack surface**: Prevents direct attacks on your origin infrastructure 12 | 2. **DDoS protection**: Front Door helps mitigate DDoS attacks by absorbing traffic at the edge 13 | 3. **Enhanced security posture**: Implements defense-in-depth by hiding your backend resources 14 | 4. **Enforced traffic routing**: All traffic must pass through Front Door's security controls 15 | 5. **Centralized WAF protection**: Web Application Firewall can filter malicious traffic at the edge 16 | 17 | Azure Frontdoor, Azure Application gateway and Azure Load balancers all allow to expose an internal resource on internet. When choose azure frontdoor vs Application gatewa or Azure load balancers? 18 | 19 | Azure Frontdoor and Azure Application gateway, while both services provide WAF and routing capabilities, they have different strengths: 20 | 21 | - **Global vs Regional**: Front Door is a global service, while Application Gateway is regional 22 | - **Edge security**: Front Door filters traffic at Microsoft's edge locations worldwide 23 | - **Scalability**: Front Door auto-scales globally without manual intervention 24 | - **Protocol optimization**: Front Door optimizes HTTP/S protocols over Microsoft's global network 25 | - **Multi-region architecture**: Better suited for applications with global reach and multi-region backends 26 | 27 | Azure Front Door provides also several advantages over a simple public load balancer: 28 | 29 | - **Content delivery network**: Caches content closer to users for faster delivery 30 | - **SSL offloading**: Handles SSL/TLS termination at the edge 31 | - **Intelligent routing**: Directs users to closest/healthiest backend 32 | - **Security features**: Built-in WAF capabilities to protect against web vulnerabilities 33 | - **Global health probes**: Continuous monitoring of backend health across regions 34 | 35 | The final architecture involves the following resources: 36 | 37 | - Azure Front Door Premium with WAF 38 | - Private Link Service 39 | - Internal Load Balancer 40 | - Virtual Machine in a spoke network running a web server 41 | 42 | as shown below: 43 | 44 | ![frontdoor architecture](../images/frontdoor.png) 45 | 46 | _Download a [draw.io file](../images/frontdoor.drawio) of this schema._ 47 | 48 | ## Pre-requisites 49 | 50 | In order to apply this solution you have to deploy the `hub-playground` only. 51 | For this sample I have installed all hub's resources in `northeurope`. 52 | 53 | ## Solution 54 | 55 | ### Activate a web server on `spoke-01-vm` 56 | 57 | Go to Azure Portal > virtual machines > spoke-01-vm > connect via bastion 58 | 59 | Once logged in, open PowerShell and type the following: 60 | 61 | ``` powershell 62 | Install-WindowsFeature -name Web-Server -IncludeManagementTools 63 | Remove-Item -Path 'C:\inetpub\wwwroot\iisstart.htm' 64 | Add-Content -Path 'C:\inetpub\wwwroot\iisstart.htm' -Value $($env:computername) 65 | ``` 66 | 67 | ### Create an internal load balancer 68 | 69 | Go to Azure Portal > Load balancers > Create > standard load balancer 70 | 71 | #### Basic 72 | * Name: `nlb-01` 73 | * Region: `northeurope` 74 | 75 | #### Frontend IP configuration 76 | 77 | Add frontend IP Configuration: 78 | * Name: `nlb-frontend-01` 79 | * IP version: IPv4 80 | * Virtual network: `spoke-01` 81 | * Subnet: `default` 82 | * Assignment: Dynamic 83 | * Availability zone: Zone redundant 84 | 85 | 86 | #### Backend pools 87 | 88 | Add a backend pool: 89 | * Name: `nlb-backend-01` 90 | * Backend pool configuration: NIC 91 | * Resource name: `spoke-01-vm` 92 | * Click **SAVE** 93 | 94 | #### Inbound rules 95 | 96 | Add a load balancing rule: 97 | 98 | * Name: `lb-rule-01` 99 | * IP version: IPv4 100 | * Frontend IP address: `nlb-frontend-01` 101 | * Backend pool: `nlb-backend-01` 102 | * Protocol: TCP 103 | * Inbound port: `80` 104 | * Backend port: `80` 105 | * Create a new health probe: 106 | * Name: `probe-01` 107 | * Protocol: HTTP 108 | * Port: `80` 109 | * Path: `/` 110 | * Interval: `5` sec 111 | * Click **SAVE** 112 | * Click **SAVE** 113 | 114 | #### Outbound rule 115 | 116 | None. 117 | 118 | #### Review and create 119 | 120 | Click **CREATE** 121 | 122 | ### Create a Private Link Service 123 | 124 | Go to Azure Portal > Private Link Services > Create 125 | 126 | #### Basics 127 | * Name: `pls-nlb-01` 128 | * Region: `northeurope` 129 | 130 | #### Outbound settings 131 | * Load Balancer: `nlb-01` 132 | * Load balancer frontend IP address: `10.13.1.5` 133 | * Source NAT subnet: `default` 134 | 135 | #### Review and create 136 | Click **CREATE**. 137 | 138 | ### Create an Azure Front Door 139 | 140 | Go to Azure Portal > Front Doors > Create > Azure Front Door/quick create 141 | 142 | #### Configuration 143 | * Name: `afd-01` 144 | * Tier: Premium 145 | * Endpoint name: `afd-01` 146 | * SKU: Standard 147 | * Origin type: Custom 148 | * Origin host name: `10.13.1.5` (internal load balancer, front end IP) 149 | * Private link service: ENABLE 150 | * Select private link: In my directory 151 | * Resource: `pls-nlb-01` 152 | * Request message: `please approve me` 153 | * WAF policy: Create new: 154 | * Name: `afdwaf01` 155 | * Bot protection: ON 156 | * Click **CREATE** 157 | * Click **CREATE** 158 | 159 | Go to Azure Portal > Private Link Services > **pls-nlb-01** > Private endpoint connections. 160 | You will find 1 private endpoint connection, select it, then click to **APPROVE** 161 | 162 | The connection state should change to Approved. It might take a couple of minutes for the connection to fully establish. 163 | You can now access your internal load balancer from Azure Front Door. 164 | 165 | Go to Azure Portal > **afd-01** > Front Door Manager > **default-route** > Update and change forwarding protocol to **HTTP only** (because your backend VM exposes an HTTP-only web server). 166 | 167 | ## Test solution 168 | Because Azure Front Door is a globally distributed service, each update can require up to 10-20 minutes to propagate everywhere. After this time, open the `afd-01` endpoint hostname public URL found on the overview page (something like `https://afd-01-abcefgh.b01.azurefd.net`). You will see `spoke-01-vm` displayed in your browser. 169 | 170 | ## More information 171 | 172 | * [Azure Front Door with Private Link](https://learn.microsoft.com/en-us/azure/frontdoor/private-link) 173 | * [Creating a Private Link Service](https://learn.microsoft.com/en-us/azure/private-link/create-private-link-service-portal?tabs=dynamic-ip) 174 | * [Enabling Private Link with internal load balancers](https://learn.microsoft.com/en-us/azure/frontdoor/standard-premium/how-to-enable-private-link-internal-load-balancer) -------------------------------------------------------------------------------- /on-prem-2-bicep/on-prem-2.bicep: -------------------------------------------------------------------------------- 1 | param location string = 'germanywestcentral' 2 | param username string = 'nicola' 3 | @secure() 4 | param password string = 'password.123' 5 | param virtualMachineSKU string = 'Standard_D2_v5' 6 | 7 | var onPremNetworkName = 'on-prem-net-2' 8 | 9 | var bastionName = 'bastion-on-prem-2' 10 | var bastionIPName = 'bastion-on-prem-2-publicip' 11 | 12 | var vmOnPremLinux01DiskName = 'vm-linux-onprem-01-disk' 13 | var vmOnPremLinux01NicName = 'vm-linux-01-onprem-nic' 14 | var vmOnPremLinux01Name = 'lin-onprem' 15 | var autoshutdownLinux01Name = 'shutdown-computevm-${vmOnPremLinux01Name}' 16 | 17 | var vmOnPremLinux02DiskName = 'vm-linux-onprem-02-disk' 18 | var vmOnPremLinux02NicName = 'vm-linux-02-onprem-nic' 19 | var vmOnPremLinux02Name = 'lin-onprem-2' 20 | var autoshutdownLinux02Name = 'shutdown-computevm-${vmOnPremLinux02Name}' 21 | 22 | var vnetGatewayIPName = 'onprem-2-gateway-virtualip' 23 | var vnetGatewayName = 'on-prem-2-gateway' 24 | 25 | resource onpremvnet2 'Microsoft.Network/virtualNetworks@2019-09-01' = { 26 | name: onPremNetworkName 27 | location: location 28 | properties: { addressSpace: { addressPrefixes: [ '10.20.0.0/16' ] } 29 | subnets: [ 30 | { name: 'GatewaySubnet', properties: { addressPrefix: '10.20.3.0/24' } } 31 | { name: 'AzureBastionSubnet', properties: { addressPrefix: '10.20.2.0/24' } } 32 | { name: 'DefaultSubnet', properties: { addressPrefix: '10.20.1.0/24' } } 33 | ] 34 | } 35 | } 36 | 37 | resource bastionIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = { 38 | name: bastionIPName 39 | location: location 40 | sku: { name: 'Standard' } 41 | properties: { publicIPAllocationMethod: 'Static' } 42 | } 43 | 44 | resource bastion 'Microsoft.Network/bastionHosts@2019-09-01' = { 45 | name: bastionName 46 | location: location 47 | dependsOn: [ onpremvnet2 ] 48 | properties: { 49 | ipConfigurations: [ { 50 | name: 'ipconfig1' 51 | properties: { 52 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'AzureBastionSubnet') } 53 | publicIPAddress: { id: bastionIP.id } 54 | } 55 | } 56 | ] 57 | } 58 | } 59 | 60 | resource onpremdisk01 'Microsoft.Compute/disks@2019-07-01' = { 61 | name: vmOnPremLinux01DiskName 62 | location: location 63 | properties: { 64 | creationData: { createOption: 'Empty' } 65 | diskSizeGB: 128 66 | } 67 | } 68 | 69 | resource vmonpremnic01 'Microsoft.Network/networkInterfaces@2019-09-01' = { 70 | name: vmOnPremLinux01NicName 71 | location: location 72 | dependsOn: [ onpremvnet2 ] 73 | properties: { 74 | ipConfigurations: [ { 75 | name: 'ipconfig1' 76 | properties: { 77 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'DefaultSubnet') } 78 | privateIPAllocationMethod: 'Dynamic' 79 | } 80 | } 81 | ] 82 | } 83 | } 84 | 85 | resource vmonprem01 'Microsoft.Compute/virtualMachines@2019-07-01' = { 86 | name: vmOnPremLinux01Name 87 | location: location 88 | dependsOn: [ ] 89 | properties: { 90 | hardwareProfile: { vmSize: virtualMachineSKU } 91 | storageProfile: { 92 | imageReference: { publisher: 'Canonical', offer: 'UbuntuServer', sku: '19_04-gen2', version: 'latest'} 93 | dataDisks: [ { 94 | lun: 0 95 | name: vmOnPremLinux01DiskName 96 | createOption: 'Attach' 97 | managedDisk: { id: onpremdisk01.id } 98 | } 99 | ] 100 | } 101 | osProfile: { 102 | computerName: vmOnPremLinux01Name 103 | adminUsername: username 104 | adminPassword: password 105 | linuxConfiguration: { 106 | disablePasswordAuthentication: false 107 | provisionVMAgent: true 108 | } 109 | } 110 | networkProfile: { 111 | networkInterfaces: [ { 112 | id: vmonpremnic01.id 113 | } 114 | ] 115 | } 116 | } 117 | } 118 | 119 | resource shutdownVm01 'microsoft.devtestlab/schedules@2018-09-15' = { 120 | name: autoshutdownLinux01Name 121 | location: location 122 | properties: { 123 | status: 'Enabled' 124 | taskType: 'ComputeVmShutdownTask' 125 | timeZoneId: 'UTC' 126 | dailyRecurrence: { time: '20:00' } 127 | notificationSettings: { status: 'Disabled' } 128 | targetResourceId: vmonprem01.id 129 | } 130 | } 131 | 132 | resource vnetGatewayIP 'Microsoft.Network/publicIPAddresses@2020-08-01' = { 133 | name: vnetGatewayIPName 134 | location: location 135 | sku: { name: 'Standard'} 136 | properties: { publicIPAllocationMethod: 'Static' } 137 | } 138 | 139 | resource vnetGateway 'Microsoft.Network/virtualNetworkGateways@2019-09-01' = { 140 | name: vnetGatewayName 141 | location: location 142 | dependsOn: [ onpremvnet2 ] 143 | properties: { 144 | ipConfigurations: [ { 145 | name: 'ipconfig1' 146 | properties: { 147 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'GatewaySubnet') } 148 | publicIPAddress: { id: vnetGatewayIP.id } 149 | } 150 | } 151 | ] 152 | gatewayType: 'Vpn' 153 | vpnType: 'RouteBased' 154 | enableBgp: false 155 | sku: { name: 'VpnGw1', tier: 'VpnGw1' } 156 | } 157 | } 158 | 159 | resource onpremdisk02 'Microsoft.Compute/disks@2019-07-01' = { 160 | name: vmOnPremLinux02DiskName 161 | location: location 162 | properties: { 163 | creationData: { createOption: 'Empty' } 164 | diskSizeGB: 128 165 | } 166 | } 167 | 168 | resource vmonpremnic02 'Microsoft.Network/networkInterfaces@2019-09-01' = { 169 | name: vmOnPremLinux02NicName 170 | location: location 171 | dependsOn: [ onpremvnet2 ] 172 | properties: { 173 | ipConfigurations: [ { 174 | name: 'ipconfig1' 175 | properties: { 176 | subnet: { id: resourceId('Microsoft.Network/virtualNetworks/subnets', onPremNetworkName, 'DefaultSubnet') } 177 | privateIPAllocationMethod: 'Dynamic' 178 | } 179 | } 180 | ] 181 | } 182 | } 183 | 184 | resource vmonprem02 'Microsoft.Compute/virtualMachines@2019-07-01' = { 185 | name: vmOnPremLinux02Name 186 | location: location 187 | dependsOn: [ ] 188 | properties: { 189 | hardwareProfile: { vmSize: virtualMachineSKU } 190 | storageProfile: { 191 | imageReference: { publisher: 'Canonical', offer: 'UbuntuServer', sku: '19_04-gen2', version: 'latest'} 192 | dataDisks: [ { 193 | lun: 0 194 | name: vmOnPremLinux02DiskName 195 | createOption: 'Attach' 196 | managedDisk: { id: onpremdisk02.id } 197 | } 198 | ] 199 | } 200 | osProfile: { 201 | computerName: vmOnPremLinux02Name 202 | adminUsername: username 203 | adminPassword: password 204 | linuxConfiguration: { 205 | disablePasswordAuthentication: false 206 | provisionVMAgent: true 207 | } 208 | } 209 | networkProfile: { 210 | networkInterfaces: [ { 211 | id: vmonpremnic02.id 212 | } 213 | ] 214 | } 215 | } 216 | } 217 | 218 | resource shutdownVm02 'microsoft.devtestlab/schedules@2018-09-15' = { 219 | name: autoshutdownLinux02Name 220 | location: location 221 | properties: { 222 | status: 'Enabled' 223 | taskType: 'ComputeVmShutdownTask' 224 | timeZoneId: 'UTC' 225 | dailyRecurrence: { time: '20:00' } 226 | notificationSettings: { status: 'Disabled' } 227 | targetResourceId: vmonprem02.id 228 | } 229 | } 230 | -------------------------------------------------------------------------------- /scenarios/name-resolution-with-dns-private-resolver.md: -------------------------------------------------------------------------------- 1 | 2 | # SOLUTION: Configure a DNS on the cloud, and be sure that all machines are reachable via FQDN also from on-premise (DNS Resolver version) 3 | 4 | In this walktrough I show how use an [Azure private DNS resolver](https://learn.microsoft.com/en-us/azure/dns/dns-private-resolver-overview) and [Azure private DNS zone](https://learn.microsoft.com/en-us/azure/dns/private-dns-privatednszone) to allow DNS name resolution between on-premises and a hub-and-spoke architecture on Azure, connected via a S2S VPN. 5 | 6 | Since the on-premise network is also implemented on Azure, I plan to use an instance of Azure Private DNS resolver on this side as well. In a real-world scenario, the on-premise network would likely have a more traditional DNS server configured properly. 7 | 8 | > The hub and spoke network is a network topology that helps in the organization of virtual networks. The hub acts as a central point of communication while the spokes connect to the hub, thereby forming a hub and spoke architecture. Additionally, organizations can also connect their on-premises network with the Azure hub and spoke network to create a hybrid architecture. 9 | 10 | While using the hub and spoke network, it is essential to enable communication between the cloud resources and on-premises resources. This communication requires a naming resolution system that can resolve domain names to IP addresses. The Azure Private DNS resolver can help with this, allowing for the resolution of domain names to IP addresses. 11 | 12 | **Azure Private DNS resolver** is a Platform-as-a-Service (PaaS) offering that provides scalable and high-performance DNS resolution for virtual networks. Unlike a VM with DNS on it, it is not necessary to manage and maintain the infrastructure required for a PaaS private resolver. The PaaS private resolver offers many advantages such as built-in security features, the ability to configure custom domain names, and integration with other Azure services. Additionally, PaaS private resolver can scale automatically, allowing for quick provisioning of DNS resolution resources as needed. Overall, PaaS private resolver is a more efficient and scalable solution than a VM with DNS on it. 13 | 14 | **Azure Private DNS zone** provides a reliable, secure DNS service to manage and resolve domain names in a virtual network without the need to add a custom DNS solution. By using private DNS zones, you can use your own custom domain names rather than the Azure-provided names available today. You can link a private DNS zone to one or more virtual networks by creating virtual network links. You can also enable the autoregistration feature to automatically manage the life cycle of the DNS records for the virtual machines that get deployed in a virtual network. 15 | 16 | This "solution" implements the following: 17 | 18 | ![architecture TO UPDATE!!!](../images/name-resolution-with-dns-resolver.png) 19 | 20 | We have an on-premise network connected to a hub on Azure. the hub have 3 spokes, each with a VM installed. Azure private DNS zone service with auto registration enabled allows to populate the DNS with all the required information. Once the private DNS zone is associated to a specific virtual network, the default Azure DNS is able to resolve al the names on the private zone. 21 | 22 | The DNS query process when using an Azure DNS Private Resolver is summarized below: 23 | 24 | 1. A client in a virtual network issues a DNS query. 25 | 2. If the DNS servers for this virtual network are specified as custom, then the query is forwarded to the specified IP addresses. 26 | 3. If Default (Azure-provided) DNS servers are configured in the virtual network, and there are Private DNS zones linked to the same virtual network, these zones are consulted. 27 | 4. If the query doesn't match a Private DNS zone linked to the virtual network, then Virtual network links for DNS forwarding rulesets are consulted. 28 | 5. If no ruleset links are present, then Azure DNS is used to resolve the query. 29 | 6. If ruleset links are present, the DNS forwarding rules are evaluated. 30 | 7. If a suffix match is found, the query is forwarded to the specified address. 31 | 8. If multiple matches are present, the longest suffix is used. 32 | 9. If no match is found, no DNS forwarding occurs and Azure DNS is used to resolve the query. 33 | 34 | In this scenario, the private DNS resolver on-premise is forwarding DNS queries for the `cloudasset.internal` zone to the Azure Private DNS resolver in the hub. The Azure Private DNS resolver in the hub then forwards the query to the Azure default DNS, which knows about the `cloudasset.internal` zone because of the private zone link that has been configured. This allows for successful resolution of cloud resources from the on-premises network. 35 | 36 | ## Pre-requisites 37 | 38 | 1. deploy HUB playground 39 | 2. deploy ONPREMISE-2 playground 40 | 3. configure a site-to-site VPN as [documented here](vnet-to-vnet-2.md) 41 | 4. configure a DNS in the cloud as [documented here](dns.md) 42 | 43 | ## Solution 44 | 45 | ### Create subnet for DNS Resolver in the Hub and on-premise 46 | Open Virtual Networks > `hub-lab-net` > Subnet > add subnet: 47 | * Name: `dns-resolver-subnet-01` 48 | * Subnet address range: `10.12.0.0/28` 49 | 50 | Open Virtual Networks > `onprem-net` > Subnet > add subnet: 51 | * Name: `inbound-subnet-02` 52 | * Subnet address range: `192.168.0.0/28` 53 | 54 | Open Virtual Networks > `onprem-net` > Subnet > add subnet: 55 | * Name: `outbound-subnet-02` 56 | * Subnet address range: `192.168.0.16/28` 57 | 58 | 59 | ### Create DNS resolver in the cloud 60 | Open DNS private resolvers > Create 61 | * Name: `hub-dns-resolver-01` 62 | * v-net: `hub-lab-net` 63 | * Inbound endpoint: 64 | * name: `inbound-endpoint-01` 65 | * subnet: `dns-resolver-subnet-01` 66 | * IP: static > `10.12.0.5` 67 | * CREATE 68 | 69 | ### create DNS resolver on-premise 70 | Open DNS private resolvers > Create 71 | * Name: `onprem-dns-resolver-02` 72 | * v-net: `on-prem-net` 73 | * Inbound endpoint: 74 | * name: `inbound-endpoint-02` 75 | * subnet: `inbound-subnet-02` 76 | * IP: static > `192.168.0.5` 77 | * Outbound endpoint: 78 | * name: `outbound-endpoint-02` 79 | * subnet: `outbound-subnet-02` 80 | * Ruleset > Add: 81 | * Name: `ruleset-01` 82 | * Endpoint(s): `outbound-subnet-02` 83 | * Rules: Add 84 | * Name: `cloudasset-stuff` 85 | * Domain: `cloudasset.internal.` 86 | * Destination: `10.12.0.5:53` (inbound IP DNS resolver in cloud) 87 | * CREATE 88 | 89 | ### configure new on-prem DNS server 90 | 91 | Open virtual networks > `on-prem-net` > DNS servers > `192.168.0.5` (onprem DNS resolver inbound IP) 92 | 93 | Reboot `W11-onprem` VM. 94 | 95 | ## Test solution 96 | via RDP Bastion, connect to `wm11-onprem` machine. 97 | Open command prompt and type: 98 | 99 | ``` 100 | nslookup spoke-01-vm.cloudasset.internal 101 | nslookup spoke-02-vm.cloudasset.internal 102 | nslookup spoke-03-vm.cloudasset.internal 103 | nslookup hub-vm.cloudasset.internal 104 | ``` 105 | 106 | you will be able to resolve names as shown below. 107 | 108 | ``` 109 | C:\Users\nicola>nslookup spoke-01-vm.cloudasset.internal 110 | Name: spoke-01-vm.cloudasset.internal 111 | Address: 10.13.1.4 112 | 113 | C:\Users\nicola>nslookup spoke-02-vm.cloudasset.internal 114 | Name: spoke-02-vm.cloudasset.internal 115 | Address: 10.13.2.4 116 | 117 | C:\Users\nicola>nslookup spoke-03-vm.cloudasset.internal 118 | Name: spoke-03-vm.cloudasset.internal 119 | Address: 10.13.3.4 120 | 121 | C:\Users\nicola>nslookup hub-vm.cloudasset.internal 122 | Name: hub-vm.cloudasset.internal 123 | Address: 10.12.1.4 124 | ``` 125 | -------------------------------------------------------------------------------- /images/elastic-san.drawio: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /scenarios/publish-waf-fw.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: Secure a WEB workload with both Azure Firewall Premium and Azure Web Application Firewall 2 | 3 | To secure a web Azure application workloads, you use protective measures, such as authentication and encryption, in the applications themselves. You can also add security layers to the virtual machine (VM) networks that host the applications. The layers protect inbound flows from users. They also protect outbound flows to the internet that your application might require. 4 | 5 | This solutions shows how secure a workload with Azure Firewall Premium and Azure Application Firewall, when it is hosted in a Hub-and-spoke architecture. 6 | 7 | * [Hub-and-Spoke reference architecture](https://docs.microsoft.com/en-us/azure/architecture/reference-architectures/hybrid-networking/hub-spoke): a hub virtual network acts as a central point of connectivity to many spoke virtual networks. The hub can also be used as the connectivity point to your on-premises networks. The spoke virtual networks peer with the hub and can be used to isolate workloads. The benefits of using a hub and spoke configuration include cost savings, overcoming subscription limits, and workload isolation. 8 | * [Azure Firewall Premium](https://docs.microsoft.com/en-us/azure/firewall/premium-features): is a managed next-generation firewall that offers NAT, packet filtering, threat intelligence to identify malicious IP address, TLS inspection and Intrusion Detection and Protection System (IDPS) 9 | * [Azure Application Gateway](https://docs.microsoft.com/en-us/azure/application-gateway/) with Web Application Firewall: is a managed web traffic load balancer and HTTP(S) full reverse proxy that can do Secure Socket Layer (SSL) encryption and decryption. Application Gateway also uses [Web Application Firewall](https://docs.microsoft.com/en-us/azure/web-application-firewall) to inspect web traffic and detect attacks at the HTTP layer 10 | 11 | the proposed solution is shown in the image below: 12 | 13 | ![WAF + Firewall](/images/waf-fw.png) 14 | 15 | In this option, inbound web traffic goes through both Azure Firewall and WAF. The WAF provides protection at the web application layer. Azure Firewall acts as a central logging and control point, and it inspects traffic between the Application Gateway and the backend servers. The Application Gateway and Azure Firewall aren't sitting in parallel, but one after the other. Gateway and workload are deployed in 2 spoke connected via a central hub. 16 | 17 | more information on this approach: [Application Gateway before Firewall scenario](https://docs.microsoft.com/en-us/azure/architecture/example-scenario/gateway/firewall-application-gateway#application-gateway-before-firewall) 18 | 19 | ## Pre-requisites 20 | 21 | In order to apply this solution you have to deploy hub playground only. 22 | 23 | ## Solution 24 | 25 | ### Step 1 - Create Gateway Virtual Network 26 | Create the following Virtual Network: 27 | * Name: `spoke-gateway` 28 | * Region: `west-europe` 29 | * IPv4 address space: `10.13.4.0/24` 30 | * subnet 31 | * Name: `AppGatewaySubnet` 32 | * subnet range: `10.13.4.0/24` 33 | 34 | create network peering with `hub-lab-net` 35 | 36 | * Peering Name to hub: `gateway-to-hub` 37 | * Peering Name from hub: `hub-to-gateway` 38 | 39 | ### Step 2 - create Application Gateway with WAF 40 | 41 | Create the following Azure Application Gateway 42 | 43 | * Name: `hub-gateway` 44 | * Region: `west-europe` 45 | * Tier: `waf-v2` 46 | * Minimum instance count: `1` 47 | * Create WAF Policy 48 | * Name: hub-gateway-waf-policy 49 | * Virtual Network: `spoke-gateway` 50 | * Virtual Network: `AppGatewaySubnet` 51 | * Front ends > Create public IP 52 | * Name: `hub-gateway-ip` 53 | * Backends > Add backend pool 54 | * Name: `backend-01` 55 | * Target type `IP`: `10.13.3.4` 56 | * Configuration > Add a routing rule 57 | * Name: `rule-01` 58 | * Priority: `100` 59 | * Listener 60 | * Name: `Listener-01` 61 | * Frontend: `public ip` 62 | * protocol: `http` 63 | * type: `basic` 64 | * Backend targets 65 | * type: backend pool > `backend-01` 66 | * backend settings > new 67 | * Name: `settings-01` 68 | * protocol: `HTTP` 69 | * port: `80` 70 | * Hoverride new hostname: `NO` 71 | * clock **ADD** 72 | 73 | 74 | ### Step 3 - Create user defined routing tables 75 | 76 | 77 | Create the following route table in `west europe`: `spokes-we-to-hub-routes` 78 | 79 | | Name | Address Prefix | Next hop type | Next hop IP addr | 80 | |---|---|---|---| 81 | | to-spoke-03 | 10.13.3.0/24 | Virtual Appliance | 10.12.3.4 (fw) | 82 | 83 | 84 | | subnet Name | Virtual Network | 85 | |---|---| 86 | | AppGatewaySubnet | spoke-gateway | 87 | 88 | Create the following route table in `north europe`: `spokes-ne-to-hub-routes` 89 | 90 | | Name | Address Prefix | Next hop type | Next hop IP addr | 91 | |---|---|---|---| 92 | | to-gateway | 10.13.4.0/24 | Virtual Appliance | 10.12.3.4 (fw) | 93 | 94 | | subnet Name | Virtual Network | 95 | |---|---| 96 | | default | spoke-03 | 97 | 98 | ### Step 04 - configure firewall for routing 99 | 100 | Create the following `IP Groups` in `west europe`: 101 | * `group-01`: `10.13.3.0/24`, `10.13.4.0/24` 102 | 103 | Create the following Firewall Policy: `hub-fw-policy` 104 | 105 | **Network Rules**: 106 | * Rule Collection Name: `my-collection` 107 | * Rule Collection type: `Network` 108 | * Priority: `1000` 109 | * Rule Colletion action: `Allow` 110 | * Rule Collection group: `DefaultNetworkRuleCollectionGroup` 111 | 112 | 113 | | rule name | source | port | protocol | destination | 114 | |---|---|---|---|---| 115 | | all-to-all | `group-01` | * | Any | `group-01` | 116 | 117 | Associate the policy `hub-fw-policy` to `lab-firewall` via Firewall Manager. 118 | 119 | **Enable logging** 120 | 121 | Create a Log Analytics Workspace named `playground-workspace` in `west-europe`. 122 | In order to analyze firewall logs go to `lab-firewall` -> Diagnostic Settings -> Add Diagnostic Settings: 123 | * Name: `firewall-diagnostics ` 124 | * Logs: Category Group -> All logs 125 | * Metrics: All Metrics 126 | * Destination details: Send to Log Analytics Workspace -> `playground-workspace` 127 | * click **SAVE** 128 | 129 | ### Step 5 - install a web server on `spoke-03-vm` 130 | 131 | Access to spoke-03-vm via Bastion and type 132 | 133 | ``` 134 | sudo apt-get update 135 | sudo apt-get upgrade 136 | sudo apt install nginx -y 137 | ``` 138 | 139 | test web server installation status 140 | 141 | `wget http://10.13.3.4 -S -O -` 142 | 143 | response 144 | 145 | ``` 146 | Connecting to 10.13.3.4:80... connected. 147 | HTTP request sent, awaiting response... 148 | HTTP/1.1 200 OK 149 | Server: nginx/1.14.0 (Ubuntu) 150 | 151 | ... 152 | ``` 153 | 154 | # Test Solution 155 | 156 | from a brower open `http://xxxx/` where **xxx** is `hub-gateway-ip`'s public IP you should see the following: 157 | 158 | ``` 159 | Welcome to nginx! 160 | If you see this page, the nginx web server is successfully installed and working. Further configuration is required. 161 | 162 | For online documentation and support please refer to nginx.org. 163 | Commercial support is available at nginx.com. 164 | 165 | Thank you for using nginx. 166 | ``` 167 | 168 | go to `lab-firewall` -> Logs -> click on `Run` button in **Network Rule Log Data** Box. 169 | 170 | In the Result pane, the first row should be something like: 171 | 172 | | Time | msg | Protocol | SourceIP | srcport| TargetIp | target port | Action | 173 | |---|---|----|---|---|---|---|---| 174 | |...|...|TCP|10.13.4.6|xxx|10.13.3.4|80|**Allow**| 175 | 176 | > Log ingestion from Azure Firewall can require from 60 seconds to 15 minutes, so if nothing is displayed, wait some minute and try again. 177 | -------------------------------------------------------------------------------- /scenarios/p2s-vpn-certificate-always-on.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: Configure a P2S VPN with Certificate Authentication and user tunnel Always-ON 2 | 3 | This solution shows how to configure a P2S VPN to allow individual clients running Windows 10 to an Azure Virtual Network using **Azure certificate authentication** and **always On VPN user tunnel**. 4 | 5 | A new feature of the Windows 10 or later VPN client, Always On, is the ability to maintain a VPN connection. With Always On, the active VPN profile can connect automatically and remain connected based on triggers, such as user sign-in, network state change, or device screen active. 6 | 7 | You can use gateways with Always On to establish persistent user tunnels and device tunnels to Azure. 8 | 9 | Always On VPN connections include either of two types of tunnels: 10 | 11 | * Device tunnel: Connects to specified VPN servers before users sign in to the device. Pre-sign-in connectivity scenarios and device management use a device tunnel. 12 | 13 | * User tunnel: Connects only after users sign in to the device. By using user tunnels, you can access organization resources through VPN servers. 14 | 15 | Device tunnels and user tunnels operate independent of their VPN profiles. They can be connected at the same time, and they can use different authentication methods and other VPN configuration settings, as appropriate. 16 | 17 | More information on this procedure: https://learn.microsoft.com/en-us/azure/vpn-gateway/vpn-gateway-howto-always-on-user-tunnel 18 | 19 | ## Pre-requisites 20 | 21 | In order to apply this solution, you have to deploy [Configure a P2S VPN with Certificate Authentication solution](p2s-vpn-certificate.md) before. 22 | 23 | ## Solution 24 | * Uninstall Azure client VPN, if you installed to test the pre-requisite solution. 25 | * in point-to-site configuration change Tunnel type to `IKEv2`, the only supported protocol 26 | * Copy the following text, and save it as usercert.ps1: 27 | 28 | ``` 29 | Param( 30 | [string]$xmlFilePath, 31 | [string]$ProfileName 32 | ) 33 | 34 | $a = Test-Path $xmlFilePath 35 | echo $a 36 | 37 | $ProfileXML = Get-Content $xmlFilePath 38 | 39 | echo $XML 40 | 41 | $ProfileNameEscaped = $ProfileName -replace ' ', '%20' 42 | 43 | $Version = 201606090004 44 | 45 | $ProfileXML = $ProfileXML -replace '<', '<' 46 | $ProfileXML = $ProfileXML -replace '>', '>' 47 | $ProfileXML = $ProfileXML -replace '"', '"' 48 | 49 | $nodeCSPURI = './Vendor/MSFT/VPNv2' 50 | $namespaceName = "root\cimv2\mdm\dmmap" 51 | $className = "MDM_VPNv2_01" 52 | 53 | $session = New-CimSession 54 | 55 | try 56 | { 57 | $newInstance = New-Object Microsoft.Management.Infrastructure.CimInstance $className, $namespaceName 58 | $property = [Microsoft.Management.Infrastructure.CimProperty]::Create("ParentID", "$nodeCSPURI", 'String', 'Key') 59 | $newInstance.CimInstanceProperties.Add($property) 60 | $property = [Microsoft.Management.Infrastructure.CimProperty]::Create("InstanceID", "$ProfileNameEscaped", 'String', 'Key') 61 | $newInstance.CimInstanceProperties.Add($property) 62 | $property = [Microsoft.Management.Infrastructure.CimProperty]::Create("ProfileXML", "$ProfileXML", 'String', 'Property') 63 | $newInstance.CimInstanceProperties.Add($property) 64 | 65 | $session.CreateInstance($namespaceName, $newInstance) 66 | $Message = "Created $ProfileName profile." 67 | Write-Host "$Message" 68 | } 69 | catch [Exception] 70 | { 71 | $Message = "Unable to create $ProfileName profile: $_" 72 | Write-Host "$Message" 73 | exit 74 | } 75 | $Message = "Complete." 76 | Write-Host "$Message" 77 | ``` 78 | 79 | Copy the following text, and save it as `VPNProfile.xml` in the same folder as `usercert.ps1`. Edit the following text to match your environment: 80 | 81 | * *Servers*: azuregateway-1234-56-78dc.cloudapp.net - Can be found in the generic\VpnSettings.xml in the downloaded profile zip file 82 | 83 | * *Routes*: can be found in `generic\VpnSettings.xml` 84 | 85 | ``` 86 | 87 | 88 | azuregateway-aa420069-de4b-479d-92ec-2976794df855-abdb6574d293.vpn.azure.com 89 | IKEv2 90 | 91 | Eap 92 | 93 | 94 | 1300013truefalsefalsefalsefalse 95 | 96 | 97 | 98 | SplitTunnel 99 | 100 | true 101 | 102 | 103 | 104 |
10.12.0.0
105 | 16 106 |
107 | 108 |
10.13.2.0
109 | 24 110 |
111 | 112 |
10.13.3.0
113 | 24 114 |
115 | 116 |
10.13.1.0
117 | 24 118 |
119 | 120 | 121 | 192.168.3.4, 192.168.3.5 122 | 123 | 124 | true 125 | true 126 | 127 | true 128 |
129 | 130 | ``` 131 | 132 | 133 | * Run Powershell as Administrator 134 | * In PowerShell, switch to the folder where `usercert.ps1` and `VPNProfile.xml` are located, and run the following command: `.\usercert.ps1 .\VPNProfile.xml UserAlwaysOn` 135 | 136 | result: 137 | 138 | ``` 139 | AlwaysOn : 140 | ByPassForLocal : 141 | DeviceTunnel : 142 | DnsSuffix : 143 | EdpModeId : 144 | InstanceID : UserAlwaysOn 145 | LockDown : 146 | ParentID : ./Vendor/MSFT/VPNv2 147 | ProfileXML : 148 | RegisterDNS : 149 | RememberCredentials : 150 | TrustedNetworkDetection : 151 | PSComputerName : 152 | 153 | Created UserTest profile. 154 | Complete. 155 | 156 | ``` 157 | 158 | 159 | ## Test Solution 160 | 161 | * Under VPN Settings, look for the UserTest entry, and then select Connect. 162 | * If the connection succeeds, you've successfully configured an Always On user tunnel. 163 | * open a `remote desktop connection` app and connect to `10.13.1.4` 164 | -------------------------------------------------------------------------------- /scenarios/app-gateway-01.md: -------------------------------------------------------------------------------- 1 | # SOLUTION - publish internal web app via Azure Application Gateway on private and public IPs 2 | 3 | In this solution I show how to expose through Azure Application Gateway both externally (internet) in HTTP and internally (intranet) in HTTPS an HTTPS web service delivered by a windows VM, with a certificates emitted by a privare certification authority. 4 | 5 | The service is exposed by the Azure Application Gateway, via HTTP and HTTPS, (both externally and internally). 6 | 7 | The scenario is deployed into a Hub and Spoke network topology context. Specifically, the virtual network that hosts the Web Service VM is different from the virtual network that hosts the Azure Application Gateway. 8 | 9 | Traffic between these 2 spokes is secured by an Azure Firewall in the hub virtual network. 10 | 11 | In this configuration, inbound web traffic goes through both Azure Firewall and WAF. The WAF provides protection at the web application layer. Azure Firewall acts as a central logging and control point, and it inspects traffic between the Application Gateway and the backend servers. With Azure Firewall Premium, this design can support end-to-end scenarios, where the Azure Firewall applies TLS inspection to do IDPS on the encrypted traffic between the Application Gateway and the web backend. 12 | 13 | This design is appropriate for applications that need to know incoming client source IP addresses, for example to serve geolocation-specific content or for logging. Application Gateway in front of Azure Firewall captures the incoming packet's source IP address in the X-forwarded-for header, so the web server can see the original IP address in this header. 14 | 15 | The resulting overall architecture is shown in the following schema. 16 | 17 | ![architecture](../images/app-gateway-01.png) 18 | _download drawio version of this image [here](../images/app-gateway-01.drawio)._ 19 | 20 | ## Pre-requisites 21 | In order to apply this solution you have to deploy the `hub-01` and the `any-to-any` routing, so that you have a fully configured hub-and-spoke network with firewall and routing between spokes. 22 | 23 | ## Solution 24 | 25 | The steps to configure the solution are as follows: 26 | 27 | 1. generate 2 self signed certificate 28 | 2. Create a DNS private zone 29 | 3. configure spoke-01-vm as HTTPS orgin web site 30 | 4. configure the Azure Application Gateway 31 | 32 | ### Generate 2 certificates 33 | We will use `mkcert` to generate 2 certificates: 34 | 35 | * `web.spoke01.com`: the HTTPS name for web server 36 | * `published.spoke01.com`: the HTTPS name for the application Gateway private Front-End 37 | 38 | Install [mkcer](https://github.com/FiloSottile/mkcert) to create a local certification authority and generate 2 web certificates. 39 | 40 | ``` 41 | mkcert -install 42 | mkcert -pkcs12 web.spoke01.com 43 | mkcert -pkcs12 published.spoke01.com 44 | ``` 45 | 46 | this will create 2 files 47 | 48 | ``` 49 | web.spoke01.com.p12 50 | published.spoke01.com.p12 51 | ``` 52 | 53 | rename `published.spoke01.com.p12` to `published.spoke01.com.pfx` (_p12 and pfx file format are the same, but Azure Application gateway requires this extension!_) 54 | 55 | Convert root certificate in .cer, so we can load in Azure Application Gateway 56 | ``` 57 | mkcert -CAROOT 58 | 59 | openssl x509 -inform PEM -in -outform DER -out rootCA.cer 60 | ``` 61 | 62 | ### Configure the private DNS Zone 63 | Create a DNS private zone. 64 | From Azure Portal > Private DNS Zones > Create: 65 | * name: `spoke01.com` 66 | * click **Create** 67 | 68 | In this private DNS zone create 2 A records: 69 | 70 | | name | type | ipaddress | 71 | |---|---|---| 72 | | `web` | `A` | `10.13.1.4` | 73 | | `published` | `A` | `10.13.2.100` | 74 | 75 | unded Virtual Network Links, add 2 links: 76 | 77 | | link name | virtual network | enable auto registration | 78 | |---|---|---| 79 | | `spoke-01-link` | `spoke-01` | NO | 80 | | `spoke-02-link` | `spoke-02` | NO | 81 | 82 | ### configure `spoke-01-vm` as HTTPS orgin web site 83 | 84 | Install the certificate `web-on-spoke-01` on `spoke-01-vm` 85 | 86 | * right-click > Install PFX 87 | * select: `local machine` 88 | * password: `changeit` 89 | * set: mark this key as exportable 90 | * set: include all extended properties 91 | * set: automatically select the certificate store 92 | 93 | Configure IIS on `spoke-01-vm`, from Windows server mamager: 94 | * add roles and features 95 | * select web server (IIS) 96 | * click «**Install**» 97 | 98 | Configure SSL web site: from Internet Information Services > Default Web Site > SSL Settings > Bindings > Add: 99 | * Type: `https` 100 | * IP address: `10.13.1.4` 101 | * hostname: `web.spoke01.com` 102 | * SSL certificate: `web.spoke01.com` 103 | 104 | Test web site and connection: from `spoke-02-vm` brose with Microsot Edge tp https://web.spoke01.com/ 105 | you will receive a "_your connection isn't private_" message. Go to advanced> ignore to see the IIS web site. 106 | 107 | ### Configure the Azure Application Gateway 108 | 109 | It is required to create a new routing table on Application Gateway subnet that allows direct traffic to internet. From Azure Portal > Route tables > Create 110 | * region: `west europe` 111 | * name: `spoke-02-services-rt` 112 | * click **create** 113 | 114 | When the route table is created, goes to Settings > Routes > add: 115 | * name: `to-hub` 116 | * destination type: **IP address** 117 | * IPs: `10.13.1.0/24` 118 | * next hop: `Virtual Appliance` 119 | * IP: `10.12.3.4` 120 | 121 | Associate this route table to (Azure Portal > `spoke-02` > subnets): 122 | * vnet: `spoke-02` 123 | * subnet: `services` 124 | 125 | Create an Azure Application Gateway: From Azure Portal > Aplication Gateways > Create Application Gateway: 126 | * Name: `lab-public-gateway` 127 | * region: `west europe` 128 | * tier: `standard V2` 129 | * Virtual network: `spoke-02` 130 | * subnet: `services` 131 | * FrontEnd 132 | * Address type: `both` 133 | * public IP Name (**new**): `lab-app-gw-ip` 134 | * private IP Address: `10.13.2.100` 135 | * Backends > add backend pool 136 | * Name: `spoke-01-pool` 137 | * IP/FQDN: `10.13.1.4` 138 | * Configuration > Add a routing rule 139 | * name: `spoke-01-rule` 140 | * priority: `1000` 141 | * Listener 142 | * Name: `listener-public` 143 | * frontend IP: `public` 144 | * protocol: `http` 145 | * listener type: `basic` 146 | * backend targets 147 | * type: `backend pool` 148 | * backend target: `spoke-01-pool` 149 | * backend settings > add new: 150 | * name: `spoke-01-settings` 151 | * backend protocol: `https` 152 | * Use well known CA certificate: `NO` 153 | * CER certificate: **rootCA.cer** file 154 | * override backend path: 155 | * override with new hostname: `yes` 156 | * hostname: `web.spoke01.com` 157 | * create custom probes: `no` 158 | * click **ADD** 159 | * Review and Create > **Create** 160 | 161 | Once the Application Gateway is created, add the internal listener and routing rule 162 | 163 | From your Azure Applicationg Gateway > Settings > Listers > Add Listener: 164 | * Name: listener-private 165 | * Front end IP: private 166 | * protocol: HTTPS 167 | * Certificate > Add 168 | * upload a certificate 169 | * cert name: published-certificate 170 | * PFX File: `published.spoke01.com.pfx` created above 171 | * password: `changeit` 172 | * listener-type: basic 173 | * click **ADD** 174 | 175 | From your Azure Applicationg Gateway > Settings > Rules > Add Routing Rule: 176 | * Name: Rule-internal 177 | * Priority: 2000 178 | * Listener: Listener-private 179 | * Backend Targets 180 | * target-type: backend-pool 181 | * backend-target: spoke-01-pool 182 | * backend-settings: spoke-01-settings 183 | * click 184 | 185 | ## Test solution 186 | 187 | From your local machine open a browser and type _http:///_ 188 | From spoke-02-vm open Edge ant type https://published.spoke01.com/: you will receive a "_your connection isn't private_" message. Go to advanced > ignore to see the IIS web site. 189 | 190 | ## More information 191 | 192 | * How Application Gateway works: https://learn.microsoft.com/en-us/azure/application-gateway/how-application-gateway-works 193 | * Firewall topologies: https://learn.microsoft.com/en-us/azure/architecture/example-scenario/gateway/firewall-application-gateway 194 | * local personal certification authority: https://github.com/FiloSottile/mkcert 195 | * use mkcert with IIS: https://medium.com/@aweber01/locally-trusted-development-certificates-with-mkcert-and-iis-e09410d92031 196 | * 197 | -------------------------------------------------------------------------------- /on-prem-bicep/on-prem.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.management.azure.com/schemas/2019-04-01/deploymentTemplate.json#", 3 | "contentVersion": "1.0.0.0", 4 | "metadata": { 5 | "_generator": { 6 | "name": "bicep", 7 | "version": "0.36.1.42791", 8 | "templateHash": "5865605153906493223" 9 | } 10 | }, 11 | "parameters": { 12 | "location": { 13 | "type": "string", 14 | "defaultValue": "francecentral" 15 | }, 16 | "deployVM": { 17 | "type": "bool", 18 | "defaultValue": true 19 | }, 20 | "username": { 21 | "type": "string", 22 | "defaultValue": "nicola" 23 | }, 24 | "password": { 25 | "type": "securestring", 26 | "defaultValue": "password.123" 27 | }, 28 | "virtualMachineSize": { 29 | "type": "string", 30 | "defaultValue": "Standard_D2_v5" 31 | }, 32 | "deployBastion": { 33 | "type": "bool", 34 | "defaultValue": true 35 | }, 36 | "enableBgp": { 37 | "type": "bool", 38 | "defaultValue": false 39 | }, 40 | "localGatewayFqdn": { 41 | "type": "string", 42 | "defaultValue": "" 43 | } 44 | }, 45 | "variables": { 46 | "onPremNetworkName": "on-prem-net", 47 | "bastionName": "bastion-on-prem", 48 | "bastionIPName": "bastion-on-prem-publicip", 49 | "vmOnPremDiskName": "vm-w11-onprem-disk", 50 | "vmOnPremNicName": "vm-w11-onprem-nic", 51 | "vmOnPremName": "W11-onprem", 52 | "autoshutdownName": "[format('shutdown-computevm-{0}', variables('vmOnPremName'))]", 53 | "vnetGatewayIPName": "onprem-gateway-virtualip", 54 | "vnetGatewayName": "on-prem-gateway", 55 | "localGatewayName": "lab-local-gateway", 56 | "subnets": "[concat(createArray(createObject('name', 'GatewaySubnet', 'properties', createObject('addressPrefix', '192.168.3.0/24')), createObject('name', 'DefaultSubnet', 'properties', createObject('addressPrefix', '192.168.1.0/24'))), if(parameters('deployBastion'), createArray(createObject('name', 'AzureBastionSubnet', 'properties', createObject('addressPrefix', '192.168.2.0/24'))), createArray()))]" 57 | }, 58 | "resources": [ 59 | { 60 | "type": "Microsoft.Network/virtualNetworks", 61 | "apiVersion": "2019-09-01", 62 | "name": "[variables('onPremNetworkName')]", 63 | "location": "[parameters('location')]", 64 | "properties": { 65 | "addressSpace": { 66 | "addressPrefixes": [ 67 | "192.168.0.0/16" 68 | ] 69 | }, 70 | "subnets": "[variables('subnets')]" 71 | } 72 | }, 73 | { 74 | "condition": "[parameters('deployBastion')]", 75 | "type": "Microsoft.Network/publicIPAddresses", 76 | "apiVersion": "2020-08-01", 77 | "name": "[variables('bastionIPName')]", 78 | "location": "[parameters('location')]", 79 | "sku": { 80 | "name": "Standard" 81 | }, 82 | "properties": { 83 | "publicIPAllocationMethod": "Static" 84 | } 85 | }, 86 | { 87 | "condition": "[parameters('deployBastion')]", 88 | "type": "Microsoft.Network/bastionHosts", 89 | "apiVersion": "2019-09-01", 90 | "name": "[variables('bastionName')]", 91 | "location": "[parameters('location')]", 92 | "properties": { 93 | "ipConfigurations": [ 94 | { 95 | "name": "ipconfig1", 96 | "properties": { 97 | "subnet": { 98 | "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('onPremNetworkName'), 'AzureBastionSubnet')]" 99 | }, 100 | "publicIPAddress": { 101 | "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('bastionIPName'))]" 102 | } 103 | } 104 | } 105 | ] 106 | }, 107 | "dependsOn": [ 108 | "[resourceId('Microsoft.Network/publicIPAddresses', variables('bastionIPName'))]", 109 | "[resourceId('Microsoft.Network/virtualNetworks', variables('onPremNetworkName'))]" 110 | ] 111 | }, 112 | { 113 | "condition": "[parameters('deployVM')]", 114 | "type": "Microsoft.Compute/disks", 115 | "apiVersion": "2019-07-01", 116 | "name": "[variables('vmOnPremDiskName')]", 117 | "location": "[parameters('location')]", 118 | "properties": { 119 | "creationData": { 120 | "createOption": "Empty" 121 | }, 122 | "diskSizeGB": 128 123 | } 124 | }, 125 | { 126 | "condition": "[parameters('deployVM')]", 127 | "type": "Microsoft.Network/networkInterfaces", 128 | "apiVersion": "2019-09-01", 129 | "name": "[variables('vmOnPremNicName')]", 130 | "location": "[parameters('location')]", 131 | "properties": { 132 | "ipConfigurations": [ 133 | { 134 | "name": "ipconfig1", 135 | "properties": { 136 | "subnet": { 137 | "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('onPremNetworkName'), 'DefaultSubnet')]" 138 | }, 139 | "privateIPAllocationMethod": "Dynamic" 140 | } 141 | } 142 | ] 143 | }, 144 | "dependsOn": [ 145 | "[resourceId('Microsoft.Network/virtualNetworks', variables('onPremNetworkName'))]" 146 | ] 147 | }, 148 | { 149 | "condition": "[parameters('deployVM')]", 150 | "type": "Microsoft.Compute/virtualMachines", 151 | "apiVersion": "2019-07-01", 152 | "name": "[variables('vmOnPremName')]", 153 | "location": "[parameters('location')]", 154 | "properties": { 155 | "hardwareProfile": { 156 | "vmSize": "[parameters('virtualMachineSize')]" 157 | }, 158 | "storageProfile": { 159 | "imageReference": { 160 | "publisher": "MicrosoftWindowsDesktop", 161 | "offer": "windows-11", 162 | "sku": "win11-24h2-ent", 163 | "version": "latest" 164 | }, 165 | "dataDisks": [ 166 | { 167 | "lun": 0, 168 | "name": "[variables('vmOnPremDiskName')]", 169 | "createOption": "Attach", 170 | "managedDisk": { 171 | "id": "[resourceId('Microsoft.Compute/disks', variables('vmOnPremDiskName'))]" 172 | } 173 | } 174 | ] 175 | }, 176 | "osProfile": { 177 | "computerName": "[variables('vmOnPremName')]", 178 | "adminUsername": "[parameters('username')]", 179 | "adminPassword": "[parameters('password')]", 180 | "windowsConfiguration": { 181 | "enableAutomaticUpdates": true 182 | } 183 | }, 184 | "networkProfile": { 185 | "networkInterfaces": [ 186 | { 187 | "id": "[resourceId('Microsoft.Network/networkInterfaces', variables('vmOnPremNicName'))]" 188 | } 189 | ] 190 | } 191 | }, 192 | "dependsOn": [ 193 | "[resourceId('Microsoft.Compute/disks', variables('vmOnPremDiskName'))]", 194 | "[resourceId('Microsoft.Network/networkInterfaces', variables('vmOnPremNicName'))]" 195 | ] 196 | }, 197 | { 198 | "condition": "[parameters('deployVM')]", 199 | "type": "Microsoft.DevTestLab/schedules", 200 | "apiVersion": "2018-09-15", 201 | "name": "[variables('autoshutdownName')]", 202 | "location": "[parameters('location')]", 203 | "properties": { 204 | "status": "Enabled", 205 | "taskType": "ComputeVmShutdownTask", 206 | "timeZoneId": "UTC", 207 | "dailyRecurrence": { 208 | "time": "20:00" 209 | }, 210 | "notificationSettings": { 211 | "status": "Disabled" 212 | }, 213 | "targetResourceId": "[resourceId('Microsoft.Compute/virtualMachines', variables('vmOnPremName'))]" 214 | }, 215 | "dependsOn": [ 216 | "[resourceId('Microsoft.Compute/virtualMachines', variables('vmOnPremName'))]" 217 | ] 218 | }, 219 | { 220 | "type": "Microsoft.Network/publicIPAddresses", 221 | "apiVersion": "2020-08-01", 222 | "name": "[variables('vnetGatewayIPName')]", 223 | "location": "[parameters('location')]", 224 | "sku": { 225 | "name": "Standard" 226 | }, 227 | "properties": { 228 | "publicIPAllocationMethod": "Static" 229 | } 230 | }, 231 | { 232 | "type": "Microsoft.Network/virtualNetworkGateways", 233 | "apiVersion": "2022-09-01", 234 | "name": "[variables('vnetGatewayName')]", 235 | "location": "[parameters('location')]", 236 | "properties": { 237 | "ipConfigurations": [ 238 | { 239 | "name": "ipconfig1", 240 | "properties": { 241 | "subnet": { 242 | "id": "[resourceId('Microsoft.Network/virtualNetworks/subnets', variables('onPremNetworkName'), 'GatewaySubnet')]" 243 | }, 244 | "publicIPAddress": { 245 | "id": "[resourceId('Microsoft.Network/publicIPAddresses', variables('vnetGatewayIPName'))]" 246 | } 247 | } 248 | } 249 | ], 250 | "gatewayType": "Vpn", 251 | "vpnType": "RouteBased", 252 | "enableBgp": "[parameters('enableBgp')]", 253 | "bgpSettings": "[if(parameters('enableBgp'), createObject('asn', 65510), null())]", 254 | "sku": { 255 | "name": "VpnGw1", 256 | "tier": "VpnGw1" 257 | } 258 | }, 259 | "dependsOn": [ 260 | "[resourceId('Microsoft.Network/virtualNetworks', variables('onPremNetworkName'))]", 261 | "[resourceId('Microsoft.Network/publicIPAddresses', variables('vnetGatewayIPName'))]" 262 | ] 263 | }, 264 | { 265 | "condition": "[parameters('enableBgp')]", 266 | "type": "Microsoft.Network/localNetworkGateways", 267 | "apiVersion": "2022-09-01", 268 | "name": "[variables('localGatewayName')]", 269 | "location": "[parameters('location')]", 270 | "properties": { 271 | "bgpSettings": { 272 | "asn": 65511, 273 | "bgpPeeringAddress": "192.168.3.254" 274 | }, 275 | "localNetworkAddressSpace": { 276 | "addressPrefixes": [ 277 | "10.12.0.0/16", 278 | "10.13.1.0/24", 279 | "10.13.2.0/24", 280 | "10.13.3.0/24" 281 | ] 282 | }, 283 | "fqdn": "[parameters('localGatewayFqdn')]" 284 | } 285 | } 286 | ] 287 | } -------------------------------------------------------------------------------- /scenarios/apim-stv1.md: -------------------------------------------------------------------------------- 1 | # SOLUTION: deploy a private Azure API Management stv1 platform in one spoke 2 | 3 | In this solution, I will show how to deploy an APIM version stv1 in a virtual private network. 4 | 5 | 6 | 7 | APIM configuration, on the other hand, is more complex. APIM is a service that is composed of, and interacts with, numerous other Azure infrastructure components, so the private deployment exposes us to challenges that we need to solve: 8 | 9 | * A **network security group** attached to the APIM subnet is required to explicitly allow inbound connectivity because the load balancer used internally by API Management is secure by default and rejects all inbound traffic. 10 | * **Service endpoints** in the subnet to dependent services such as Azure Storage, Event Hubs, and Azure SQL are also required because, in this context, the user route (`0.0.0.0/0`) through the Azure firewall would interrupt connectivity with API Management. 11 | * A Standard SKU **public IPv4** address. The public IP address in this configuration is used only for management operations. 12 | * A **Private DNS zone** is also required to resolve internal APIM endpoints. 13 | 14 | the final architecture is shown in the image below. 15 | 16 | ..... 17 | 18 | 19 | ## Pre-requisites 20 | 21 | In order to apply this solution you have to deploy the `hub-01` and the `any-to-any` routing, so that you have a fully configured hub-and-spoke network with firewall and routing between spokes. 22 | 23 | ## Solution 24 | We will do the following: 25 | 26 | * Deploy and configure an Azure API Manager (APIM) in the `services` subnet of `spoke-02` network. 27 | 28 | 29 | Go to Azure Portal > Azure AI | Azure OpenAI > Create 30 | * region: West Europe 31 | * Name: `aoai-01` 32 | * pricing tier: `standard S0` 33 | 34 | Network 35 | * Type: `disabled` 36 | * add private endpoint 37 | * Location: west europe 38 | * Name: `aoai-01-pe` 39 | * Virtual Network: `spoke-01` 40 | * Subnet: `services` 41 | * Integrate with private dns zone: No 42 | * click [create] 43 | 44 | Take note ok the `KEY1` found in Resource Management > Keys and Endpoint > KEY1. 45 | 46 | Go to Azure Portal > private DNS zone > create 47 | * name: `aoai-01.openai.azure.com` 48 | * Click [Create] 49 | 50 | Go to Azure Portal > Private DNS zones > `aoai-01.openai.azure.com` > + Record set 51 | 52 | * Name: `*` 53 | * Type: `A` 54 | * IP: `10.13.1.68` (Azure Open AI private endpoint IP) 55 | * Click [OK] 56 | 57 | Go to Virtual Network links > Add 58 | * name: `spoke-01-link` 59 | * subnet `spoke-01` 60 | * click [OK] 61 | 62 | Go to Virtual Network links > Add 63 | * name: `spoke-02-link` 64 | * subnet `spoke-02` 65 | * click [OK] 66 | 67 | ### API Management Service public IP 68 | Go to Azure Portal > Public IP addresses > Create 69 | 70 | Basic 71 | * Region: West Europe 72 | * Name: `apiman-ip-02` 73 | * IP version: `v4` 74 | * SKU: `developer` (stv1) 75 | * DNS name label: `apiman-02-ip` 76 | * click [create] 77 | 78 | ### API Management subnet NSG 79 | Go top Azure Portal > Network security groups > create 80 | * Name: `apiman-nsg` 81 | * Region: `West Europe` 82 | * click [create] 83 | 84 | add the following inbound rules 85 | 86 | | priority | name | port src/dst | protocol | source/service tag | destination/service tag | action | 87 | |----|----|----|----|----|----|----| 88 | | 1000 |apimanagement-inbound | */3443| TCP | ApiManagement | Virtual Network| Allow 89 | | 1100 |mgmt-endpoint-portal | */ * | TCP | AzureLoadBalancer | VirtualNetwork | allow 90 | 91 | 92 | add the following outbound rules 93 | 94 | | priority | name | port src/dst | protocol | source/service tag| destination/service tag | action | 95 | |----|----|----|----|----|----|----| 96 | | 1000 |to-storage | */443| TCP | Virtual Network | Storage| Allow 97 | | 1100 |to-sqlserver | */1443| TCP | Virtual Network | Sql| Allow 98 | | 1200 |to-azuremonitor | */1886,443| TCP | Virtual Network | Azure Monitor| Allow 99 | 100 | Go to Azure Portal > Network security groups > `apiman-nsg` > subnets > associate: 101 | * virtual network: `spoke-02` 102 | * subnet: `services` 103 | 104 | ### API Management subnet route table 105 | Go to Azure Portal > Route Table > Create 106 | 107 | * Region: West Europe 108 | * Name: `apim-02-to-firewall` 109 | * click [create] 110 | 111 | Go to Azure Portal > Route Table > `apim-02-to-firewall` > Routes > Add 112 | 113 | | Name | Type | Addr prefix | next hope type | next hope ip addr | 114 | |----|----|---|----|----| 115 | | apim2internet | Service Tag | ApiManagement | internet | | 116 | | 2firewall | Ip Addresses |0.0.0.0/0 | virtual appliance | 10.12.3.4 | 117 | 118 | Go to Azure Portal > Virtual Networks > `Spoke-02` > subnets > `services` 119 | 120 | * Route Table: `apim-02-to-firewall` 121 | * click [save] 122 | 123 | ### API Management subnet Service Endpoints 124 | 125 | Go to Azure Portal > Virtual Networks > `Spoke-02` > subnets > `services` > Endpoints > add 126 | 127 | * Microsoft.EventHub 128 | * Microsoft.Sql 129 | * Microsoft.Storage 130 | 131 | Click [save] 132 | 133 | ### Api Management Service Instance 134 | Go to Azure Portal > API Management Service > Create 135 | 136 | Basics 137 | * Region: `West Europe` 138 | * Name: `apiman-02` 139 | * Organization: `contoso` 140 | * Organization email: `admin@contoso.com` 141 | * Tier: `Premium` (stv1) 142 | * click [create] 143 | 144 | Go to Azure Portal > API Management Service > `apiman-02` > Network > Virtual Network: 145 | 146 | * Virtual Network: `internal` 147 | * Location: `West Europe` 148 | * Virtual Network: `spoke-02` 149 | * Management public IP address: `apiman-ip-02` 150 | * subnet : `services` 151 | * click [save] 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | Go to Azure Portal > private DNS zone > create 163 | * name: `azure-api.net` 164 | * Click [Create] 165 | 166 | Go to Azure Portal > Private DNS zones > `azure-api.net` > + Record set 167 | 168 | | Name | Type | IP | 169 | |----|----|----| 170 | | `apiman-02` | A | `10.13.2.68` | 171 | | `apiman-02.portal` | A | `10.13.2.68` | 172 | | `apiman-02.developer` | A | `10.13.2.68` | 173 | | `apiman-02.management` | A | `10.13.2.68` | 174 | | `apiman-02.scm` | A | `10.13.2.68` | 175 | 176 | 177 | Go to Virtual Network links > Add 178 | * name: `spoke-03-link` 179 | * subnet `spoke-03` 180 | * click [OK] 181 | 182 | Go to Virtual Network links > Add 183 | * name: `spoke-02-link` 184 | * subnet `spoke-02` 185 | * click [OK] 186 | 187 | Go to Virtual Network links > Add 188 | * name: `hub-link` 189 | * subnet `hub-lab-net` 190 | * click [OK] 191 | 192 | 193 | ### Import OpenAI Rest interface in APIM using its swagger specification 194 | 195 | Azure OpenAI provides you with REST API references, that can easily be imported into Azure API Management. In this scenario we will expose via APIM the **completition endpoint**. 196 | 197 | * download the swagger specification (`swagger.json`) for the API version you want to expose from [the official AOAI endpoint swagger specifications](https://learn.microsoft.com/en-us/azure/ai-services/openai/reference#completions) i.e. `2022-12-01` 198 | * replace '{endpoint}' string with your deployed api 199 | 200 | ``` 201 | (Get-Content .\swagger.json).Replace('{endpoint}', 'aoai-01.openai.azure.com') | Set-Content .\swagger.json 202 | ``` 203 | 204 | Go to Azure Portal > API Management Services > `apiman-02` > APIs > create from definition > Open API > upload `swagger.json` file > create 205 | 206 | Azure OpenAI is protected using the concept of API keys. An API key is a unique access code that acts as a secure identifier. We will use Azure API Management policies to append an API key to our operations. 207 | 208 | Go to Azure Portal > API Management Services > `apiman-02` > APIs > All Operations > Inbound processing > Policy > Edit and insert the following XML 209 | 210 | ``` 211 | 212 | 213 | 214 | {{APPPSVC-KEY}} 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | ``` 224 | 225 | For security reasons, we want to use Named values in Azure API Management and don’t paste the API key directly into our policy. 226 | 227 | For this go to Azure Portal > API Management Services > `apiman-02` > Named Values > Add 228 | 229 | * Name: `APPSVC-KEY` 230 | * Display Name: `APPPSVC-KEY` 231 | * Type: Secret 232 | * Value: `KEY1` (the KEY1 value you taken above from Open AI) 233 | * click [SAVE] 234 | 235 | Now disable subscription key required at API level, so that we can easily test the call in our environment. 236 | 237 | For this open Azure Portal > API Management Services > `apiman-02` > `Azure OpenAI Service API` > Settings > Subscription: 238 | 239 | * Subscription Required > `NO` 240 | * click [SAVE] 241 | 242 | ![APIM configuration](../images/aoai-deployment-apim-configuration.png) 243 | 244 | _download drawio version of this image [here](../images/aoai-deployment.drawio)._ 245 | 246 | 247 | ## Test solution 248 | Finally, you are able to test your API call in Azure API Management. 249 | 250 | Connect via bastion/RDP to `spoke-02-vm` and test API call locally. 251 | 252 | Open Azure Portal > API Management Services > `apiman-02` > `Azure OpenAI Service API` > Test Tab > Test `Create a completition...` 253 | 254 | * deployment-id: `mygpt` 255 | * api-verison: `2022-12-01` 256 | * click [Send] 257 | 258 | you should receive an HTTP 200 answer. 259 | 260 | Connect via bastion/ssh to `spoke-03-vm` and type the following: 261 | 262 | ``` 263 | curl https://apiman-02.azure-api.net/deployments/mygpt/completions?api-version=2022-12-01\ 264 | -H "Content-Type: application/json" \ 265 | -d "{ 266 | \"prompt\": \"Once upon a time\", 267 | \"max_tokens\": 50 268 | }" 269 | 270 | ``` 271 | you should receive a json message coming from Azure Open AI. 272 | 273 | 274 | > {"id":"cmpl-8ugbMFmX3n2P...","object":"text_completion","created":...,"model":"gpt-35-turbo-instruct","choices":[{"text":" some scadians dressed themselves as “lawmen” and rode out to confront the proprietors of Masker’s Serpent. They “remained a few minutes” and left. No one was arrested. No one does anything on Cape Cod that anyone","index":0,"finish_reason":"length","logprobs":null}],"usage":{"prompt_tokens":4,"completion_tokens":50,"total_tokens":54}} 275 | 276 | ## More information 277 | 278 | 279 | * Azure API Management Service https://learn.microsoft.com/en-us/azure/api-management/ 280 | * vnet internal deployment: https://learn.microsoft.com/en-us/azure/api-management/api-management-using-with-internal-vnet 281 | * Deploy your Azure API Management instance to a virtual network - internal mode https://learn.microsoft.com/en-us/azure/api-management/api-management-using-with-internal-vnet?tabs=stv2 282 | 283 | * Private endpoint https://learn.microsoft.com/en-us/azure/private-link/private-endpoint-overview 284 | * Publish Open AI via APIM https://techcommunity.microsoft.com/t5/apps-on-azure-blog/build-an-enterprise-ready-azure-openai-solution-with-azure-api/ba-p/3907562 285 | * Understanding Azure API Management policies: https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-policies 286 | 287 | 288 | --------------------------------------------------------------------------------