在 Azure Kubernetes Service (AKS) 中使用網路原則來保護 Pod 之間的流量

當您在 Kubernetes 中執行新式微服務架構的應用程式時,您通常想要控制哪些元件可以彼此通訊。 最低權限原則應該套用至流量可以在 Azure Kubernetes Service (AKS) 叢集中的 Pod 之間流動的方式。 假設您可能想要封鎖直接流向後端應用程式的流量。 Kubernetes 中的「網路原則」功能可讓您針對叢集中 Pod 之間的輸入和輸出流量定義規則。

本文示範如何安裝網路原則引擎,並建立 Kubernetes 網路原則來控制 AKS 中 Pod 之間的流量流程。 網路原則只應用於 AKS 中的 Linux 型節點和 Pod。

開始之前

您必須安裝並設定 Azure CLI 版本 2.0.61 或更新版本。 執行 az --version 以尋找版本。 如果您需要安裝或升級,請參閱安裝 Azure CLI

提示

如果您已在預覽期間使用網路原則功能,我們建議您建立新的叢集

如果您想要繼續使用在預覽期間使用網路原則的現有測試叢集,請將您的叢集升級至最新 GA 版本的新 Kubernetes 版本,然後部署下列 YAML 資訊清單,以修正損毀的計量伺服器和 Kubernetes 儀表板。 只有使用 Calico 網路原則引擎的叢集才需要此修正。

作為安全性最佳做法,檢閱此 YAML 資訊清單的內容,了解部署到 AKS 叢集的內容。

kubectl delete -f https://raw.githubusercontent.com/Azure/aks-engine/master/docs/topics/calico-3.3.1-cleanup-after-upgrade.yaml

網路原則概觀

根據預設,AKS 叢集中的所有 Pod 都可以無限制地傳送及接收流量。 為了提升安全性,您可以定義可控制流量流程的規則。 例如,後端應用程式通常只會對必要的前端服務公開。 或者,只有連線到資料庫元件的應用層才能存取它們。

網路原則是一種 Kubernetes 規格,其定義 Pod 之間通訊的存取原則。 使用網路原則時,您可以定義一組已排序的規則來傳送和接收流量,並將其套用至符合一或多個標籤選取器的 Pod 集合。

這些網路原則規則會定義為 YAML 資訊清單。 網路原則可納入更廣泛的資訊清單中,而該清單也會建立部署或服務。

AKS 中的網路原則選項

Azure 提供兩種方式來實作網路原則。 當您建立 AKS 叢集時,可以選擇網路原則選項。 建立叢集之後,就無法變更原則選項:

  • Azure 本身的實作,稱為 Azure 網路原則
  • Calico 網路原則,這是 Tigera 創建的開放原始碼網路和網路安全性解決方案。

這兩個部署都會使用 Linux IPTables 來強制執行指定的原則。 這些原則會轉譯成一組允許和不允許的 IP 配對。 這些配對接著會透過程式設計為 IPTable 篩選規則。

Azure 與 Calico 原則之間的差異及其功能

功能 Azure Calico
支援的平台 Linux Linux、Windows Server 2019 (preview)
支援的網路功能選項 Azure CNI Azure CNI (Windows Server 2019 和 Linux) 和 kubenet (Linux)
符合 Kubernetes 規格 支援所有原則類型 支援所有原則類型
其他功能 None 由全域網路原則、全域網路集和主機端點組成的延伸原則模型。 如需使用 calicoctl CLI 管理這些擴充功能的詳細資訊,請參閱 calicoctl 使用者參考
支援 受到 Azure 支援和工程團隊支援 Azure 社群支援。 如需其他付費支援的詳細資訊,請參閱 Project Calico 支援選項
記錄 在 IPTables 中新增/刪除的規則會記錄在每一部主機的 /var/log/azure-npm.log 如需詳細資訊,請參閱 Calico 元件記錄

建立 AKS 叢集並啟用網路原則

若要查看運作中的網路原則,讓我們建立而後展開可定義流量流程的原則:

  • 拒絕流向 Pod 的所有流量。
  • 允許以 Pod 標籤為基礎的流量。
  • 允許以命名空間為基礎的流量。

首先,讓我們建立支援網路原則的 AKS 叢集。

重要

只有在建立叢集時,才可以啟用網路原則功能。 您無法在現有的 AKS 叢集上啟用網路原則。

若要使用 Azure 網路原則,您必須使用 Azure CNI 外掛程式並定義自己的虛擬網路和子網路。 如需有關如何規劃出必要子網路範圍的詳細資訊,請參閱設定進階網路。 Calico 網路原則可以與這個相同的 Azure CNI 外掛程式或與 Kubenet CNI 外掛程式搭配使用。

下列範例指令碼:

  • 建立虛擬網路和子網路。
  • 建立 Azure Active Directory (Azure AD) 服務主體,以便搭配 AKS 叢集使用。
  • 針對虛擬網路上的 AKS 叢集服務主體指派「參與者」權限。
  • 在定義的虛擬網路中建立 AKS 叢集,然後啟用網路原則。
    • 即會使用 Azure 網路原則選項。 若要改用 Calico 做為網路原則選項,請使用 --network-policy calico 參數。 注意:Calico 可以搭配 --network-plugin azure--network-plugin kubenet 使用。

請注意,不是使用服務主體,而是您可以使用受控識別來取得權限。 如需詳細資訊,請參閱使用受控識別

提供您自己的安全 SP_PASSWORD。 您可以取代 RESOURCE_GROUP_NAMECLUSTER_NAME 變數:

RESOURCE_GROUP_NAME=myResourceGroup-NP
CLUSTER_NAME=myAKSCluster
LOCATION=canadaeast

# Create a resource group
az group create --name $RESOURCE_GROUP_NAME --location $LOCATION

# Create a virtual network and subnet
az network vnet create \
    --resource-group $RESOURCE_GROUP_NAME \
    --name myVnet \
    --address-prefixes 10.0.0.0/8 \
    --subnet-name myAKSSubnet \
    --subnet-prefix 10.240.0.0/16

# Create a service principal and read in the application ID
SP=$(az ad sp create-for-rbac --role Contributor --output json)
SP_ID=$(echo $SP | jq -r .appId)
SP_PASSWORD=$(echo $SP | jq -r .password)

# Wait 15 seconds to make sure that service principal has propagated
echo "Waiting for service principal to propagate..."
sleep 15

# Get the virtual network resource ID
VNET_ID=$(az network vnet show --resource-group $RESOURCE_GROUP_NAME --name myVnet --query id -o tsv)

# Assign the service principal Contributor permissions to the virtual network resource
az role assignment create --assignee $SP_ID --scope $VNET_ID --role Contributor

# Get the virtual network subnet resource ID
SUBNET_ID=$(az network vnet subnet show --resource-group $RESOURCE_GROUP_NAME --vnet-name myVnet --name myAKSSubnet --query id -o tsv)

建立適用于 Azure 網路原則的 AKS 叢集

建立 AKS 叢集,並為網路外掛程式和網路原則指定虛擬網路、服務主體資訊和 azure

az aks create \
    --resource-group $RESOURCE_GROUP_NAME \
    --name $CLUSTER_NAME \
    --node-count 1 \
    --generate-ssh-keys \
    --service-cidr 10.0.0.0/16 \
    --dns-service-ip 10.0.0.10 \
    --docker-bridge-address 172.17.0.1/16 \
    --vnet-subnet-id $SUBNET_ID \
    --service-principal $SP_ID \
    --client-secret $SP_PASSWORD \
    --network-plugin azure \
    --network-policy azure

建立叢集需要幾分鐘的時間。 當叢集備妥時,請使用 az aks get-credentials 命令,設定 kubectl 以連線到 Kubernetes 叢集。 此命令會下載憑證並設定 Kubernetes CLI 以供使用:

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

建立適用于 Calico 網路原則的 AKS 叢集

建立 AKS 叢集並指定虛擬網路、服務主體資訊、 azure 作為網路外掛程式,以及網路原則的 calico 。 使用calico作為網路原則可在 Linux 和Windows節點集區上啟用 Calico 網路功能。

如果您打算將Windows節點集區新增至叢集,請包含 windows-admin-username 符合Windows 伺服器密碼需求的windows-admin-password 參數。 若要搭配Windows節點集區使用 Calico,您也需要註冊 Microsoft.ContainerService/EnableAKSWindowsCalico

EnableAKSWindowsCalico使用az feature register 命令註冊功能旗標,如下列範例所示:

az feature register --namespace "Microsoft.ContainerService" --name "EnableAKSWindowsCalico"

您可以使用 az feature list 命令檢查註冊狀態:

az feature list -o table --query "[?contains(name, 'Microsoft.ContainerService/EnableAKSWindowsCalico')].{Name:name,State:properties.state}"

準備就緒時,請使用az provider register命令重新整理Microsoft.ContainerService資源提供者的註冊:

az provider register --namespace Microsoft.ContainerService

重要

目前,使用 Calico 網路原則搭配 Windows 節點,可在新的叢集上使用 Kubernetes 1.20 版或更新版本搭配 Calico 3.17.2,而且需要使用 Azure CNI 網路。 Windows已啟用 Calico 之 AKS 叢集上的節點,預設也會啟用直接伺服器傳回 (DSR)

若叢集只有執行 Kubernetes 1.20 且舊版 Calico 的 Linux 節點集區,Calico 版本會自動升級為 3.17.2。

具有Windows節點的 Calico 網路原則目前為預覽狀態。

重要

AKS 預覽功能可依自助式、加入宣告提供。 預覽提供「原狀」和「可用」,而且會從服務等級協定和有限擔保中排除。 客戶支援會以最佳方式涵蓋 AKS 預覽。 因此,這些功能不適合用于生產環境。 如需詳細資訊,請參閱下列支援文章:

建立使用者名稱,以作為叢集上Windows伺服器容器的系統管理員認證。 下列命令會提示您輸入使用者名稱,並將它設定為WINDOWS_USERNAME在稍後的命令中使用, (請記住,本文中的命令會輸入 BASH 殼層) 。

echo "Please enter the username to use as administrator credentials for Windows Server containers on your cluster: " && read WINDOWS_USERNAME
az aks create \
    --resource-group $RESOURCE_GROUP_NAME \
    --name $CLUSTER_NAME \
    --node-count 1 \
    --generate-ssh-keys \
    --service-cidr 10.0.0.0/16 \
    --dns-service-ip 10.0.0.10 \
    --docker-bridge-address 172.17.0.1/16 \
    --vnet-subnet-id $SUBNET_ID \
    --service-principal $SP_ID \
    --client-secret $SP_PASSWORD \
    --windows-admin-username $WINDOWS_USERNAME \
    --vm-set-type VirtualMachineScaleSets \
    --kubernetes-version 1.20.2 \
    --network-plugin azure \
    --network-policy calico

建立叢集需要幾分鐘的時間。 根據預設,您的叢集只會使用 Linux 節點集區來建立。 如果您想要使用Windows節點集區,您可以新增節點集區。 例如:

az aks nodepool add \
    --resource-group $RESOURCE_GROUP_NAME \
    --cluster-name $CLUSTER_NAME \
    --os-type Windows \
    --name npwin \
    --node-count 1

當叢集備妥時,請使用 az aks get-credentials 命令,設定 kubectl 以連線到 Kubernetes 叢集。 此命令會下載憑證並設定 Kubernetes CLI 以供使用:

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

拒絕流向 Pod 的所有輸入流量

在您定義規則以允許特定網路流量之前,先建立網路原則以拒絕所有流量。 此原則可讓您開始建立只允許流量的允許清單。 您可以也清楚地看到套用網路原則時會捨棄該流量。

針對範例應用程式環境和流量規則,讓我們先建立名為 development 的命名空間以執行範例 Pod:

kubectl create namespace development
kubectl label namespace/development purpose=development

建立可執行 NGINX 的範例後端 Pod。 這個後端 Pod 可用於模擬範例後端 Web 應用程式。 在 development 命名空間中建立這個 Pod,然後開啟連接埠 80 來處理 Web 流量。 替 Pod 加上 app=webapp,role=backend 標籤,以便我們在下一節中使用網路原則以其作為目標:

kubectl run backend --image=mcr.microsoft.com/oss/nginx/nginx:1.15.5-alpine --labels app=webapp,role=backend --namespace development --expose --port 80

建立另一個 Pod,然後連結終端機工作階段,來測試您是否可以成功觸達預設 NGINX 網頁:

kubectl run --rm -it --image=mcr.microsoft.com/aks/fundamental/base-ubuntu:v0.0.11 network-policy --namespace development

在殼層提示字元,使用 wget 來確認您可以存取預設 NGINX 網頁:

wget -qO- http://backend

下列範例輸出會顯示傳回的預設 NGINX 網頁:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
[...]

結束連結的終端機工作階段。 測試 Pod 會自動刪除。

exit

建立並套用網路原則

既然已確認您可以在範例後端 Pod 上使用基本 NGINX 網頁,請建立網路原則以拒絕所有流量。 建立名為 backend-policy.yaml 的檔案,並貼上下列 YAML 資訊清單。 此資訊清單會使用 podSelector 將原則連結至具有 app:webapp,role:backend 標籤的 Pod,例如您的範例 NGINX Pod。 ingress 之下未定義任何規則,因此會拒絕流向 Pod 的所有輸入流量:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: backend-policy
  namespace: development
spec:
  podSelector:
    matchLabels:
      app: webapp
      role: backend
  ingress: []

移至 https://shell.azure.com,以在瀏覽器中開啟 Azure Cloud Shell。

使用 kubectl apply 命令來套用網路原則,並指定 YAML 資訊清單的名稱:

kubectl apply -f backend-policy.yaml

測試網路原則

讓我們看看您是否可以在後端 Pod 上再次使用 NGINX 網頁。 建立另一個測試 Pod 並連結終端機工作階段:

kubectl run --rm -it --image=mcr.microsoft.com/aks/fundamental/base-ubuntu:v0.0.11 network-policy --namespace development

在殼層提示字元,使用 wget 來查看您是否可以存取預設 NGINX 網頁。 此時,將逾時值設定為 2 秒。 網路原則現在會封鎖所有輸入流量,因此無法載入此頁面,如下列範例所示:

wget -O- --timeout=2 --tries=1 http://backend
wget: download timed out

結束連結的終端機工作階段。 測試 Pod 會自動刪除。

exit

允許以 Pod 標籤為基礎的輸入流量

在上一節中,已排程後端 NGINX Pod,並建立了網路原則以拒絕所有流量。 讓我們建立前端 Pod 並更新網路原則,以允許來自前端 Pod 的流量。

更新網路原則,以允許來自任何命名空間中具有 app:webapp,role:frontend 標籤的 Pod 流量。 編輯先前的 backend-policy.yaml 檔案,然後新增 matchLabels 輸入規則,讓您的資訊清單看起來如下列範例所示:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: backend-policy
  namespace: development
spec:
  podSelector:
    matchLabels:
      app: webapp
      role: backend
  ingress:
  - from:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          app: webapp
          role: frontend

注意

此網路原則針對輸入規則使用 namespaceSelectorpodSelector 元素。 YAML 語法對於附加輸入規則非常重要。 在此範例中,兩個元素都必須與要套用的輸入規則相符。 在 Kubernetes 1.12 之前的版本,可能無法如您所預期般正確地轉譯這些元素並限制網路流量。 如需此行為的詳細資訊,請參閱選取器的來回行為

使用 kubectl apply 命令來套用已更新的網路原則,並指定 YAML 資訊清單的名稱:

kubectl apply -f backend-policy.yaml

排程標籤為 app=webapp,role=frontend 的 Pod 並連結終端機工作階段:

kubectl run --rm -it frontend --image=mcr.microsoft.com/aks/fundamental/base-ubuntu:v0.0.11 --labels app=webapp,role=frontend --namespace development

在殼層提示字元,使用 wget 來查看您是否可以存取預設 NGINX 網頁:

wget -qO- http://backend

因為輸入規則允許具有 app: webapp,role: frontend 標籤的 Pod 流量,所以允許來自前端 Pod 的流量。 下列範例輸出會顯示傳回的預設 NGINX 網頁:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
[...]

結束連結的終端機工作階段。 Pod 會自動刪除。

exit

測試沒有相符標籤的 Pod

網路原則允許來自標籤為 app: webapp,role: frontend 的 Pod 流量,但應該拒絕所有其他流量。 讓我們測試以查看另一個沒有這些標籤的 Pod 是否可以存取後端 NGINX Pod。 建立另一個測試 Pod 並連結終端機工作階段:

kubectl run --rm -it --image=mcr.microsoft.com/aks/fundamental/base-ubuntu:v0.0.11 network-policy --namespace development

在殼層提示字元,使用 wget 來查看您是否可以存取預設 NGINX 網頁。 網路原則會封鎖輸入流量,因此無法載入此頁面,如下列範例所示:

wget -O- --timeout=2 --tries=1 http://backend
wget: download timed out

結束連結的終端機工作階段。 測試 Pod 會自動刪除。

exit

只允許來自已定義命名空間的流量

在上述範例中,您建立了可拒絕所有流量的網路原則,然後更新了此原則,以允許來自具有特定標籤的 Pod 流量。 另一個常見需求是讓流量僅限於指定的命名空間內。 如果先前的範例是針對 development 命名空間中的流量,請建立網路原則,以防止來自另一個命名空間 (例如 production) 的流量觸達 Pod。

首先,建立新的命名空間,以模擬生產命名空間:

kubectl create namespace production
kubectl label namespace/production purpose=production

在標籤為 app=webapp,role=frontendproduction 命名空間中排程測試 Pod。 連結終端機工作階段:

kubectl run --rm -it frontend --image=mcr.microsoft.com/aks/fundamental/base-ubuntu:v0.0.11 --labels app=webapp,role=frontend --namespace production

在殼層提示字元,使用 wget 來確認您可以存取預設 NGINX 網頁:

wget -qO- http://backend.development

因為 Pod 的標籤符合網路原則目前許可的內容,所以允許流量。 網路原則不會查看命名空間,只會查看 Pod 標籤。 下列範例輸出會顯示傳回的預設 NGINX 網頁:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
[...]

結束連結的終端機工作階段。 測試 Pod 會自動刪除。

exit

更新網路原則

讓我們更新輸入規則 namespaceSelector 區段,只允許來自 development 命名空間的流量。 編輯 backend-policy.yaml 資訊清單檔,如下列範例所示:

kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
  name: backend-policy
  namespace: development
spec:
  podSelector:
    matchLabels:
      app: webapp
      role: backend
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          purpose: development
      podSelector:
        matchLabels:
          app: webapp
          role: frontend

在更複雜的範例中,您可以定義多個輸入規則,例如依序使用 namespaceSelectorpodSelector

使用 kubectl apply 命令來套用已更新的網路原則,並指定 YAML 資訊清單的名稱:

kubectl apply -f backend-policy.yaml

測試已更新的網路原則

production 命名空間中排程另一個 Pod,然後連結終端機工作階段:

kubectl run --rm -it frontend --image=mcr.microsoft.com/aks/fundamental/base-ubuntu:v0.0.11 --labels app=webapp,role=frontend --namespace production

在殼層提示字元,使用 wget 來查看網路原則現在是否會拒絕流量:

wget -O- --timeout=2 --tries=1 http://backend.development
wget: download timed out

結束測試 Pod:

exit

拒絕了來自 production 命名空間的流量,回到 development 命名空間中排程測試 Pod,然後連結終端機工作階段:

kubectl run --rm -it frontend --image=mcr.microsoft.com/aks/fundamental/base-ubuntu:v0.0.11 --labels app=webapp,role=frontend --namespace development

在殼層提示字元,使用 wget 來查看網路原則是否允許流量:

wget -qO- http://backend

因為 Pod 已排定在符合網路原則許可內容的命名空間中,所以允許流量。 下列範例輸出會顯示傳回的預設 NGINX 網頁:

<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
[...]

結束連結的終端機工作階段。 測試 Pod 會自動刪除。

exit

清除資源

在本文中,我們已建立兩個命名空間並套用網路原則。 若要清除這些資源,請使用 kubectl delete 命令並指定資源名稱:

kubectl delete namespace production
kubectl delete namespace development

後續步驟

如需網路資源的詳細資訊,請參閱 Azure Kubernetes Service (AKS) 中應用程式的網路概念

若要深入了解原則,請參閱 Kubernetes 網路原則