使用 Azure Kubernetes 服务 (AKS) 中的 Azure 防火墙限制网络流量

本文介绍如何使用 AKS 群集的出站网络和完全限定的域名(FQDN)规则 来控制使用 Azure 防火墙的出口流量。 为了简化此配置,Azure 防火墙提供了一个 Azure Kubernetes 服务 (AzureKubernetesService) FQDN 标记,用于限制来自 AKS 群集的出站流量。

防火墙前端 IP 要求

  • 生产起点:在Azure 防火墙上规划至少 20 个前端 IP,并调整工作负荷的配置大小,以避免源网络地址转换(SNAT)端口耗尽。
  • 高流量群集:如果群集创建多个到同一目标的出站连接,则可能需要更多前端 IP 或Azure 防火墙与 NAT 网关集成以提高 SNAT 容量。
  • API 服务器保护:将防火墙的公共前端 IP 添加到 API 服务器授权 IP 范围 ,以提高安全性
  • 开发人员访问:在使用授权 IP 范围时,若要在防火墙的虚拟网络 (VNet) 中使用跳板机,或将开发者终端 IP 地址添加到授权范围内。

本指南适用于本文中所述的配置过程。

注意

FQDN 标签包含 AKS 集群的出站网络和 FQDN 规则中列出的所有 FQDN,并会自动更新。

体系结构概述

下图演示了使用 Azure 防火墙限制出口流量的 AKS 群集的体系结构:

锁定的拓扑

该图显示了位于专用子网中的 AKS 代理节点如何使用 UDR 通过 Azure 防火墙路由出站流量。 对于入站流量,DNAT 规则会将防火墙公共 IP 地址转换为公共负载均衡器前端 IP 地址。

此体系结构的关键组件包括:

  • 公共入口被迫流经防火墙筛选器
    • AKS 代理节点在专用子网中隔离。
    • Azure 防火墙 部署在其自己的子网中。
    • DNAT 规则将防火墙公共 IP 转换为负载均衡器前端 IP。
  • 出站请求从代理节点发往 Azure 防火墙专用 IP,并使用用户定义的路由 (UDR)
    • 来自 AKS 代理节点的请求遵循部署在 AKS 群集所在子网中的 UDR。
    • Azure 防火墙从公用 IP 前端外发虚拟网络 (VNet)。
    • 访问公共互联网或其他 Azure 服务的流量通过防火墙前端 IP 地址进出。
    • 可以使用 API 服务器授权 IP 范围(包括防火墙公共前端 IP 地址)保护对 AKS 控制平面的访问。
  • 内部流量

配置环境变量

下表列出了本文中使用的环境变量。 在继续作之前,请在 shell 中设置这些变量,或修改命令以使用自己的值。

Variable Description 示例值
RESOURCE_GROUP 资源组的名称 aks-egress-rg
LOCATION Azure 资源所在的区域 eastus
CLUSTER_NAME AKS 群集的名称 aks-egress
VNET_NAME 虚拟网络的名称 aks-egress-vnet
AKS_SUBNET_NAME AKS 的子网名称 aks-subnet
FW_SUBNET_NAME Azure 防火墙子网的名称 AzureFirewallSubnet
FW_NAME Azure 防火墙的名称 aks-egress-fw
FW_PUBLICIP_NAME Azure 防火墙的公共 IP 的名称 aks-egress-fwpublicip
FW_IPCONFIG_NAME Azure 防火墙的 IP 配置的名称 aks-egress-fwconfig
FW_ROUTE_TABLE_NAME Azure 防火墙的路由表的名称 aks-egress-fwrt
FW_ROUTE_NAME_1 Azure 防火墙路由的名称 aks-egress-fwrn
FW_ROUTE_NAME_2 Azure 防火墙的 Internet 路由的名称 aks-egress-fwrn-internet

创建资源组

  • 使用 az group create 命令创建资源组。

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

创建包含多个子网的虚拟网络

使用两个不同的子网预配虚拟网络:一个用于群集,另一个用于防火墙。 还可以选择为内部服务入口创建一个。 下图演示了部署任何资源之前空的网络拓扑:

空网络拓扑

该图显示了在部署资源之前,VNet 中为 AKS 和 Azure 防火墙分别设置了独立的子网。

  1. 使用 az network vnet create 命令创建 VNet。

    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. 使用 az network vnet subnet create 命令为 Azure 防火墙创建子网。

    # 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
    

为 Azure 防火墙创建公共 IP

  • 使用 az network public-ip create 命令创建标准 SKU 公共 IP 资源。 将此资源用作Azure 防火墙的前端 IP 地址。 本文中的单 IP 配置用于测试。 对于生产,请使用 防火墙前端 IP 要求 为工作负荷规划 SNAT 容量。

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

安装 Azure 防火墙 CLI 扩展

创建 Azure 防火墙并启用 DNS 代理

注意

有关高流量方案,请参阅 防火墙前端 IP 要求 部分。

有关如何创建具有多个 IP 的 Azure 防火墙的详细信息,请参阅使用 Bicep 创建具有多个公共 IP 地址的 Azure 防火墙

防火墙和 UDR

此图显示了在其子网中部署Azure 防火墙,UDR 将来自 AKS 子网的流量定向到防火墙专用 IP 地址。

  • 使用az network firewall create命令并将--enable-dns-proxy设置为true来创建 Azure 防火墙并启用 DNS 代理。

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

    将公共 IP 地址设置为 Azure 防火墙可能需要几分钟时间。 准备就绪后,可以将 IP 地址分配给防火墙前端。

    注意

    若要在网络规则上使用 FQDN,需要启用 DNS 代理。 启用 DNS 代理后,防火墙侦听端口 53 并将 DNS 请求转发到指定的 DNS 服务器。 此设置允许防火墙自动转换 FQDN。

为 Azure 防火墙创建 IP 配置

  • 使用 az network firewall ip-config create 命令创建 Azure 防火墙 IP 配置。

    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
    

获取 Azure 防火墙 IP 地址

  • 使用以下命令保存公共和专用防火墙前端 IP 地址以供稍后进行配置:

    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)
    

    注意

    有关 API 服务器安全性,请参阅 防火墙前端 IP 要求 部分。

将虚拟网络配置为使用 Azure 防火墙 作为其 DNS 服务器

  • 使用命令将虚拟网络配置为使用Azure 防火墙专用 IP 地址作为其 DNS 服务器az network vnet update。 此配置可确保 AKS 节点和 Azure 防火墙以一致的方式解析网络规则中的目标 FQDN。

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

AKS 通过 Azure 防火墙出站流量的 UDR 要求

Azure 自动路由 Azure 子网、VNet 和本地网络之间的流量。 若要修改默认路由,请创建具有以下要求的路由表:

Category 要求 值或说明
路由参数 目的地 0.0.0.0/0 (所有流量)
路由参数 下一个跃点类型 网络虚拟设备 (NVA)
路由参数 下一跳 IP Azure 防火墙专用 IP 地址
路由参数 关联 每个子网一个路由表(允许 个或 一个
UDR 约束 默认 Internet 路由 0.0.0.0/0 已存在,但需要 SNAT 的公共 IP 地址。
UDR 约束 下一跳 路由必须指向网关或 NVA,而非直接指向互联网。
UDR 约束 AKS 验证 AKS 验证路由配置并阻止直接 Internet 路由。
UDR 约束 路由表限制 每个子网最多支持 一个 关联的路由表。
出站类型影响 UDR (userDefinedRouting 不会为出站请求创建任何负载均衡器公共 IP 地址。
出站类型影响 负载均衡器公共 IP 地址 仅为使用 LoadBalancer 服务的入站请求创建。
出站类型影响 SNAT 配置 需要正确配置的公共 IP 地址才能建立出站连接。

有关详细信息,请参阅 Azure 负载均衡器的出站规则

创建包含 Azure 防火墙跃点的路由

  1. 使用 az network route-table create 命令创建空路由表。 路由表将 Azure 防火墙定义为下一跃点。 每个子网可以有一个与其关联的路由表。

    az network route-table create --resource-group $RESOURCE_GROUP --location $LOCATION --name $FW_ROUTE_TABLE_NAME
    
  2. 使用 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
    

有关如何替代 Azure 的默认系统路由或向子网路由表添加更多路由的信息,请参阅 虚拟网络路由表文档

通过 Azure 防火墙 的 AKS 出口所需的出站规则

注意

对于需要与 API 服务器通信的kube-systemgatekeeper-system命名空间外的应用程序,除了为fqdn-tagAzureKubernetesService添加应用程序规则之外,还需要添加一条额外的网络规则,以允许与 API 服务器 IP 的 443 端口进行 TCP 通信。

以下网络规则允许本文中使用的出口流量:

  • 第一和第二个网络规则允许从GitHub容器注册表(ghcr.io)和Docker Hub(docker.io)拉取容器。

使用 konnectivity 代理的 AKS 群集(包括本文中创建的群集)不需要对 TCP 端口 9000 或 UDP 端口 1194 进行出站访问。 2021 年 3 月之后预配的节点也不需要对 UDP 端口 123 进行出站 NTP 访问。 有关其他群集配置和Azure云所需的规则,请参阅 AKS 群集的出站网络和 FQDN 规则

在 Azure 防火墙上创建网络规则

  • 使用以下命令 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'
    

在 Azure 防火墙上创建应用程序规则

  • 使用 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
    

若要详细了解 Azure 防火墙,请参阅 Azure 防火墙文档

将路由表关联到 AKS

若要将群集与防火墙相关联,群集子网的专用子网必须引用路由表。

  • 使用 az network vnet subnet update 命令将路由表关联到 AKS。

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

部署遵循您的出站规则的 AKS 集群

现在可以将 AKS 群集部署到现有的 VNet 中。 使用 userDefinedRouting 出站类型,确保所有出站流量都被强制通过防火墙,并且不存在其他出口路径。 还可以使用 loadBalancer 出站类型

aks-deploy

此图显示了部署在现有 VNet 中的 AKS 群集,并使用 UDR 通过 Azure 防火墙 路由出站流量。

  • 使用以下命令为目标子网的子网 ID 设置环境变量:

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

您需要定义出站类型以使用子网上已存在的 UDR。 此配置使 AKS 能够跳过负载均衡器的设置和 IP 预配。

提示

可以将额外的功能添加到群集部署,例如 专用群集

有关 API 服务器授权 IP 范围的设置和开发人员访问注意事项,请参阅 防火墙前端 IP 要求 部分。


创建具有系统分配的控制平面标识的 AKS 群集

注意

此群集使用系统分配的控制平面标识。 如果未 指定自己的 kubelet 托管标识,AKS 会在节点资源组中创建用户分配的 kubelet 标识。

对于用户定义的路由,系统分配的标识仅支持 CNI 网络插件。

  • 使用 az aks create 命令,通过 CNI 网络插件使用系统分配的托管标识创建 AKS 群集。

    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
    

创建用户分配的托管标识

如果没有用户分配的标识,请按照本部分中的步骤进行操作。 如果已有用户分配的标识,请跳到使用用户分配的标识创建 AKS 群集

  1. 使用 az identity create 命令创建托管标识。

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

    输出应与下面的示例输出类似:

     {
       ...
       "id": "/subscriptions/<subscriptionid>/resourcegroups/aks-egress-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/myIdentity",
       "location": "eastus",
       "name": "myIdentity",
       ...
       "type": "Microsoft.ManagedIdentity/userAssignedIdentities"
     }
    
  2. 使用 az identity create 命令创建 kubelet 托管标识。

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

    输出应与下面的示例输出类似:

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

注意

如果创建自己的 VNet 和路由表,其中资源位于工作器节点资源组之外,CLI 会自动添加角色分配。 如果你使用 ARM 模板或其他方法,则需要使用群集托管标识的主体 ID 来执行角色分配

创建具有用户分配标识的 AKS 群集

  • 使用 az aks create 命令在子网中创建具有现有的用户分配的托管标识的 AKS 群集。 提供控制平面的托管标识的资源 ID 以及 kubelet 标识的资源 ID。

    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
    

使开发人员能够访问 API 服务器

如果在上一步中为群集使用了已授权 IP 范围,则必须将开发人员工具 IP 地址添加到 AKS 群集的已批准 IP 范围列表,以便从该处访问 API 服务器。 还可以在防火墙的 VNet 的单独子网内配置带有所需工具的 Jumpbox。

  1. 使用以下命令检索 IP 地址:

    CURRENT_IP=$(dig @resolver1.opendns.com ANY myip.opendns.com +short)
    
  2. 使用 az aks update 命令将 IP 地址添加到已批准的范围。 此选项 --api-server-authorized-ip-ranges 将替换当前列表,因此在添加开发人员 IP 地址时包括防火墙公共 IP 地址。

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

连接到 AKS 群集

  • 配置 kubectl 以使用 az aks get-credentials 命令连接到 AKS 群集。

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

在 AKS 上部署公共服务

现在可以开始公开服务并将应用程序部署到此群集。 此示例公开了一个公共服务,但你可能还希望使用 内部负载均衡器公开内部服务。

公共服务 DNAT

此图显示了到达防火墙公共 IP 地址的入站公共流量,这些流量由 DNAT 规则转换,并转发到 AKS 服务的公共负载均衡器前端 IP 地址。

  1. 查看 AKS 应用商店演示快速入门 清单,了解已部署的组件。

  2. 使用 kubectl apply 命令部署服务。

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

获取负载均衡器公共前端 IP

  1. 使用 kubectl get services 命令获取分配给负载均衡器的公共前端 IP 地址。

    kubectl get services
    

    IP 地址应列在 EXTERNAL-IP 列中,如以下示例输出中所示:

    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. 使用 kubectl get svc store-front 命令保存公共负载均衡器前端 IP 地址。

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

在 Azure 防火墙上创建 DNAT 规则

重要

使用Azure 防火墙来限制出口流量并创建 UDR 以强制所有出口流量时,请确保在Azure 防火墙中创建适当的 DNAT 规则,以正确允许入口流量。 将 Azure 防火墙与 UDR 配合使用会因非对称路由而导致入站配置失效。 如果 AKS 子网具有一条指向防火墙私有 IP 地址的默认路由,但你使用的是公共负载均衡器(Ingress 或类型为 loadBalancer 的 Kubernetes 服务),就会出现此问题。 在这种情况下,传入的负载均衡流量通过其公共 IP 地址接收,而返回路径则通过防火墙的私有 IP 地址。 由于Azure 防火墙是有状态的,因此它会删除返回的数据包,因为Azure 防火墙不知道已建立的会话。 若要了解如何将 Azure 防火墙与入口或服务负载均衡器集成,请参阅将 Azure 防火墙与 Azure 标准负载均衡器集成

若要配置入站连接,请在Azure 防火墙上创建 DNAT 规则。 为了测试与群集的连接,规则会将防火墙前端公共 IP 地址转换为保存在中的 $SERVICE_IP公共负载均衡器前端 IP 地址。 可以自定义目标地址。 转换的端口必须是 Kubernetes 服务的已公开端口。 对于改用专用前端 IP 地址的拓扑,请配置 内部负载均衡器 ,并使用其专用 IP 地址作为已转换的地址。

  • 使用 az network firewall nat-rule create 命令添加 NAT 规则。

    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
    

验证连接

  • 在浏览器中导航到 Azure 防火墙前端 IP 地址来验证连接。 你应该会看到 AKS 商店应用。 在此示例中,防火墙公共 IP 为 52.253.228.132

    显示 Azure Store Front 应用的屏幕截图,该应用在本地浏览器中打开。

    在此页上,可以查看产品,将其添加到购物车,然后下订单。

清理资源

如果不再需要本文中创建的资源,可以将其删除,以避免产生将来的成本。

  • 使用 az group delete 命令删除 AKS 资源组。

    az group delete --name $RESOURCE_GROUP