Einschränken des Netzwerkdatenverkehrs mit Azure Firewall in Azure Kubernetes Service (AKS)

In diesem Artikel wird erläutert, wie Sie die Regeln für ausgehende Netzwerke und vollqualifizierte Domänennamen (Fully Qualified Domain Name, FQDN) für AKS-Cluster verwenden, um den Datenverkehr mithilfe der Azure-Firewall zu steuern. Um diese Konfiguration zu vereinfachen, stellt Azure Firewall ein FQDN-Tag (AzureKubernetesServiceAzure Kubernetes Service) bereit, das ausgehenden Datenverkehr aus dem AKS-Cluster einschränkt.

Voraussetzungen

Ip-Anforderungen für Firewall-Frontend

  • Produktionsstartpunkt: Planen Sie mindestens 20 Front-End-IPs auf Azure Firewall, und legen Sie die Konfiguration für Ihre Workload fest, um die SNAT-Portausschöpfung (Source Network Address Translation) zu vermeiden.
  • Cluster mit hohem Datenverkehr: Wenn Ihr Cluster viele ausgehende Verbindungen mit denselben Zielen erstellt, benötigen Sie möglicherweise mehr Front-End-IPs oder Azure Firewall Integration mit NAT-Gateway, um die SNAT-Kapazität zu erhöhen.
  • API-Serverschutz: Fügen Sie die öffentliche Front-End-IP der Firewall zu den API-Server-Autorisierten-IP-Bereichen hinzu, um die Sicherheit zu erhöhen.
  • Entwicklerzugriff: Wenn Sie autorisierte IP-Bereiche verwenden, verwenden Sie entweder einen Jumpbox im virtuellen Netzwerk (VNet) der Firewall oder fügen Entwicklerendpunkt-IPs zum autorisierten Bereich hinzu.

Dieser Leitfaden gilt für den gesamten in diesem Artikel beschriebenen Konfigurationsprozess.

Hinweis

Das FQDN-Tag enthält alle FQDNs, die in ausgehenden Netzwerk- und FQDN-Regeln für AKS-Cluster aufgelistet sind und automatisch aktualisiert werden.

Architekturübersicht

Das folgende Diagramm veranschaulicht die Architektur eines AKS-Clusters mit eingeschränktem Ausgangsdatenverkehr mithilfe der Azure Firewall:

Gesperrte Topologie

Das Diagramm zeigt AKS-Agentknoten in einem dedizierten Subnetz, die ausgehenden Datenverkehr mithilfe einer UDR über Azure Firewall leiten. Bei eingehendem Datenverkehr setzt eine DNAT-Regel die öffentliche IP-Adresse der Firewall auf die Frontend-IP-Adresse des öffentlichen Lastenausgleichs um.

Zu den wichtigsten Komponenten dieser Architektur gehören:

  • Der öffentliche Eingangsvorgang wird gezwungen, durch Firewallfilter zu fließen:
    • AKS-Agentknoten sind in einem dedizierten Subnetz isoliert.
    • Azure Firewall wird in einem eigenen Subnetz bereitgestellt.
    • Eine DNAT-Regel übersetzt die öffentliche IP der Firewall in die Frontend-IP des Lastenausgleichs.
  • Ausgehende Anforderungen werden von den Agentknoten über eine benutzerdefinierte Route (UDR) an die private IP-Adresse der Azure Firewall geleitet:
    • Anforderungen von AKS-Agent-Knoten folgen einer benutzerdefinierten Route, die in dem Subnetz platziert wurde, in dem der AKS-Cluster bereitgestellt ist.
    • Azure Firewall wird aus dem VNet über ein öffentliches IP-Frontend weitergeleitet.
    • Datenverkehr zum öffentlichen Internet oder zu anderen Azure-Diensten fließt zur und von der Frontend-IP-Adresse der Firewall.
    • Sie können den Zugriff auf die AKS-Steuerebene mithilfe autorisierter IP-Bereiche des API-Servers schützen, einschließlich der öffentlichen IP-Adresse der Firewall.
  • Interner Datenverkehr:

Konfigurieren von Umgebungsvariablen

In der folgenden Tabelle sind die In diesem Artikel verwendeten Umgebungsvariablen aufgeführt. Legen Sie diese Variablen in Der Shell fest, bevor Sie fortfahren, oder ändern Sie die Befehle, um Ihre eigenen Werte zu verwenden.

Variable Description Beispielwert
RESOURCE_GROUP Name der Ressourcengruppe aks-egress-rg
LOCATION Azure-Region für Ressourcen eastus
CLUSTER_NAME Name des AKS-Clusters aks-egress
VNET_NAME Name des virtuellen Netzwerks aks-egress-vnet
AKS_SUBNET_NAME Name des Subnetzes für AKS aks-subnet
FW_SUBNET_NAME Name des Subnetzes für Azure Firewall AzureFirewallSubnet
FW_NAME Name der Azure-Firewall aks-egress-fw
FW_PUBLICIP_NAME Name der öffentlichen IP für Azure Firewall aks-egress-fwpublicip
FW_IPCONFIG_NAME Name der IP-Konfiguration für Azure Firewall aks-egress-fwconfig
FW_ROUTE_TABLE_NAME Name der Routentabelle für Azure Firewall aks-egress-fwrt
FW_ROUTE_NAME_1 Name der Route für Azure Firewall aks-egress-fwrn
FW_ROUTE_NAME_2 Name der Internetroute für Azure Firewall aks-egress-fwrn-internet

Erstellen einer Ressourcengruppe

  • Erstellen Sie mit dem Befehl az group create eine Ressourcengruppe.

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

Erstellen eines virtuellen Netzwerks mit mehreren Subnetzen

Stellen Sie ein VNet mit zwei separaten Subnetzen bereit: eines für den Cluster und eines für die Firewall. Optional können Sie eines für internen eingehenden Dienstdatenverkehr erstellen. Das folgende Diagramm veranschaulicht die leere Netzwerktopologie vor der Bereitstellung von Ressourcen:

Leere Netzwerktopologie

Das Diagramm zeigt das VNet mit separaten Subnetzen für AKS und Azure Firewall, bevor Ressourcen bereitgestellt werden.

  1. Erstellen Sie ein VNet mit dem az network vnet create Befehl.

    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. Erstellen Sie mithilfe des az network vnet subnet create Befehls ein Subnetz für azure Firewall.

    # 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
    

Erstellen einer öffentlichen IP für Azure Firewall

  • Erstellen Sie mithilfe des az network public-ip create-Befehls eine öffentliche IP-Ressource der Standard-SKU. Verwenden Sie diese Ressource als Front-End-IP-Adresse für die Azure Firewall. Die Einzel-IP-Konfiguration in diesem Artikel ist für Tests vorgesehen. Verwenden Sie für die Produktion die Firewall-Frontend-IP-Anforderungen , um die SNAT-Kapazität für Ihre Workload zu planen.

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

Installieren der Azure Firewall CLI-Erweiterung

Erstellen einer Azure-Firewall und Aktivieren eines DNS-Proxys

Hinweis

Informationen zu Szenarien mit hohem Datenverkehr finden Sie im Abschnitt " Firewall-Frontend-IP-Anforderungen ".

Weitere Informationen zum Erstellen einer Azure Firewall mit mehreren IP-Adressen finden Sie unter Erstellen einer Azure Firewall mit mehreren öffentlichen IP-Adressen mittels Bicep.

Firewall und benutzerdefinierte Routen

Das Diagramm zeigt Azure Firewall, die in ihrem Subnetz bereitgestellt ist, wobei eine UDR den Datenverkehr vom AKS-Subnetz an die private IP-Adresse der Firewall weiterleitet.

  • Erstellen Sie eine Azure Firewall und aktivieren Sie den DNS Proxy mit dem az network firewall create-Befehl, wobei --enable-dns-proxy auf true festgelegt ist.

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

    Das Einrichten der öffentlichen IP-Adresse für die Azure-Firewall kann einige Minuten dauern. Sobald es fertig ist, können Sie die IP-Adresse dem Firewall-Front-End zuweisen.

    Hinweis

    Um FQDN für Netzwerkregeln zu verwenden, benötigen Sie DNS-Proxy aktiviert. Wenn DER DNS-Proxy aktiviert ist, überwacht die Firewall Port 53 und leitet DNS-Anforderungen an den von Ihnen angegebenen DNS-Server weiter. Mit dieser Einstellung kann die Firewall den FQDN automatisch übersetzen.

Erstellen einer IP-Konfiguration für Azure Firewall

  • Erstellen Sie mithilfe des az network firewall ip-config create-Befehls eine Azure Firewall IP-Konfiguration.

    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
    

Abrufen der Azure Firewall-IP-Adressen

  • Speichern Sie die Frontend-IP-Adressen der öffentlichen und privaten Firewall zur späteren Konfiguration mittels der folgenden Befehle:

    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)
    

    Hinweis

    Informationen zur API-Serversicherheit finden Sie im Abschnitt " Firewall-Frontend-IP-Anforderungen ".

Konfigurieren des virtuellen Netzwerks für die Verwendung von Azure Firewall als DNS-Server

  • Konfigurieren Sie das virtuelle Netzwerk so, dass die Azure Firewall private IP-Adresse mithilfe des az network vnet update Befehls als DNS-Server verwendet wird. Diese Konfiguration stellt sicher, dass die AKS-Knoten und Azure Firewall die Ziel-FQDNs in den Netzwerkregeln konsistent auflösen.

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

UDR-Anforderungen für den AKS-Ausgang über Azure Firewall

Azure leitet automatisch Datenverkehr zwischen Azure-Subnetzen, VNets und lokalen Netzwerken weiter. Um das Standardrouting zu ändern, erstellen Sie eine Routentabelle mit den folgenden Anforderungen:

Kategorie Anforderung Wert oder Beschreibung
Routenparameter Destination 0.0.0.0/0 (gesamter Datenverkehr)
Routenparameter Typ des nächsten Sprungs Virtuelles Netzwerkgerät (Network Virtual Appliance, NVA)
Routenparameter IP-Adresse für nächsten Hop Private IP-Adresse der Azure Firewall
Routenparameter Verein Eine Routentabelle pro Subnetz (Null oder eine zulässige)
UDR-Beschränkung Standard-Internetroute 0.0.0.0/0 ist bereits vorhanden, erfordert jedoch eine öffentliche IP-Adresse für SNAT.
UDR-Einschränkung Nächster Sprung Die Route muss auf ein Gateway oder eine NVA verweisen, nicht direkt auf das Internet.
UDR-Einschränkung AKS-Validierung AKS überprüft die Routenkonfiguration und verhindert direkte Internetrouten.
UDR-Einschränkung Grenzwert für Routingtabellen Jedes Subnetz unterstützt maximal eine zugeordnete Routentabelle.
Auswirkungen des ausgehenden Typs UDR (userDefinedRouting) Für ausgehenden Datenverkehr wird keine öffentliche IP-Adresse für den Lastenausgleich erstellt.
Auswirkungen des ausgehenden Typs Öffentliche IP-Adresse des Load Balancers Wird nur für eingehende Anforderungen erstellt, die einen LoadBalancer Dienst verwenden.
Auswirkungen des ausgehenden Typs SNAT-Konfiguration Erfordert eine ordnungsgemäß konfigurierte öffentliche IP-Adresse für ausgehende Verbindungen.

Weitere Informationen finden Sie unter Ausgangsregeln für Azure Load Balancer.

Erstellen Sie eine Route mit einem Hop zur Azure Firewall

  1. Erstellen Sie mithilfe des az network route-table create Befehls eine leere Routentabelle. Die Routentabelle definiert die Azure-Firewall als nächsten Hop. Jedes Subnetz kann keine oder eine Routentabelle zugeordnet haben.

    az network route-table create --resource-group $RESOURCE_GROUP --location $LOCATION --name $FW_ROUTE_TABLE_NAME
    
  2. Erstellen Sie Routen in der Routingtabelle für die Subnetze mithilfe des az network route-table route create-Befehls.

    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
    

Informationen zum Außerkraftsetzen der Standardsystemrouten von Azure oder zum Hinzufügen weiterer Routen zur Routingtabelle eines Subnetzs finden Sie in der Dokumentation zur Tabelle der virtuellen Netzwerkroute.

Erforderliche ausgehende Regeln für den AKS-Ausgang über Azure Firewall

Hinweis

Für Anwendungen außerhalb der kube-system- oder gatekeeper-system-Namespaces, die mit dem API-Server kommunizieren müssen, ist eine zusätzliche Netzwerkregel erforderlich. Diese Regel muss zusätzlich zu der Anwendungsregel für fqdn-tag von AzureKubernetesService die TCP-Kommunikation mit Port 443 für die API-Server-IP erlauben.

Die folgenden Netzwerkregeln erlauben den in diesem Artikel verwendeten ausgehenden Datenverkehr:

  • Die ersten und zweiten Netzwerkregeln ermöglichen den Zugriff auf Container aus GitHub Containerregistrierung (ghcr.io) und Docker Hub (docker.io).

AKS-Cluster, die den Konnectivity-Agent verwenden, einschließlich des in diesem Artikel erstellten Clusters, erfordern keinen ausgehenden Zugriff auf TCP-Port 9000 oder UDP-Port 1194. Nach März 2021 bereitgestellte Knoten erfordern auch keinen ausgehenden NTP-Zugriff auf UDP-Port 123. Informationen zu den Regeln, die für andere Clusterkonfigurationen und Azure Clouds erforderlich sind, finden Sie unter ausgehenden Netzwerk- und FQDN-Regeln für AKS-Cluster.

Erstellen von Netzwerkregeln in Azure Firewall

  • Erstellen Sie die Netzwerkregeln mithilfe der folgenden az network firewall network-rule create Befehle.

    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'
    

Erstellen von Anwendungsregeln in Azure Firewall

  • Erstellen Sie die Anwendungsregel mithilfe des az network firewall application-rule create-Befehls.

    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
    

Weitere Informationen zur Azure Firewall finden Sie in der Azure Firewall-Dokumentation.

Zuordnen der Routingtabelle zu AKS

Um den Cluster der Firewall zuzuordnen, muss das dedizierte Subnetz für das Subnetz des Clusters auf die Routentabelle verweisen.

  • Ordnen Sie die Routentabelle mit dem az network vnet subnet update Befehl AKS zu.

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

Bereitstellen eines AKS-Clusters, der Ihren ausgehenden Regeln folgt

Sie können jetzt einen AKS-Cluster im vorhandenen VNet bereitstellen. Sie verwenden den userDefinedRouting Outbound-Typ, mit dem sichergestellt wird, dass ausgehender Datenverkehr über die Firewall geleitet wird und keine anderen Egress-Pfade bestehen. Sie können auch den loadBalancer ausgehenden Typ verwenden.

aks-deploy

Das Diagramm zeigt den im vorhandenen VNet bereitgestellten AKS-Cluster, wobei ausgehender Datenverkehr über Azure Firewall mithilfe des UDR weitergeleitet wird.

  • Legen Sie eine Umgebungsvariable für die Subnetz-ID des Zielsubnetz mithilfe des folgenden Befehls fest:

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

Sie definieren den ausgehenden Typ, um den UDR zu verwenden, der bereits im Subnetz vorhanden ist. Mit dieser Konfiguration kann AKS die Setup- und IP-Bereitstellung für das Lastenausgleichsmodul überspringen.

Tipp

Sie können der Clusterbereitstellung zusätzliche Features hinzufügen, z. B. private Cluster.

Informationen zu autorisierten IP-Bereichen für API-Server und Überlegungen zum Entwicklerzugriff finden Sie im Abschnitt " Firewall-Frontend-IP-Anforderungen ".


Erstellen Sie einen AKS-Cluster mit einer systemseitig zugewiesenen Control-Plane-Identität

Hinweis

Dieser Cluster verwendet eine systemseitig zugewiesene Identität der Steuerungsebene. AKS erstellt eine vom Benutzer zugewiesene Kubelet-Identität in der Knotenressourcengruppe, wenn Sie keine eigene verwaltete Kubelet-Identität angeben.

Für das benutzerdefinierte Routing unterstützt die systemseitig zugewiesene Identität nur das CNI-Netzwerk-Plug-In.

  • Erstellen Sie einen AKS-Cluster mithilfe einer systemseitig zugewiesenen verwalteten Identität mit dem CNI-Netzwerk-Plug-In mittels dem az aks create-Befehl.

    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
    

Erstellen von vom Benutzer zugewiesenen verwalteten Identitäten

Wenn Sie nicht über benutzerseitig zugewiesene Identitäten verfügen, folgen Sie den Schritten in diesem Abschnitt. Wenn Sie bereits über benutzerseitig zugewiesene Identitäten verfügen, fahren Sie mit Erstellen eines AKS-Clusters mit benutzerseitig zugewiesenen Identitäten fort.

  1. Erstellen Sie mithilfe des az identity create-Befehls eine verwaltete Identität.

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

    Die Ausgabe sollte in etwa wie die folgende Beispielausgabe aussehen:

     {
       ...
       "id": "/subscriptions/<subscriptionid>/resourcegroups/aks-egress-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myIdentity",
       "location": "eastus",
       "name": "myIdentity",
       ...
       "type": "Microsoft.ManagedIdentity/userAssignedIdentities"
     }
    
  2. Erstellen Sie mithilfe des az identity create-Befehls eine verwaltete Kubelet-Identität.

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

    Die Ausgabe sollte in etwa wie die folgende Beispielausgabe aussehen:

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

Hinweis

Wenn Sie eine eigene VNet- und Routentabelle erstellen, in der sich die Ressourcen außerhalb der Ressourcengruppe des Arbeitsknotens befinden, fügt die CLI automatisch die Rollenzuweisung hinzu. Wenn Sie eine ARM-Vorlage oder eine andere Methode verwenden, müssen Sie die Prinzipal-ID der vom Cluster verwalteten Identität verwenden, um eine Rollenzuweisung durchzuführen.

Erstellen eines AKS-Clusters mit benutzerdefinierten Identitäten

  • Erstellen Sie einen AKS-Cluster mit Ihren vorhandenen benutzerseitig zugewiesenen verwalteten Identitäten im Subnetz mit dem az aks create-Befehl. Geben Sie die Ressourcen-ID der verwalteten Identität für die Steuerungsebene und die Ressourcen-ID der Kubelet-Identität an.

    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
    

Überprüfen Sie den Terraform-Code

Das Terraform-Beispiel stellt die folgenden Ressourcen bereit:

  • Ein virtuelles Azure-Netzwerk mit zwei Subnetzen: eines für die AKS-Clusterknoten und ein dediziertes AzureFirewallSubnetSubnetz für Azure Firewall.
  • Eine öffentliche SKU-Standard-IP-Adresse und eine Azure Firewall (AZFW_VNet SKU, Standard Ebene), die eine Azure Firewall-Richtlinie mit aktiviertem DNS-Proxy verwendet.
  • Eine Firewallrichtlinienregelsammlungsgruppe, die die Netzwerk- und Anwendungsregeln enthält, die für den AKS-Ausgang erforderlich sind: Netzwerkregeln für die API-Serverports (TCP/9000, UDP/1194), NTP (ntp.ubuntu.com auf UDP/123), GitHub Containerregistrierung und Docker Hub sowie eine Anwendungsregel, die das AzureKubernetesService FQDN-Tag an den Ports 80 und 443 verwendet.
  • Eine Routentabelle mit einer Standardroute (0.0.0.0/0), die auf die private IP-Adresse der Firewall als nächster Hop einer virtuellen Appliance verweist, und eine Route, die die eigene öffentliche IP-Adresse der Firewall direkt an das Internet sendet, um asymmetrisches Routing zu vermeiden. Die Routentabelle ist nur dem AKS-Subnetz zugeordnet.
  • Ein AKS-Cluster, das eine systemseitig zugewiesene verwaltete Identität, das azure Netzwerkplugin, den userDefinedRouting-Ausgangstyp und api_server_access_profile.authorized_ip_ranges, beschränkt auf die öffentliche IP-Adresse der Firewall, verwendet.
  • Eine Rollenzuweisung , die der verwalteten Identität des AKS-Clusters die Network Contributor Rolle in der Ressourcengruppe zugibt, sodass der Cluster die netzwerkressourcen verwalten kann, von denen es abhängt.

Das Beispiel erstellt weder einen Demo-Workload noch eine DNAT-Regel oder einen Load-Balancer-Dienst. Nachdem Sie den Cluster überprüft haben, verwenden Sie kubectl, um Ihre eigene Anwendung bereitzustellen, und befolgen Sie dann die weiter oben in diesem Artikel beschriebenen Anweisungen, um in Azure Firewall eine DNAT-Regel hinzuzufügen, falls Sie eingehende Verbindungen benötigen.

  1. Erstellen Sie ein Verzeichnis, um den Terraform-Beispielcode zu testen, und machen Sie es zum aktuellen Verzeichnis.

  2. Erstellen Sie eine Datei mit dem Namen main.tf, und fügen Sie den folgenden Code ein:

    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
    }
    

Terraform initialisieren

Führen Sie terraform init aus, um die Bereitstellung der Terraform zu initialisieren. Dieser Befehl lädt den zum Verwalten Ihrer Azure-Ressourcen erforderlichen azurermAnbieter herunter.

terraform init -upgrade

Formatieren und Überprüfen der Konfiguration

Führen Sie die Ausführung terraform fmt aus, um die Konfigurationsdatei zu formatieren, und terraform validate um zu bestätigen, dass die Konfiguration syntaktisch gültig ist.

terraform fmt
terraform validate

Terraform-Ausführungsplan erstellen

Ausführen terraform plan , um einen Ausführungsplan zu erstellen.

terraform plan -out main.tfplan

Wenden Sie einen Terraform-Ausführungsplan an

Führen Sie diesen terraform apply Befehl aus, um den Ausführungsplan auf Ihr Azure-Abonnement anzuwenden.

terraform apply main.tfplan

Das Erstellen des virtuellen Netzwerks, Azure Firewall und des AKS-Clusters dauert mehrere Minuten.

Überprüfen der Konfiguration von Ausgangs-, Firewall- und Routenkonfigurationen

  1. Rufen Sie den Ressourcengruppennamen, den Clusternamen und die öffentliche IP-Adresse der Firewall aus den Terraform-Ausgaben ab.

    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. Vergewissern Sie sich, dass der Cluster den userDefinedRouting ausgehenden Typ verwendet und mithilfe des az aks show Befehls auf die öffentliche IP-Adresse der Firewall beschränkt ist.

    az aks show --resource-group $RESOURCE_GROUP --name $CLUSTER_NAME --query "{outboundType:networkProfile.outboundType, authorizedIpRanges:apiServerAccessProfile.authorizedIpRanges}"
    
  3. Vergewissern Sie sich, dass die Route-Tabelle des AKS-Subnetzes den Standarddatenverkehr an die private IP-Adresse der Firewall sendet. Rufen Sie den Namen der Routentabelle mithilfe des az network route-table list Befehls ab, und listen Sie dann die Routen mithilfe des az network route-table route list Befehls auf.

    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. Vergewissern Sie sich, dass die Netzwerk- und Anwendungsregeln vorhanden sind. Rufen Sie den Namen der Firewallrichtlinie mithilfe des az network firewall policy list Befehls ab, und zeigen Sie dann die Regelsammlungsgruppe mithilfe des az network firewall policy rule-collection-group show Befehls an.

    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
    

Herstellen einer Verbindung mit dem Cluster

Konfigurieren Sie kubectl so, dass eine Verbindung mit Ihrem Cluster hergestellt wird, mithilfe des Befehls az aks get-credentials, und überprüfen Sie dann die Verbindung mithilfe des Befehls kubectl get nodes.

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

Der aus dem Cluster ausgehende Datenverkehr wird jetzt über die Azure Firewall geleitet. Die Firewallrichtlinie ermöglicht die erforderlichen AKS-Endpunkte und blockiert standardmäßig alle anderen ausgehenden Verbindungen.

Ermöglichen des Entwicklerzugriffs auf den API-Server

Wenn Sie im vorherigen Schritt autorisierte IP-Adressbereiche für Ihren Cluster verwendet haben, müssen Sie die IP-Adressen Ihrer Entwicklertools der AKS-Clusterliste mit den genehmigten IP-Adressbereichen hinzufügen, d. h. Sie greifen von dort aus auf den API-Server zu. Sie können auch einen Jumpbox mit den erforderlichen Tools in einem separaten Subnetz im VNet der Firewall konfigurieren.

  1. Rufen Sie Ihre IP-Adresse mit dem folgenden Befehl ab:

    CURRENT_IP=$(dig @resolver1.opendns.com ANY myip.opendns.com +short)
    
  2. Fügen Sie die IP-Adresse mithilfe des az aks update Befehls zu den genehmigten Bereichen hinzu. Die --api-server-authorized-ip-ranges Option ersetzt die aktuelle Liste. Schließen Sie also die öffentliche IP-Adresse der Firewall ein, wenn Sie die Entwickler-IP-Adresse hinzufügen.

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

Herstellen einer Verbindung mit dem AKS-Cluster

  • Konfigurieren Sie kubectl, um mithilfe des Befehls az aks get-credentials eine Verbindung mit Ihrem AKS-Cluster herzustellen.

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

Einen öffentlichen Dienst auf AKS einrichten

Nun können Sie damit beginnen, Dienste verfügbar zu machen und Anwendungen in diesem Cluster bereitzustellen. In diesem Beispiel wird ein öffentlicher Dienst verfügbar gemacht, sie können aber auch einen internen Dienst mithilfe eines internen Lastenausgleichs verfügbar machen.

DNAT für öffentlichen Dienst

Das Diagramm zeigt eingehenden öffentlichen Datenverkehr, der an die öffentliche IP-Adresse der Firewall gelangt, durch die DNAT-Regel umgesetzt und an die Frontend-IP-Adresse des öffentlichen Lastenausgleichs für den AKS-Dienst weitergeleitet wird.

  1. Überprüfen Sie das AKS Store Demo-Schnellstartmanifest , um die bereitgestellten Komponenten zu verstehen.

  2. Stellen Sie den Dienst mithilfe des kubectl apply Befehls bereit.

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

Öffentliche Frontend-IP-Adresse des Lastenausgleichs abrufen

  1. Rufen Sie die öffentliche Front-End-IP-Adresse ab, die dem Lastenausgleichsmodul zugewiesen ist, indem Sie den kubectl get services Befehl verwenden.

    kubectl get services
    

    Die IP-Adresse sollte in der EXTERNAL-IP Spalte aufgeführt werden, wie in der folgenden Beispielausgabe gezeigt:

    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. Speichern Sie die Frontend-IP-Adresse des öffentlichen Load Balancers mit dem Befehl kubectl get svc store-front.

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

Erstellen einer DNAT-Regel in Azure Firewall

Wichtig

Wenn Sie Azure Firewall verwenden, um den Ausgehenden Datenverkehr einzuschränken und einen UDR zu erstellen, um den gesamten Ausgangsverkehr zu erzwingen, stellen Sie sicher, dass Sie eine entsprechende DNAT-Regel in Azure Firewall erstellen, um den Eingehenden Datenverkehr korrekt zuzulassen. Durch die Verwendung von Azure Firewall mit einer UDR wird das Setup für eingehenden Datenverkehr aufgrund von asymmetrischem Routing unterbrochen. Das Problem tritt auf, wenn das AKS-Subnetz über eine Standardroute verfügt, die zur privaten IP-Adresse der Firewall führt, Sie aber einen öffentlichen Lastenausgleich verwenden – eingehend oder Kubernetes-Dienst vom Typ loadBalancer. In diesem Fall wird der eingehende Load Balancer-Datenverkehr über die öffentliche IP-Adresse empfangen, während der Rückpfad über die private IP-Adresse der Firewall verläuft. Da Azure Firewall zustandsbehaftet ist, verwirft sie das zurückkehrende Paket, weil Azure Firewall keine Kenntnis von einer bestehenden Sitzung hat. Informationen zur Integration von Azure Firewall in ihren Eingangs- oder Service Dienst-Load Balancer finden Sie unter Integrieren von Azure Firewall mit Azure Load Balancer Standard.

Um eingehende Konnektivität zu konfigurieren, erstellen Sie eine DNAT-Regel für Azure Firewall. Um die Konnektivität zu Ihrem Cluster zu testen, übersetzt die Regel die öffentliche Frontend-IP-Adresse der Firewall in die öffentliche Frontend-IP-Adresse des Lastenausgleichs-Frontend, die unter $SERVICE_IP gespeichert ist. Sie können die Zieladresse anpassen. Der übersetzte Port muss der verfügbar gemachte Port für Ihren Kubernetes-Dienst sein. Konfigurieren Sie für eine Topologie, die stattdessen eine private Frontend-IP-Adresse verwendet, einen internen Lastenausgleichsdienst, und verwenden Sie dessen private IP-Adresse als umgesetzte Adresse.

  • Fügen Sie die NAT-Regel mithilfe des az network firewall nat-rule create-Befehls hinzu.

    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
    

Überprüfen der Konnektivität

  • Navigieren Sie in einem Browser zur IP-Adresse des Azure Firewall-Front-Ends, um die Konnektivität zu überprüfen. Die AKS Store-App sollte angezeigt werden. In diesem Beispiel lautete 52.253.228.132die öffentliche IP-Adresse der Firewall:

    Screenshot: Azure-Storefront-App, die in einem lokalen Browser geöffnet ist

    Auf dieser Seite können Sie Produkte anzeigen und ihrem Warenkorb hinzufügen und dann eine Bestellung aufgeben.

Bereinigen von Ressourcen

Wenn Sie die in diesem Artikel erstellten Ressourcen nicht mehr benötigen, können Sie sie löschen, um zukünftige Kosten zu vermeiden.

  • Löschen Sie die AKS-Ressourcengruppe mithilfe des az group delete Befehls.

    az group delete --name $RESOURCE_GROUP
    

Azure Firewall wird stündlich abgerechnet, unabhängig davon, ob der Datenverkehr aktiv verarbeitet wird oder nicht, zuzüglich Gebühren für die Datenverarbeitung. Lassen Sie die Firewall (oder den Rest dieses Beispiels) nicht länger laufen als nötig.

Warning

Wenn Sie mit vorhandenen oder Produktionsressourcen im selben Arbeitsverzeichnis arbeiten, überprüfen Sie den Ausführungsplan sorgfältig, bevor Sie sie zerstören. Beim Ausführen terraform destroy wird jede Ressource entfernt, die in der Terraform-Zustandsdatei für diese Konfiguration nachverfolgt wird, einschließlich des virtuellen Netzwerks, der Subnetze, Azure Firewall, der Firewallrichtlinie und -regeln, der Routentabelle, des AKS-Clusters und der Rollenzuweisung. Vermeiden Sie, es auf gemeinsam genutzter Infrastruktur auszuführen, es sei denn, Sie sind sicher, dass es gefahrlos entfernt werden kann.

Führen Sie terraform plan mit der -destroy Kennzeichnung aus, um zu überprüfen, was Terraform entfernt.

terraform plan -destroy -out main.destroy.tfplan

Führen Sie die Ausführung terraform apply aus, um den Zerstörungsplan anzuwenden und alle von diesem Beispiel erstellten Ressourcen zu entfernen, einschließlich Azure Firewall.

terraform apply main.destroy.tfplan