Azure Firewall e NSGs na Landing Zone

Azure Firewall e NSGs na Landing Zone

Azure Firewall e NSGs na Landing Zone

Azure Firewall e NSGs na Landing Zone

Neste artigo você vai configurar o Azure Firewall e NSGs na Landing Zone Hub-Spoke criada no artigo anterior, adicionando uma camada de segurança centralizada no Hub e regras de segmentação por camada no Spoke. Vamos implantar o Firewall com Firewall Policy, criar User-Defined Routes para forçar todo o tráfego do Spoke pelo Firewall, e definir Network Security Groups específicos para frontend, backend, data e AKS — tudo com Terraform em Brazil South e East US.

Série: DR Azure e Landing Zone
  • 📖 Art. 01: Planejamento de DR Azure e Landing Zone
  • 📖 Art. 02: Hub-Spoke Landing Zone com Terraform
  • ⚙️ Art. 03 (este): Azure Firewall e NSGs na Landing Zone
  • 🔒 Art. 04: Azure Bastion na Landing Zone sem IP Público
  • 🔒 Art. 05: Azure Front Door e Traffic Manager para Failover
  • 🔒 Art. 06: DNS Privado Multi-Região no Azure
  • 🔒 Art. 07: Azure Site Recovery para VMs Multi-Região
  • 🔒 Art. 08: Azure Storage com Geo-Replicação para DR
  • 🔒 Art. 09: AKS Multi-Região com Failover no Azure
  • 🔒 Art. 10: Velero no AKS para Backup e Restore Cross-Region
  • 🔒 Art. 11: Runbooks de Failover no Azure Automation
  • 🔒 Art. 12: Simular um DR no Azure sem Impacto em Produção
  • 🔒 Art. 13: Monitoramento do DR no Azure com Azure Monitor

Sumário

Azure Firewall vs NSG — quando usar cada um

Os azure firewall e nsgs na landing zone têm papéis complementares e não concorrentes. Entender a diferença é fundamental para não criar brechas de segurança nem bloquear tráfego legítimo.

Característica Azure Firewall NSG
Camada OSI L3–L7 (FQDN, TLS inspection) L3–L4 (IP e porta)
Estado Stateful Stateless (avalia cada pacote)
Escopo Centralizado no Hub — protege múltiplas VNets Por subnet ou NIC
Regras FQDN Sim (ex: *.windowsupdate.com) Não — apenas IPs
Custo Alto (instância fixa + dados processados) Gratuito
Posição na Landing Zone Hub — AzureFirewallSubnet Spoke — cada subnet do workload

Portanto, a estratégia usada neste artigo é combinar os azure firewall e nsgs na landing zone em duas camadas: o Firewall inspeciona e filtra todo o tráfego Norte-Sul (Spoke → Internet) e Leste-Oeste entre Spokes, enquanto os NSGs impõem microssegmentação dentro do próprio Spoke, limitando a comunicação entre as camadas de aplicação.

Arquitetura do lab

Os recursos de Azure Firewall e NSGs na Landing Zone complementam a Hub-Spoke do Art. 02. O fluxo de tráfego após a configuração é:fego após a configuração é: workload no Spoke → UDR força para o Firewall no Hub → Firewall inspeciona → Internet (ou outra VNet). Os NSGs atuam antes do tráfego chegar ao Firewall, como primeira linha de defesa na subnet.

Recurso Região Descrição
afwp-drlab-brazilsouth Brazil South Firewall Policy com regras de rede e aplicação
afwp-drlab-eastus East US Firewall Policy com regras de rede e aplicação
afw-drlab-brazilsouth Brazil South Azure Firewall SKU Standard na AzureFirewallSubnet
afw-drlab-eastus East US Azure Firewall SKU Standard na AzureFirewallSubnet
pip-afw-drlab-brazilsouth Brazil South IP público estático do Firewall primário
pip-afw-drlab-eastus East US IP público estático do Firewall secundário
rt-spoke-brazilsouth Brazil South Route Table com default route → Firewall
rt-spoke-eastus East US Route Table com default route → Firewall
nsg-frontend / backend / data / aks BRS + EUS NSG por camada com regras de microssegmentação

Regras NSG por camada

NSG Regra Origem Porta Ação
nsg-frontend allow-https-inbound Internet 443 Allow
nsg-frontend allow-http-inbound Internet 80 Allow
nsg-frontend deny-all-inbound * * Deny (p.4096)
nsg-backend allow-frontend-inbound 10.2.1.0/24 8080 Allow
nsg-backend deny-internet-inbound Internet * Deny (p.200)
nsg-data allow-backend-sql 10.2.2.0/24 1433 Allow
nsg-data deny-all-inbound * * Deny (p.4096)
nsg-aks allow-lb-inbound AzureLoadBalancer 443 Allow

Pré-requisitos

Segurança: Os comandos deste artigo utilizam variáveis de ambiente para credenciais. Nunca insira IDs de subscription, senhas ou chaves diretamente nos comandos. Use um arquivo .env local (não versionado) ou o Azure Key Vault para armazenar segredos.

  • Hub-Spoke Landing Zone do Art. 02 provisionada (4 VNets + subnets + peerings)
  • Terraform ≥ 1.5 instalado — terraform -version
  • Azure CLI autenticado — az account show
  • IP privado do Firewall anotado após o deploy (será usado na Route Table)

O custo do Azure Firewall e NSGs na Landing Zone deve ser considerado: o Azure Firewall SKU Standard custa aproximadamente USD 1,25/hora por instância — por isso, neste lab, recomenda-se destruir o Fir lab, recomenda-se destruir o Firewall ao final de cada sessão de testes e recriá-lo quando necessário. O custo dos demais recursos deste artigo é negligenciável.

Variáveis de ambiente

No Azure Firewall e NSGs na Landing Zone, além da subscription_id, o módulo recebe o IP privado do Firewall para configurar as rotas da UDR. O IP privado éara configurar as rotas da UDR. O IP privado é atribuído automaticamente pelo Azure na AzureFirewallSubnet (normalmente o primeiro IP disponível, como 10.0.0.4):

export TF_VAR_subscription_id="<SUBSCRIPTION_ID>"

# Obter o IP privado do Firewall após o apply do passo 1
export TF_VAR_firewall_private_ip_primary="10.0.0.4"   # Hub Brazil South
export TF_VAR_firewall_private_ip_secondary="10.1.0.4" # Hub East US

Para confirmar o IP privado após criar o Firewall, use o comando abaixo. O valor real pode variar conforme a subnet:

az network firewall show \
  --name afw-drlab-brazilsouth \
  --resource-group rg-drlab-network-brazilsouth \
  --query "ipConfigurations[0].privateIPAddress" \
  -o tsv

Passo 1 — Azure Firewall Policy e Azure Firewall

A Firewall Policy é o modelo moderno de gerenciamento de regras — substitui as coleções clássicas diretamente no recurso do Firewall. Ela permite reutilizar a mesma política em múltiplos Firewalls e herdar regras de uma política pai. Neste lab, cada região tem sua própria política para facilitar a gestão independente:

# Buscar recursos do Art. 02 via data sources
data "azurerm_resource_group" "this" {
  name = "rg-drlab-network-brazilsouth"
}

data "azurerm_subnet" "firewall" {
  name                 = "AzureFirewallSubnet"
  virtual_network_name = "vnet-hub-drlab-brazilsouth"
  resource_group_name  = data.azurerm_resource_group.this.name
}

# IP Público — obrigatório para o Azure Firewall Standard
resource "azurerm_public_ip" "firewall" {
  name                = "pip-afw-drlab-brazilsouth"
  resource_group_name = data.azurerm_resource_group.this.name
  location            = data.azurerm_resource_group.this.location
  allocation_method   = "Static"
  sku                 = "Standard"
  tags                = var.tags
}

# Firewall Policy — gerencia Rule Collection Groups
resource "azurerm_firewall_policy" "this" {
  name                = "afwp-drlab-brazilsouth"
  resource_group_name = data.azurerm_resource_group.this.name
  location            = data.azurerm_resource_group.this.location
  sku                 = "Standard"
  tags                = var.tags
}

# Azure Firewall — associado à Policy e à subnet
resource "azurerm_firewall" "this" {
  name                = "afw-drlab-brazilsouth"
  resource_group_name = data.azurerm_resource_group.this.name
  location            = data.azurerm_resource_group.this.location
  sku_name            = "AZFW_VNet"
  sku_tier            = "Standard"
  firewall_policy_id  = azurerm_firewall_policy.this.id
  tags                = var.tags

  ip_configuration {
    name                 = "ipconfig1"
    subnet_id            = data.azurerm_subnet.firewall.id
    public_ip_address_id = azurerm_public_ip.firewall.id
  }
}

Observe que o Firewall usa sku_name = "AZFW_VNet", que é o modo de implantação dentro de uma VNet (não o modo de hub Virtual WAN). O deploy do Firewall leva entre 5 e 10 minutos — é o recurso mais lento desta série. Além disso, o par East US segue o mesmo padrão, substituindo os nomes e o address space.

Passo 2 — User-Defined Routes (UDR)

Sem as User-Defined Routes, o Azure usa o roteamento padrão e o tráfego do Spoke vai direto para a Internet, sem passar pelo Firewall. A Route Table força o tráfego de saída (0.0.0.0/0) para o IP privado do Firewall como Virtual Appliance:

resource "azurerm_route_table" "spoke" {
  name                          = "rt-spoke-brazilsouth"
  resource_group_name           = data.azurerm_resource_group.this.name
  location                      = data.azurerm_resource_group.this.location
  bgp_route_propagation_enabled = false
  tags                          = var.tags
}

resource "azurerm_route" "default_to_firewall" {
  name                   = "route-default-to-firewall"
  resource_group_name    = data.azurerm_resource_group.this.name
  route_table_name       = azurerm_route_table.spoke.name
  address_prefix         = "0.0.0.0/0"
  next_hop_type          = "VirtualAppliance"
  next_hop_in_ip_address = var.firewall_private_ip
}

# Associar a Route Table às quatro subnets do Spoke
resource "azurerm_subnet_route_table_association" "frontend" {
  subnet_id      = data.azurerm_subnet.frontend.id
  route_table_id = azurerm_route_table.spoke.id
}

resource "azurerm_subnet_route_table_association" "backend" {
  subnet_id      = data.azurerm_subnet.backend.id
  route_table_id = azurerm_route_table.spoke.id
}

resource "azurerm_subnet_route_table_association" "data_tier" {
  subnet_id      = data.azurerm_subnet.data_tier.id
  route_table_id = azurerm_route_table.spoke.id
}

resource "azurerm_subnet_route_table_association" "aks" {
  subnet_id      = data.azurerm_subnet.aks.id
  route_table_id = azurerm_route_table.spoke.id
}

O parâmetro bgp_route_propagation_enabled = false é importante: ele impede que rotas aprendidas via BGP (de um VPN Gateway ou ExpressRoute) sobrescrevam a rota forçada para o Firewall. Sem ele, um failover de conectividade on-premises poderia bypassar o Firewall inadvertidamente.

Passo 3 — NSGs por camada

Cada subnet do Spoke recebe um NSG dedicado com regras que seguem o princípio de menor privilégio: apenas o tráfego explicitamente necessário para aquela camada é permitido. Dessa forma, mesmo que o Firewall seja contornado (o que não deveria acontecer), os NSGs garantem uma segunda camada de proteção.

NSG — Frontend (porta 443 e 80 da Internet)

resource "azurerm_network_security_group" "frontend" {
  name                = "nsg-frontend-brazilsouth"
  resource_group_name = data.azurerm_resource_group.workload.name
  location            = data.azurerm_resource_group.workload.location
  tags                = var.tags
}

resource "azurerm_network_security_rule" "allow_https" {
  name                        = "allow-https-inbound"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "443"
  source_address_prefix       = "Internet"
  destination_address_prefix  = "VirtualNetwork"
  resource_group_name         = data.azurerm_resource_group.workload.name
  network_security_group_name = azurerm_network_security_group.frontend.name
}

resource "azurerm_network_security_rule" "allow_http" {
  name                        = "allow-http-inbound"
  priority                    = 110
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "80"
  source_address_prefix       = "Internet"
  destination_address_prefix  = "VirtualNetwork"
  resource_group_name         = data.azurerm_resource_group.workload.name
  network_security_group_name = azurerm_network_security_group.frontend.name
}

resource "azurerm_network_security_rule" "deny_all_frontend" {
  name                        = "deny-all-inbound"
  priority                    = 4096
  direction                   = "Inbound"
  access                      = "Deny"
  protocol                    = "*"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = data.azurerm_resource_group.workload.name
  network_security_group_name = azurerm_network_security_group.frontend.name
}

resource "azurerm_subnet_network_security_group_association" "frontend" {
  subnet_id                 = data.azurerm_subnet.frontend.id
  network_security_group_id = azurerm_network_security_group.frontend.id
}

NSG — Backend (apenas Frontend pode acessar, porta 8080)

resource "azurerm_network_security_group" "backend" {
  name                = "nsg-backend-brazilsouth"
  resource_group_name = data.azurerm_resource_group.workload.name
  location            = data.azurerm_resource_group.workload.location
  tags                = var.tags
}

resource "azurerm_network_security_rule" "allow_frontend" {
  name                        = "allow-frontend-inbound"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "8080"
  source_address_prefix       = "10.2.1.0/24"  # snet-frontend
  destination_address_prefix  = "VirtualNetwork"
  resource_group_name         = data.azurerm_resource_group.workload.name
  network_security_group_name = azurerm_network_security_group.backend.name
}

resource "azurerm_network_security_rule" "deny_internet_backend" {
  name                        = "deny-internet-inbound"
  priority                    = 200
  direction                   = "Inbound"
  access                      = "Deny"
  protocol                    = "*"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "Internet"
  destination_address_prefix  = "*"
  resource_group_name         = data.azurerm_resource_group.workload.name
  network_security_group_name = azurerm_network_security_group.backend.name
}

NSG — Data (apenas Backend via SQL, porta 1433)

resource "azurerm_network_security_group" "data" {
  name                = "nsg-data-brazilsouth"
  resource_group_name = data.azurerm_resource_group.workload.name
  location            = data.azurerm_resource_group.workload.location
  tags                = var.tags
}

resource "azurerm_network_security_rule" "allow_backend_sql" {
  name                        = "allow-backend-sql"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "1433"
  source_address_prefix       = "10.2.2.0/24"  # snet-backend
  destination_address_prefix  = "VirtualNetwork"
  resource_group_name         = data.azurerm_resource_group.workload.name
  network_security_group_name = azurerm_network_security_group.data.name
}

resource "azurerm_network_security_rule" "deny_all_data" {
  name                        = "deny-all-inbound"
  priority                    = 4096
  direction                   = "Inbound"
  access                      = "Deny"
  protocol                    = "*"
  source_port_range           = "*"
  destination_port_range      = "*"
  source_address_prefix       = "*"
  destination_address_prefix  = "*"
  resource_group_name         = data.azurerm_resource_group.workload.name
  network_security_group_name = azurerm_network_security_group.data.name
}

NSG — AKS (Azure Load Balancer na porta 443)

resource "azurerm_network_security_group" "aks" {
  name                = "nsg-aks-brazilsouth"
  resource_group_name = data.azurerm_resource_group.workload.name
  location            = data.azurerm_resource_group.workload.location
  tags                = var.tags
}

resource "azurerm_network_security_rule" "allow_lb_aks" {
  name                        = "allow-lb-inbound"
  priority                    = 100
  direction                   = "Inbound"
  access                      = "Allow"
  protocol                    = "Tcp"
  source_port_range           = "*"
  destination_port_range      = "443"
  source_address_prefix       = "AzureLoadBalancer"
  destination_address_prefix  = "VirtualNetwork"
  resource_group_name         = data.azurerm_resource_group.workload.name
  network_security_group_name = azurerm_network_security_group.aks.name
}

resource "azurerm_subnet_network_security_group_association" "aks" {
  subnet_id                 = data.azurerm_subnet.aks.id
  network_security_group_id = azurerm_network_security_group.aks.id
}

O NSG do AKS usa a Service Tag AzureLoadBalancer em vez de um range de IPs — dessa forma, o Azure atualiza automaticamente os IPs dos load balancers internos sem necessidade de manutenção manual das regras. Além disso, o AKS exige regras específicas para comunicação entre nós; para simplificar o lab, as regras de egresso ficam no nível do Firewall Policy.

Passo 4 — Executar o Terraform

A ordem de execução é importante: o Firewall deve ser criado antes da Route Table, pois a UDR precisa do IP privado do Firewall como next hop. Em seguida, os NSGs podem ser criados em qualquer ordem:

export TF_VAR_subscription_id="<SUBSCRIPTION_ID>"

# 1. Criar o Firewall (5-10 minutos)
cd scripts/art-03-firewall-nsg/afw-blog-castilho-brazilsouth/
terraform init && terraform apply -auto-approve

cd ../afw-blog-castilho-eastus/
terraform init && terraform apply -auto-approve

# 2. Obter IPs privados dos Firewalls
FW_IP_BRS=$(az network firewall show \
  --name afw-drlab-brazilsouth \
  --resource-group rg-drlab-network-brazilsouth \
  --query "ipConfigurations[0].privateIPAddress" -o tsv)

FW_IP_EUS=$(az network firewall show \
  --name afw-drlab-eastus \
  --resource-group rg-drlab-network-eastus \
  --query "ipConfigurations[0].privateIPAddress" -o tsv)

# 3. Criar as Route Tables
export TF_VAR_firewall_private_ip="$FW_IP_BRS"
cd ../rt-spoke-brazilsouth/
terraform init && terraform apply -auto-approve

export TF_VAR_firewall_private_ip="$FW_IP_EUS"
cd ../rt-spoke-eastus/
terraform init && terraform apply -auto-approve

# 4. Criar os NSGs (em paralelo ou sequencial)
for nsg in nsg-frontend-brazilsouth nsg-backend-brazilsouth nsg-data-brazilsouth nsg-aks-brazilsouth \
           nsg-frontend-eastus nsg-backend-eastus nsg-data-eastus nsg-aks-eastus; do
  cd ../$nsg/ && terraform init && terraform apply -auto-approve && cd ..
done

Verificar a configuração

Após o deploy, confirme que os azure firewall e nsgs na landing zone estão ativos e que as rotas foram associadas corretamente às subnets do Spoke:

# Verificar estado do Firewall
az network firewall show \
  --name afw-drlab-brazilsouth \
  --resource-group rg-drlab-network-brazilsouth \
  --query "{nome:name, estado:provisioningState, ip:ipConfigurations[0].privateIPAddress}" \
  -o table

# Verificar routes da Route Table
az network route-table route list \
  --route-table-name rt-spoke-brazilsouth \
  --resource-group rg-drlab-workload-brazilsouth \
  --query "[].{nome:name, prefixo:addressPrefix, nextHop:nextHopIpAddress}" \
  -o table

# Verificar NSGs das subnets do Spoke
az network vnet subnet show \
  --vnet-name vnet-spoke-drlab-brazilsouth \
  --resource-group rg-drlab-workload-brazilsouth \
  --name snet-frontend \
  --query "networkSecurityGroup.id" \
  -o tsv

O resultado esperado para a Route Table deve mostrar uma rota 0.0.0.0/0 com next hop VirtualAppliance e o IP privado do Firewall (ex: 10.0.0.4). Se a rota não aparecer associada à subnet, verificar se o azurerm_subnet_route_table_association foi aplicado sem erros.

Troubleshooting

Erro Causa Solução
AzureFirewallSubnetAddressPrefixTooSmall Subnet menor que /26 A AzureFirewallSubnet deve ter pelo menos /26 — verificar o Art. 02
Tráfego bloqueado mesmo com regra Allow no NSG A Route Table redireciona o tráfego para o Firewall antes do NSG ser avaliado Adicionar também a regra na Firewall Policy (Application ou Network Rule Collection)
FirewallPrivateIPNotFound na Route Table IP privado incorreto em TF_VAR_firewall_private_ip Consultar o IP real com az network firewall show --query ipConfigurations[0].privateIPAddress
NSG criado mas subnet não associada azurerm_subnet_network_security_group_association não aplicado Executar terraform apply novamente no módulo do NSG
Deploy do Firewall timeout (>15 min) Provisioning lento em Brazil South Aguardar — o Azure Firewall pode levar até 20 minutos; re-executar terraform apply se necessário

Limpeza dos recursos

O Azure Firewall é o recurso mais caro desta série. Para evitar cobranças durante o desenvolvimento, destrua-o ao final de cada sessão. Os NSGs e Route Tables podem permanecer — eles têm custo zero e o Azure simplesmente os ignora enquanto não há Firewall respondendo no next hop:

export TF_VAR_subscription_id="<SUBSCRIPTION_ID>"

# Destruir os Firewalls (maior economia)
cd scripts/art-03-firewall-nsg/afw-blog-castilho-brazilsouth/
terraform destroy -auto-approve

cd ../afw-blog-castilho-eastus/
terraform destroy -auto-approve

# Opcional: destruir também NSGs e Route Tables
for recurso in nsg-frontend-brazilsouth nsg-backend-brazilsouth nsg-data-brazilsouth nsg-aks-brazilsouth \
              rt-spoke-brazilsouth rt-spoke-eastus; do
  cd ../$recurso/ && terraform destroy -auto-approve && cd ..
done

Próximos passos

Com os azure firewall e nsgs na landing zone configurados, a Landing Zone está protegida contra tráfego não autorizado tanto no perímetro (Firewall) quanto entre as camadas de aplicação (NSGs). No próximo artigo, vamos adicionar o Azure Bastion em cada Hub, permitindo acesso SSH e RDP às VMs do Spoke sem expor nenhum IP público — complementando a estratégia de segurança em profundidade desta Landing Zone.

Série: DR Azure e Landing Zone
  • 📖 Art. 01: Planejamento de DR Azure e Landing Zone
  • 📖 Art. 02: Hub-Spoke Landing Zone com Terraform
  • ⚙️ Art. 03 (este): Azure Firewall e NSGs na Landing Zone
  • 🔒 Art. 04: Azure Bastion na Landing Zone sem IP Público
  • 🔒 Art. 05: Azure Front Door e Traffic Manager para Failover
  • 🔒 Art. 06: DNS Privado Multi-Região no Azure
  • 🔒 Art. 07: Azure Site Recovery para VMs Multi-Região
  • 🔒 Art. 08: Azure Storage com Geo-Replicação para DR
  • 🔒 Art. 09: AKS Multi-Região com Failover no Azure
  • 🔒 Art. 10: Velero no AKS para Backup e Restore Cross-Region
  • 🔒 Art. 11: Runbooks de Failover no Azure Automation
  • 🔒 Art. 12: Simular um DR no Azure sem Impacto em Produção
  • 🔒 Art. 13: Monitoramento do DR no Azure com Azure Monitor

Interessado em saber mais sobre artigos relacionados ao Microsoft Azure CLIQUE AQUI

🚀 Vamos nos conectar?

Não perca nenhuma oportunidade! Cadastre-se nas minhas redes e no canal do YouTube para receber conteúdos de TI, Cloud, Azure, Kubernetes e DevOps em primeira mão.

Dica: No Facebook, todos os artigos do blog são publicados automaticamente. Vale a pena curtir!


💬 Dúvidas ou Problemas?

Com o intuito de ajudar a comunidade, caso você tenha dúvidas ou encontre problemas na execução dos comandos deste artigo, deixe um comentário abaixo. Responderei o mais breve possível!

Muito obrigado pela visita e até o próximo post!

Jefferson Castilho Especialista em Cloud & DevOps.

Este guia técnico é exclusivo do Blog do Castilho. Explore nossa para mais conteúdos sobre IA e Cloud.

 

Deixe uma resposta

Rolar para cima

Descubra mais sobre Blog do Castilho - Tecnologia | FinOps | DevOps | Cloud

Assine agora mesmo para continuar lendo e ter acesso ao arquivo completo.

Continue reading