Limitare il traffico di rete con Firewall di Azure nel servizio Azure Kubernetes

Il presente articolo illustra come utilizzare le regole di rete in uscita e il nome di dominio completo (FQDN) per i cluster AKS al fine di controllare il traffico in uscita usando il Firewall di Azure. Per semplificare questa configurazione, Firewall di Azure fornisce un tag FQDN del servizio Azure Kubernetes (AzureKubernetesService) che limita il traffico in uscita dal cluster del servizio Azure Kubernetes.

Prerequisiti

Requisiti IP front-end del firewall

  • Punto di partenza per la produzione: prevedere almeno 20 indirizzi IP frontend in Firewall di Azure e dimensionare la configurazione in base al carico di lavoro per evitare l'esaurimento delle porte di SNAT.
  • Cluster a traffico elevato: se il cluster crea molte connessioni in uscita verso le stesse destinazioni, potrebbero essere necessari più indirizzi IP frontend o l'integrazione di Firewall di Azure con NAT Gateway per aumentare la capacità SNAT.
  • Protezione del server API: aggiungere l'IP front-end pubblico del firewall agli intervalli IP autorizzati del server API per una maggiore sicurezza
  • Accesso per sviluppatori: quando si usano intervalli IP autorizzati, usare un jumpbox nella rete virtuale (VNet) del firewall o aggiungere indirizzi IP degli endpoint per sviluppatori all'intervallo autorizzato

Queste linee guida si applicano durante il processo di configurazione descritto in questo articolo.

Note

Il tag FQDN contiene tutti i nomi di dominio completi elencati nelle regole di rete in uscita e FQDN per i cluster AKS e viene aggiornato automaticamente.

Panoramica dell'architettura

Il diagramma seguente illustra l'architettura di un cluster AKS con traffico in uscita limitato attraverso Firewall di Azure:

Topologia bloccata

Il diagramma mostra i nodi agente di AKS in una subnet dedicata che instradano il traffico in uscita attraverso Firewall di Azure mediante una UDR. Per il traffico in ingresso, una regola DNAT converte l'indirizzo IP pubblico del firewall nell'indirizzo IP front-end del servizio di bilanciamento del carico pubblico.

I componenti chiave di questa architettura includono:

  • L'ingresso pubblico viene forzato a passare attraverso i filtri firewall:
    • I nodi dell'agente di AKS sono isolati in una subnet dedicata.
    • Firewall di Azure viene distribuito nella propria subnet.
    • Una regola DNAT converte l'INDIRIZZO IP pubblico del firewall nell'indirizzo IP front-end del servizio di bilanciamento del carico.
  • Le richieste in uscita iniziano dai nodi agente all'indirizzo IP privato Firewall di Azure usando una route definita dall'utente:Outbound requests start from agent nodes to the Firewall di Azure private IP using a user-defined route (UDR):
    • Le richieste provenienti dai nodi agente del servizio Azure Kubernetes seguono una route definita dall'utente che è stata inserita nella subnet in cui è stato distribuito il cluster del servizio Azure Kubernetes.
    • Firewall di Azure esce dalla rete virtuale da un front-end IP pubblico.
    • L'accesso alla rete Internet pubblica o ad altri servizi di Azure passa da e verso l'indirizzo IP front-end del firewall.
    • È possibile proteggere l'accesso al piano di controllo dell'Azure Kubernetes Service usando intervalli IP autorizzati del server API, incluso l'indirizzo IP front-end pubblico del firewall.
  • Traffico interno:

Configurare le variabili di ambiente

Nella tabella seguente sono elencate le variabili di ambiente usate in questo articolo. Impostare queste variabili nella shell prima di procedere o modificare i comandi per usare i propri valori.

Variable Description Valore di esempio
RESOURCE_GROUP Nome del gruppo di risorse aks-egress-rg
LOCATION Area di Azure per le risorse eastus
CLUSTER_NAME Nome del cluster AKS aks-egress
VNET_NAME Nome della rete virtuale aks-egress-vnet
AKS_SUBNET_NAME Nome della subnet per AKS aks-subnet
FW_SUBNET_NAME Nome della subnet per Firewall di Azure AzureFirewallSubnet
FW_NAME Nome del firewall di Azure aks-egress-fw
FW_PUBLICIP_NAME Nome dell'indirizzo IP pubblico per Firewall di Azure aks-egress-fwpublicip
FW_IPCONFIG_NAME Nome della configurazione IP per Firewall di Azure aks-egress-fwconfig
FW_ROUTE_TABLE_NAME Nome della tabella di route per Firewall di Azure aks-egress-fwrt
FW_ROUTE_NAME_1 Nome della route per Firewall di Azure aks-egress-fwrn
FW_ROUTE_NAME_2 Nome della route Internet per Firewall di Azure aks-egress-fwrn-internet

Creare un gruppo di risorse

  • Creare un gruppo di risorse usando il comando az group create.

    az group create --name $RESOURCE_GROUP --location $LOCATION
    

Creare una rete virtuale con più subnet

Effettuare il provisioning di una rete virtuale con due subnet separate: una per il cluster e una per il firewall. Facoltativamente, è possibile crearne una per l'ingresso del servizio interno. Il diagramma seguente illustra la topologia di rete vuota prima di distribuire qualsiasi risorsa:

Topologia di rete vuota

Il diagramma mostra la VNet con subnet separate per AKS e Firewall di Azure prima della distribuzione delle risorse.

  1. Creare una rete virtuale usando il az network vnet create comando .

    az network vnet create \
        --resource-group $RESOURCE_GROUP \
        --name $VNET_NAME \
        --location $LOCATION \
        --address-prefixes 10.42.0.0/16 \
        --subnet-name $AKS_SUBNET_NAME \
        --subnet-prefix 10.42.1.0/24
    
  2. Creare una subnet per Firewall di Azure usando il az network vnet subnet create comando .

    # Dedicated subnet for Azure Firewall (subnet must be named "AzureFirewallSubnet")
    az network vnet subnet create \
        --resource-group $RESOURCE_GROUP \
        --vnet-name $VNET_NAME \
        --name $FW_SUBNET_NAME \
        --address-prefix 10.42.2.0/24
    

Creare un indirizzo IP pubblico per Firewall di Azure

  • Creare una risorsa IP pubblico SKU standard usando il comando az network public-ip create. Usare questa risorsa come indirizzo IP front-end per il Firewall di Azure. La configurazione a ip singolo in questo articolo è destinata ai test. Per la produzione, usare i requisiti IP front-end del firewall per pianificare la capacità SNAT per il carico di lavoro.

    az network public-ip create --resource-group $RESOURCE_GROUP --name $FW_PUBLICIP_NAME --location $LOCATION --sku "Standard"
    

Installare l'estensione CLI di Firewall di Azure

Creare un firewall di Azure e abilitare il proxy DNS

Note

Per gli scenari con traffico elevato, vedere la sezione requisiti IP front-end del firewall .

Per altre informazioni su come creare un firewall di Azure con più indirizzi IP, vedere Creare un firewall di Azure con più indirizzi IP pubblici usando Bicep.

Firewall e route definita dall'utente

Il diagramma mostra Firewall di Azure distribuito nella propria subnet, con una route definita dall'utente (UDR) che indirizza il traffico dalla subnet di AKS all'indirizzo IP privato del firewall.

  • Creare un firewall di Azure e abilitare il proxy DNS usando il az network firewall create comando con --enable-dns-proxy impostato su true.

    az network firewall create --resource-group $RESOURCE_GROUP --name $FW_NAME --location $LOCATION --enable-dns-proxy true
    

    La configurazione dell'indirizzo IP pubblico in Firewall di Azure potrebbe richiedere alcuni minuti. Una volta pronto, è possibile assegnare l'indirizzo IP al front-end del firewall.

    Note

    Per usare FQDN nelle regole di rete, è necessario abilitare il proxy DNS. Quando il proxy DNS è abilitato, il firewall è in ascolto sulla porta 53 e inoltra le richieste DNS al server DNS specificato. Questa impostazione consente al firewall di tradurre automaticamente il FQDN (nome di dominio completamente qualificato).

Creare una configurazione IP per Firewall di Azure

  • Creare una configurazione IP di Firewall di Azure usando il comando az network firewall ip-config create.

    az network firewall ip-config create --resource-group $RESOURCE_GROUP --firewall-name $FW_NAME --name $FW_IPCONFIG_NAME --public-ip-address $FW_PUBLICIP_NAME --vnet-name $VNET_NAME
    

Ottenere gli indirizzi IP di Firewall di Azure

  • Salvare gli indirizzi IP front-end del firewall pubblico e privato per la configurazione in un secondo momento usando i comandi seguenti:

    export FW_PUBLIC_IP=$(az network public-ip show --resource-group $RESOURCE_GROUP --name $FW_PUBLICIP_NAME --query "ipAddress" -o tsv)
    export FW_PRIVATE_IP=$(az network firewall show --resource-group $RESOURCE_GROUP --name $FW_NAME --query "ipConfigurations[0].privateIPAddress" -o tsv)
    

    Note

    Per la sicurezza del server API, vedere la sezione requisiti IP front-end del firewall .

Configurare la rete virtuale per l'uso di Firewall di Azure come server DNS

  • Configurare la rete virtuale per usare l'indirizzo IP privato Firewall di Azure come server DNS usando il az network vnet update comando . Questa configurazione garantisce che i nodi AKS e Firewall di Azure risolvano in modo coerente gli FQDN di destinazione nelle regole di rete.

    az network vnet update \
        --resource-group $RESOURCE_GROUP \
        --name $VNET_NAME \
        --dns-servers $FW_PRIVATE_IP
    

Requisiti UDR per il traffico in uscita di AKS tramite Firewall di Azure

Azure instrada automaticamente il traffico tra subnet di Azure, reti virtuali e reti locali. Per modificare il routing predefinito, creare una tabella di route con i requisiti seguenti:

Categoria Requisito Valore o descrizione
Parametro di route Destination 0.0.0.0/0 (tutto il traffico)
Parametro di route Tipo di passaggio successivo Appliance virtuale di rete
Parametro di route Indirizzo IP dell'hop successivo Indirizzo IP privato di Firewall di Azure
Parametro di route Associazione Una tabella di route per subnet (zero o uno consentito)
Vincolo UDR Route Internet predefinita 0.0.0.0/0 esiste già, ma richiede un indirizzo IP pubblico per SNAT.
Vincolo UDR Hop successivo La route deve puntare a un gateway o a un'appliance virtuale di rete, non direttamente a Internet.
Vincolo UDR Convalida di AKS AKS convalida la configurazione di routing e impedisce route dirette verso Internet.
Vincolo UDR Limite della tabella di instradamento Ogni subnet supporta un massimo di una tabella di route associata.
Impatto sul tipo in uscita UDR (userDefinedRouting) Non viene creato alcun indirizzo IP pubblico del servizio di bilanciamento del carico per le richieste in uscita.
Impatto del tipo di uscita Indirizzo IP pubblico del servizio di bilanciamento del carico Creato solo per le richieste in ingresso che usano un LoadBalancer servizio.
Impatto del tipo di uscita Configurazione SNAT Richiede un indirizzo IP pubblico configurato correttamente per la connettività in uscita.

Per altre informazioni, vedere Regole in uscite per Azure Load Balancer.

Creare una route con un hop a Firewall di Azure

  1. Creare una tabella di route vuota usando il az network route-table create comando . La tabella di route definisce il Firewall di Azure come hop successivo. A ogni subnet può essere associata zero o una tabella di route.

    az network route-table create --resource-group $RESOURCE_GROUP --location $LOCATION --name $FW_ROUTE_TABLE_NAME
    
  2. Creare route nella tabella di route per le subnet usando il comando az network route-table route create.

    az network route-table route create --resource-group $RESOURCE_GROUP --name $FW_ROUTE_NAME_1 --route-table-name $FW_ROUTE_TABLE_NAME --address-prefix 0.0.0.0/0 --next-hop-type VirtualAppliance --next-hop-ip-address $FW_PRIVATE_IP
    
    az network route-table route create --resource-group $RESOURCE_GROUP --name $FW_ROUTE_NAME_2 --route-table-name $FW_ROUTE_TABLE_NAME --address-prefix $FW_PUBLIC_IP/32 --next-hop-type Internet
    

Per informazioni su come eseguire l'override delle route di sistema predefinite di Azure o aggiungere altre route alla tabella di route di una subnet, vedere la documentazione della tabella di route di rete virtuale.

Regole in uscita necessarie per il traffico in uscita di AKS attraverso Firewall di Azure

Note

Per le applicazioni al di fuori degli spazi dei nomi kube-system o gatekeeper-system che devono comunicare con il server API, è necessaria una regola di rete aggiuntiva per consentire la comunicazione TCP alla porta 443 per l'IP del server API, oltre a dover aggiungere una regola applicativa per fqdn-tag di AzureKubernetesService.

Le regole di rete seguenti consentono il traffico in uscita usato in questo articolo:

  • La prima e la seconda regola di rete consentono di recuperare i container da GitHub Container Registry (ghcr.io) e Docker Hub (docker.io).

I cluster del servizio Azure Kubernetes che usano l'agente konnectivity, incluso il cluster creato in questo articolo, non richiedono l'accesso in uscita sulla porta TCP 9000 o sulla porta UDP 1194. Anche i nodi di cui è stato effettuato il provisioning dopo marzo 2021 non richiedono l'accesso NTP in uscita sulla porta UDP 123. Per le regole richieste da altre configurazioni di cluster e altri ambienti cloud di Azure, vedi regole di rete in uscita e FQDN per i cluster AKS.

Creare regole di rete in Firewall di Azure

  • Creare le regole di rete usando i comandi seguenti az network firewall network-rule create .

    az network firewall network-rule create --resource-group $RESOURCE_GROUP --firewall-name $FW_NAME --collection-name 'aksfwnr' --name 'ghcr' --protocols 'TCP' --source-addresses '*' --destination-fqdns ghcr.io pkg-containers.githubusercontent.com --destination-ports '443' --action allow --priority 100
    
    az network firewall network-rule create --resource-group $RESOURCE_GROUP --firewall-name $FW_NAME --collection-name 'aksfwnr' --name 'docker' --protocols 'TCP' --source-addresses '*' --destination-fqdns docker.io registry-1.docker.io production.cloudflare.docker.com --destination-ports '443'
    

Creare regole dell'applicazione in Firewall di Azure

  • Creare la regola dell'applicazione usando il comando az network firewall application-rule create.

    az network firewall application-rule create --resource-group $RESOURCE_GROUP --firewall-name $FW_NAME --collection-name 'aksfwar' --name 'fqdn' --source-addresses '*' --protocols 'http=80' 'https=443' --fqdn-tags "AzureKubernetesService" --action allow --priority 100
    

Per altre informazioni su Firewall di Azure, vedere la documentazione di Firewall di Azure.

Associare la tabella di route al servizio Azure Kubernetes

Per associare il cluster al firewall, la subnet dedicata del cluster deve fare riferimento alla tabella di routing.

  • Associare la tabella di routing al servizio AKS usando il comando az network vnet subnet update.

    az network vnet subnet update --resource-group $RESOURCE_GROUP --vnet-name $VNET_NAME --name $AKS_SUBNET_NAME --route-table $FW_ROUTE_TABLE_NAME
    

Distribuire un cluster del servizio Azure Kubernetes che segue le regole in uscita

È ora possibile distribuire un cluster AKS nel VNet esistente. Si usa il userDefinedRouting tipo in uscita, che garantisce che qualsiasi traffico in uscita venga forzato attraverso il firewall e che non esistano altri percorsi in uscita. È anche possibile utilizzare il loadBalancer tipo outbound.

aks-deploy

Il diagramma mostra il cluster AKS distribuito nella VNet esistente, con il traffico in uscita instradato tramite Firewall di Azure usando UDR.

  • Impostare una variabile di ambiente per l'ID subnet della subnet di destinazione usando il comando seguente:

    SUBNET_ID=$(az network vnet subnet show --resource-group $RESOURCE_GROUP --vnet-name $VNET_NAME --name $AKS_SUBNET_NAME --query id -o tsv)
    

Definisci il tipo di uscita per utilizzare la UDR già esistente nella sottorete. Questa configurazione consente ad AKS di saltare l'installazione e il provisioning IP per il bilanciamento del carico.

Suggerimento

È possibile aggiungere funzionalità aggiuntive alla distribuzione del cluster, ad esempio cluster privati.

Per le considerazioni sulla configurazione degli intervalli IP autorizzati del server API e sull'accesso agli sviluppatori, vedere la sezione Requisiti IP front-end del firewall .


Creare un cluster AKS con un'identità del piano di controllo assegnata dal sistema

Note

Questo cluster utilizza un'identità del piano di controllo assegnata dal sistema. AKS crea un'identità kubelet assegnata dall'utente nel gruppo di risorse del nodo se non si specifica una propria identità gestita kubelet.

Per il routing definito dall'utente, l'identità assegnata dal sistema supporta solo il plug-in di rete CNI.

  • Creare un cluster del servizio Azure Kubernetes usando un'identità gestita assegnata dal sistema con il plug-in di rete CNI usando il comando az aks create.

    az aks create --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --location $LOCATION \
        --node-count 3 \
        --network-plugin azure \
        --outbound-type userDefinedRouting \
        --vnet-subnet-id $SUBNET_ID \
        --api-server-authorized-ip-ranges $FW_PUBLIC_IP \
        --generate-ssh-keys
    

Creare identità gestite assegnate dall'utente

Se non si dispone di identità assegnate dall'utente, seguire la procedura descritta in questa sezione. Se si dispone già di identità assegnate dall'utente, passare a Creare un cluster del servizio Azure Kubernetes con identità assegnate dall'utente.

  1. Creare un'identità gestita usando il comando az identity create.

    az identity create --name myIdentity --resource-group $RESOURCE_GROUP
    

    L'output dovrebbe essere simile all'output di esempio seguente:

     {
       ...
       "id": "/subscriptions/<subscriptionid>/resourcegroups/aks-egress-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myIdentity",
       "location": "eastus",
       "name": "myIdentity",
       ...
       "type": "Microsoft.ManagedIdentity/userAssignedIdentities"
     }
    
  2. Creare un'identità gestita kubelet usando il comando az identity create.

    az identity create --name myKubeletIdentity --resource-group $RESOURCE_GROUP
    

    L'output dovrebbe essere simile all'output di esempio seguente:

    {
      ...
      "id": "/subscriptions/<subscriptionid>/resourcegroups/aks-egress-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myKubeletIdentity",
      "location": "eastus",
      "name": "myKubeletIdentity",
      ...
      "resourceGroup": "aks-egress-rg",
      ...
      "type": "Microsoft.ManagedIdentity/userAssignedIdentities"
    }
    

Note

Se si crea una propria rete virtuale e una tabella di route in cui le risorse si trovano all'esterno del gruppo di risorse del nodo di lavoro, l'interfaccia della riga di comando aggiunge automaticamente l'assegnazione di ruolo. Se si usa un modello di Resource Manager o un altro metodo, è necessario usare l'ID dell’entità di sicurezza dell'identità gestita del cluster per eseguire un'assegnazione di ruolo.

Creare un cluster AKS con identità assegnate dall'utente

  • Creare un cluster del servizio Azure Kubernetes con le identità gestite assegnate dall'utente esistenti nella subnet tramite il comando az aks create. Specificare l'ID della risorsa dell'identità gestita per il piano di controllo e l'ID della risorsa dell'identità kubelet.

    az aks create \
        --resource-group $RESOURCE_GROUP \
        --name $CLUSTER_NAME \
        --location $LOCATION \
        --node-count 3 \
        --network-plugin azure \
        --network-plugin-mode overlay \
        --outbound-type userDefinedRouting \
        --vnet-subnet-id $SUBNET_ID \
        --api-server-authorized-ip-ranges $FW_PUBLIC_IP \
        --assign-identity <identity-resource-id> \
        --assign-kubelet-identity <kubelet-identity-resource-id> \
        --generate-ssh-keys
    

Esaminare il codice Terraform

L'esempio Terraform distribuisce le risorse seguenti:

  • Una rete virtuale Azure con due subnet: una per i nodi del cluster del servizio Azure Kubernetes e una dedicata AzureFirewallSubnet per Firewall di Azure.
  • Un indirizzo IP pubblico dello SKU standard e un Firewall di Azure (SKU AZFW_VNet, livello Standard) che usa un criterio firewall di Azure con proxy DNS abilitato.
  • Un gruppo di raccolte di regole dei criteri del firewall che contiene le regole di rete e applicative necessarie per il traffico in uscita di AKS: regole di rete per le porte del server API (TCP/9000, UDP/1194), NTP (ntp.ubuntu.com su UDP/123), GitHub Container Registry e Docker Hub, oltre a una regola applicativa che usa il tag FQDN AzureKubernetesService sulle porte 80 e 443.
  • Tabella di route con una route predefinita (0.0.0.0/0) che punta all'indirizzo IP privato del firewall come hop successivo dell'appliance virtuale e una route che invia l'indirizzo IP pubblico del firewall direttamente a Internet per evitare il routing asimmetrico. La tabella di instradamento è associata solo alla subnet AKS.
  • Un cluster AKS che usa un'identità gestita assegnata dal sistema, il plug-in di rete azure, il tipo di uscita userDefinedRouting e api_server_access_profile.authorized_ip_ranges limitato all'indirizzo IP pubblico del firewall.
  • Un'assegnazione di ruolo che concede all'identità gestita del cluster AKS il ruolo Network Contributor sul gruppo di risorse, in modo che il cluster possa gestire le risorse di rete da cui dipende.

L'esempio non crea un carico di lavoro demo, una regola DNAT o un servizio di bilanciamento del carico. Dopo aver verificato il cluster, usare kubectl per distribuire la propria applicazione e quindi seguire le indicazioni riportate in precedenza in questo articolo per aggiungere una regola DNAT in Firewall di Azure se è necessaria la connettività in ingresso.

  1. Creare una directory per testare il codice Terraform di esempio e impostarla come directory corrente.

  2. Creare un file denominato main.tf e inserire il codice seguente:

    terraform {
      required_version = ">= 1.5.0"
    
      required_providers {
        azurerm = {
          source  = "hashicorp/azurerm"
          version = "~> 4.0"
        }
      }
    }
    
    provider "azurerm" {
      features {}
    }
    
    variable "prefix" {
      type    = string
      default = "aks-egress"
    }
    
    variable "location" {
      type    = string
      default = "eastus"
    }
    
    variable "node_count" {
      type    = number
      default = 3
    }
    
    variable "vnet_cidr" {
      type    = string
      default = "10.42.0.0/16"
    }
    
    variable "aks_subnet_cidr" {
      type    = string
      default = "10.42.1.0/24"
    }
    
    variable "firewall_subnet_cidr" {
      type    = string
      default = "10.42.2.0/24"
    }
    
    locals {
      resource_group_name    = "${var.prefix}-rg"
      vnet_name              = "${var.prefix}-vnet"
      aks_subnet_name        = "aks-subnet"
      firewall_subnet_name   = "AzureFirewallSubnet"
      firewall_name          = "${var.prefix}-fw"
      firewall_public_ip     = "${var.prefix}-fwpublicip"
      firewall_policy_name   = "${var.prefix}-fwpolicy"
      firewall_ipconfig_name = "${var.prefix}-fwconfig"
      route_table_name       = "${var.prefix}-fwrt"
      default_route_name     = "${var.prefix}-fwrn"
      firewall_public_route  = "${var.prefix}-fwrn-internet"
      aks_name               = var.prefix
    }
    
    resource "azurerm_resource_group" "this" {
      name     = local.resource_group_name
      location = var.location
    }
    
    resource "azurerm_virtual_network" "this" {
      name                = local.vnet_name
      location            = azurerm_resource_group.this.location
      resource_group_name = azurerm_resource_group.this.name
      address_space       = [var.vnet_cidr]
    }
    
    resource "azurerm_subnet" "aks" {
      name                 = local.aks_subnet_name
      resource_group_name  = azurerm_resource_group.this.name
      virtual_network_name = azurerm_virtual_network.this.name
      address_prefixes     = [var.aks_subnet_cidr]
    }
    
    resource "azurerm_subnet" "firewall" {
      name                 = local.firewall_subnet_name
      resource_group_name  = azurerm_resource_group.this.name
      virtual_network_name = azurerm_virtual_network.this.name
      address_prefixes     = [var.firewall_subnet_cidr]
    }
    
    resource "azurerm_public_ip" "firewall" {
      name                = local.firewall_public_ip
      location            = azurerm_resource_group.this.location
      resource_group_name = azurerm_resource_group.this.name
      allocation_method   = "Static"
      sku                 = "Standard"
    }
    
    resource "azurerm_firewall_policy" "this" {
      name                = local.firewall_policy_name
      location            = azurerm_resource_group.this.location
      resource_group_name = azurerm_resource_group.this.name
    
      dns {
        proxy_enabled = true
      }
    }
    
    resource "azurerm_firewall" "this" {
      name                = local.firewall_name
      location            = azurerm_resource_group.this.location
      resource_group_name = azurerm_resource_group.this.name
      sku_name            = "AZFW_VNet"
      sku_tier            = "Standard"
      firewall_policy_id  = azurerm_firewall_policy.this.id
    
      ip_configuration {
        name                 = local.firewall_ipconfig_name
        subnet_id            = azurerm_subnet.firewall.id
        public_ip_address_id = azurerm_public_ip.firewall.id
      }
    }
    
    resource "azurerm_firewall_policy_rule_collection_group" "aks_rules" {
      name               = "aks-egress-rules"
      firewall_policy_id = azurerm_firewall_policy.this.id
      priority           = 100
    
      network_rule_collection {
        name     = "aks-network-rules"
        priority = 100
        action   = "Allow"
    
        rule {
          name                  = "apitcp"
          protocols             = ["TCP"]
          source_addresses      = ["*"]
          destination_addresses = ["AzureCloud.${var.location}"]
          destination_ports     = ["9000"]
        }
    
        rule {
          name                  = "apiudp"
          protocols             = ["UDP"]
          source_addresses      = ["*"]
          destination_addresses = ["AzureCloud.${var.location}"]
          destination_ports     = ["1194"]
        }
    
        rule {
          name              = "time"
          protocols         = ["UDP"]
          source_addresses  = ["*"]
          destination_fqdns = ["ntp.ubuntu.com"]
          destination_ports = ["123"]
        }
    
        rule {
          name             = "ghcr"
          protocols        = ["TCP"]
          source_addresses = ["*"]
          destination_fqdns = [
            "ghcr.io",
            "pkg-containers.githubusercontent.com"
          ]
          destination_ports = ["443"]
        }
    
        rule {
          name             = "docker"
          protocols        = ["TCP"]
          source_addresses = ["*"]
          destination_fqdns = [
            "docker.io",
            "registry-1.docker.io",
            "production.cloudflare.docker.com"
          ]
          destination_ports = ["443"]
        }
      }
    
      application_rule_collection {
        name     = "aks-application-rules"
        priority = 200
        action   = "Allow"
    
        rule {
          name                  = "fqdn"
          source_addresses      = ["*"]
          destination_fqdn_tags = ["AzureKubernetesService"]
    
          protocols {
            type = "Http"
            port = 80
          }
    
          protocols {
            type = "Https"
            port = 443
          }
        }
      }
    }
    
    resource "azurerm_route_table" "this" {
      name                = local.route_table_name
      location            = azurerm_resource_group.this.location
      resource_group_name = azurerm_resource_group.this.name
    }
    
    resource "azurerm_route" "default_to_firewall" {
      name                   = local.default_route_name
      resource_group_name    = azurerm_resource_group.this.name
      route_table_name       = azurerm_route_table.this.name
      address_prefix         = "0.0.0.0/0"
      next_hop_type          = "VirtualAppliance"
      next_hop_in_ip_address = azurerm_firewall.this.ip_configuration[0].private_ip_address
    }
    
    resource "azurerm_route" "firewall_public_ip_to_internet" {
      name                = local.firewall_public_route
      resource_group_name = azurerm_resource_group.this.name
      route_table_name    = azurerm_route_table.this.name
      address_prefix      = "${azurerm_public_ip.firewall.ip_address}/32"
      next_hop_type       = "Internet"
    }
    
    resource "azurerm_subnet_route_table_association" "aks" {
      subnet_id      = azurerm_subnet.aks.id
      route_table_id = azurerm_route_table.this.id
    }
    
    resource "azurerm_kubernetes_cluster" "this" {
      name                = local.aks_name
      location            = azurerm_resource_group.this.location
      resource_group_name = azurerm_resource_group.this.name
      dns_prefix          = "${var.prefix}-dns"
    
      default_node_pool {
        name           = "system"
        vm_size        = "Standard_D2s_v3"
        node_count     = var.node_count
        vnet_subnet_id = azurerm_subnet.aks.id
    
        upgrade_settings {
          max_surge                     = "10%"
          drain_timeout_in_minutes      = 0
          node_soak_duration_in_minutes = 0
        }
      }
    
      identity {
        type = "SystemAssigned"
      }
    
      network_profile {
        network_plugin = "azure"
        outbound_type  = "userDefinedRouting"
      }
    
      api_server_access_profile {
        authorized_ip_ranges = [
          "${azurerm_public_ip.firewall.ip_address}/32"
        ]
      }
    
      depends_on = [
        azurerm_route.default_to_firewall,
        azurerm_subnet_route_table_association.aks,
        azurerm_firewall_policy_rule_collection_group.aks_rules
      ]
    }
    
    resource "azurerm_role_assignment" "network_contributor" {
      scope                = azurerm_resource_group.this.id
      role_definition_name = "Network Contributor"
      principal_id         = azurerm_kubernetes_cluster.this.identity[0].principal_id
    }
    
    output "resource_group_name" {
      value = azurerm_resource_group.this.name
    }
    
    output "aks_cluster_name" {
      value = azurerm_kubernetes_cluster.this.name
    }
    
    output "firewall_public_ip" {
      value = azurerm_public_ip.firewall.ip_address
    }
    
    output "firewall_private_ip" {
      value = azurerm_firewall.this.ip_configuration[0].private_ip_address
    }
    

Inizializza Terraform

Eseguire terraform init per inizializzare l'implementazione di Terraform. Questo comando scarica il azurerm provider necessario per gestire le risorse Azure.

terraform init -upgrade

Formattare e convalidare la configurazione

Eseguire terraform fmt per formattare il file di configurazione e terraform validate per verificare che la configurazione sia sintatticamente valida.

terraform fmt
terraform validate

Crea un piano di esecuzione di Terraform

Eseguire terraform plan per creare un piano di esecuzione.

terraform plan -out main.tfplan

Applicare un piano di esecuzione Terraform

Eseguire terraform apply per applicare il piano di esecuzione alla sottoscrizione Azure.

terraform apply main.tfplan

La creazione della rete virtuale, di Firewall di Azure e del cluster AKS richiede alcuni minuti.

Verificare la configurazione del traffico in uscita, del firewall e delle route

  1. Ottenere il nome del gruppo di risorse, il nome del cluster e l'indirizzo IP pubblico del firewall dagli output di Terraform.

    RESOURCE_GROUP=$(terraform output -raw resource_group_name)
    CLUSTER_NAME=$(terraform output -raw aks_cluster_name)
    FW_PUBLIC_IP=$(terraform output -raw firewall_public_ip)
    
  2. Verifica che il cluster usi il tipo in uscita userDefinedRouting e sia limitato all'indirizzo IP pubblico del firewall usando il comando az aks show.

    az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --query "{outboundType:networkProfile.outboundType, authorizedIpRanges:apiServerAccessProfile.authorizedIpRanges}"
    
  3. Verificare che la tabella di routing della subnet di AKS inoltri il traffico predefinito all'indirizzo IP privato del firewall. Ottenere il nome della tabella di route usando il az network route-table list comando e quindi elencarne le route usando il az network route-table route list comando .

    ROUTE_TABLE_NAME=$(az network route-table list --resource-group $RESOURCE_GROUP --query "[0].name" -o tsv)
    az network route-table route list --resource-group $RESOURCE_GROUP --route-table-name $ROUTE_TABLE_NAME --output table
    
  4. Verificare che siano presenti le regole di rete e applicazione. Ottenere il nome dei criteri del firewall usando il az network firewall policy list comando e quindi visualizzare il gruppo di raccolta regole usando il az network firewall policy rule-collection-group show comando .

    FW_POLICY_NAME=$(az network firewall policy list --resource-group $RESOURCE_GROUP --query "[0].name" -o tsv)
    az network firewall policy rule-collection-group show --resource-group $RESOURCE_GROUP --policy-name $FW_POLICY_NAME --name aks-egress-rules --query "ruleCollections[].name" -o tsv
    

Connettersi al cluster

Configurare kubectl per connettersi al cluster usando il az aks get-credentials comando e quindi verificare la connessione usando il kubectl get nodes comando .

az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME
kubectl get nodes

Il traffico in uscita dal cluster ora viene instradato attraverso Firewall di Azure. Il criterio del firewall consente gli endpoint AKS necessari e blocca per impostazione predefinita tutte le altre connessioni in uscita.

Consentire agli sviluppatori di accedere al server API

Se nel passaggio precedente sono stati usati intervalli IP autorizzati per il cluster, è necessario aggiungere gli indirizzi IP degli strumenti di sviluppo all'elenco di cluster del servizio Azure Kubernetes degli intervalli IP approvati in modo da accedere al server API da questa posizione. È anche possibile configurare un jumpbox con gli strumenti necessari all'interno di una subnet separata nella rete virtuale del firewall.

  1. Recuperare l'indirizzo IP usando il comando seguente:

    CURRENT_IP=$(dig @resolver1.opendns.com ANY myip.opendns.com +short)
    
  2. Aggiungere l'indirizzo IP agli intervalli approvati usando il az aks update comando . L'opzione --api-server-authorized-ip-ranges sostituisce l'elenco corrente, quindi includere l'indirizzo IP pubblico del firewall quando si aggiunge l'indirizzo IP dello sviluppatore.

    az aks update --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --api-server-authorized-ip-ranges $FW_PUBLIC_IP,$CURRENT_IP/32
    

Connettersi al cluster AKS

  • Configurare kubectl per connettersi al cluster del servizio Azure Kubernetes usando il comando az aks get-credentials.

    az aks get-credentials --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME
    

Distribuire un servizio pubblico nel servizio Azure Kubernetes

È ora possibile iniziare a esporre i servizi e distribuire applicazioni in questo cluster. In questo esempio viene esposto un servizio pubblico, ma è anche possibile esporre un servizio interno usando un servizio di bilanciamento del carico interno.

Servizio DNAT pubblico

Il diagramma mostra il traffico pubblico in ingresso che arriva all'indirizzo IP pubblico del firewall, viene tradotto dalla regola DNAT e inoltrato all'indirizzo IP frontend del servizio di bilanciamento del carico pubblico per il servizio AKS.

  1. Esaminare il manifesto di Avvio rapido della demo AKS Store per comprendere i componenti di distribuzione.

  2. Distribuire il servizio usando il kubectl apply comando .

    kubectl apply -f https://raw.githubusercontent.com/Azure-Samples/aks-store-demo/main/aks-store-quickstart.yaml
    

Ottenere l'IP front-end pubblico del Load Balancer

  1. Ottenere l'indirizzo IP front-end pubblico assegnato al servizio di bilanciamento del carico usando il kubectl get services comando .

    kubectl get services
    

    L'indirizzo IP deve essere elencato nella EXTERNAL-IP colonna , come illustrato nell'output di esempio seguente:

    NAME              TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)              AGE
    kubernetes        ClusterIP      10.0.0.1       <none>        443/TCP              9m10s
    order-service     ClusterIP      10.0.104.144   <none>        3000/TCP             11s
    product-service   ClusterIP      10.0.237.60    <none>        3002/TCP             10s
    rabbitmq          ClusterIP      10.0.161.128   <none>        5672/TCP,15672/TCP   11s
    store-front       LoadBalancer   10.0.89.139    20.39.18.6    80:32271/TCP         10s
    
  2. Salvare l'indirizzo IP front-end del servizio di bilanciamento del carico pubblico usando il kubectl get svc store-front comando .

    SERVICE_IP=$(kubectl get svc store-front -o jsonpath='{.status.loadBalancer.ingress[*].ip}')
    

Creare una regola DNAT in Firewall di Azure

Importante

Quando si utilizza Firewall di Azure per limitare il traffico in uscita e si crea una UDR (route definita dall'utente) per instradare tutto il traffico in uscita, assicurarsi di configurare in Firewall di Azure una regola DNAT appropriata per consentire correttamente il traffico in ingresso. L'uso del servizio Firewall di Azure con una route definita dall'utente interrompe la configurazione in ingresso a causa del routing asimmetrico. Questo problema si verifica se la subnet del servizio Azure Kubernetes ha una route predefinita verso l'indirizzo IP privato del firewall, ma si sta usando un servizio di bilanciamento del carico pubblico in ingresso o del servizio Kubernetes di tipo loadBalancer. In questo caso, il traffico del servizio di bilanciamento del carico in ingresso viene ricevuto tramite l'indirizzo IP pubblico, ma il percorso di ritorno passa attraverso l'indirizzo IP privato del firewall. Poiché Firewall di Azure è un firewall con stato, scarta il pacchetto di ritorno perché Firewall di Azure non riconosce una sessione già stabilita. Per informazioni su come integrare Firewall di Azure con il servizio di bilanciamento del carico in ingresso o del servizio, vedere Integrare Firewall di Azure con Azure Load Balancer Standard.

Per configurare la connettività in ingresso, creare una regola DNAT per Firewall di Azure. Per testare la connettività al cluster, la regola converte l'indirizzo IP pubblico front-end del firewall nell'indirizzo IP front-end del servizio di bilanciamento del carico pubblico salvato in $SERVICE_IP. È possibile personalizzare l'indirizzo di destinazione. La porta convertita deve essere la porta esposta per il servizio Kubernetes. Per una topologia che usa invece un indirizzo IP front-end privato, configurare un servizio di bilanciamento del carico interno e usare il relativo indirizzo IP privato come indirizzo convertito.

  • Aggiungere la regola NAT usando il comando az network firewall nat-rule create.

    az network firewall nat-rule create --collection-name exampleset --destination-addresses $FW_PUBLIC_IP --destination-ports 80 --firewall-name $FW_NAME --name inboundrule --protocols Any --resource-group $RESOURCE_GROUP --source-addresses '*' --translated-port 80 --action Dnat --priority 100 --translated-address $SERVICE_IP
    

Convalidare la connettività

  • In un browser accedere all'indirizzo IP front-end di Firewall di Azure per convalidare la connettività. Dovrebbe essere visualizzata l'app dello store AKS. In questo esempio l'indirizzo IP pubblico del firewall era 52.253.228.132:

    Screenshot che mostra l'app Azure Store Front aperta in un browser locale.

    In questa pagina è possibile visualizzare i prodotti, aggiungerli al carrello e quindi effettuare un ordine.

Pulire le risorse

Se le risorse create in questo articolo non sono più necessarie, è possibile eliminarle per evitare di incorrere in costi futuri.

  • Eliminare il gruppo di risorse del servizio AKS usando il comando az group delete.

    az group delete --name $RESOURCE_GROUP
    

Firewall di Azure fattura ogni ora indipendentemente dal fatto che stia elaborando attivamente il traffico, oltre agli addebiti per l'elaborazione dei dati. Non lasciare il firewall (o il resto di questo esempio) in esecuzione più a lungo di quanto necessario.

Avvertimento

Se si lavora con risorse esistenti o di produzione nella stessa directory di lavoro, esaminare attentamente il piano di esecuzione prima di eliminarle. L'esecuzione terraform destroy rimuove tutte le risorse rilevate nel file di stato terraform per questa configurazione, tra cui la rete virtuale, le subnet, Firewall di Azure, i criteri e le regole del firewall, la tabella di route, il cluster del servizio Azure Kubernetes e l'assegnazione di ruolo. Evitare di eseguirlo su un'infrastruttura condivisa, a meno che non si sia certi che sia sicuro rimuoverlo.

Esegui terraform plan con il flag -destroy per verificare cosa rimuove Terraform.

terraform plan -destroy -out main.destroy.tfplan

Eseguire terraform apply per applicare il piano di eliminazione definitiva e rimuovere tutte le risorse create da questo esempio, incluse le Firewall di Azure.

terraform apply main.destroy.tfplan