網路策略
如果你想在 TCP、UDP 和 SCTP 協議的 IP 地址或埠級別控制流量,那麼你可能會考慮在叢集中的特定應用程式中使用 Kubernetes NetworkPolicies。網路策略是一種以應用程式為中心的構造,它允許你指定一個 Pod 如何被允許透過網路與各種網路“實體”進行通訊(我們在此使用“實體”一詞,以避免與更常見的術語如“端點”和“服務”產生混淆,這些術語在 Kubernetes 中有特定的含義)。網路策略適用於兩端或其中一端為 Pod 的連線,與其它連線無關。
Pod 可以與之通訊的實體透過以下三個識別符號的組合來識別:
- 允許訪問的其他 Pod(例外:Pod 不能阻止對其自身的訪問)
- 允許訪問的名稱空間
- IP 塊(例外:與 Pod 或節點執行所在的節點的流量始終被允許,無論 Pod 或節點的 IP 地址是什麼)
當定義基於 Pod 或名稱空間的 NetworkPolicy 時,你使用 選擇器 來指定與匹配該選擇器的 Pod 之間允許哪些流量。
同時,當建立基於 IP 的 NetworkPolicy 時,我們根據 IP 塊(CIDR 範圍)來定義策略。
先決條件
網路策略由網路外掛實現。要使用網路策略,你必須使用支援 NetworkPolicy 的網路解決方案。建立 NetworkPolicy 資源而沒有實現它的控制器將不起作用。
兩種 Pod 隔離型別
Pod 有兩種隔離型別:出口隔離和入口隔離。它們關係到可以建立哪些連線。“隔離”在這裡不是絕對的,而是指“某些限制適用”。另一種情況,“非隔離 for $direction”,意味著在指定方向上不適用任何限制。這兩種隔離型別(或不隔離)是獨立宣告的,並且都與 Pod 之間的連線相關。
預設情況下,Pod 對出口流量是不隔離的;所有出站連線都允許。如果存在任何 NetworkPolicy 同時選擇了該 Pod 並且其 `policyTypes` 中包含 “Egress”,則該 Pod 對出口流量是隔離的;我們稱這樣的策略適用於該 Pod 的出口流量。當 Pod 對出口流量是隔離的,唯一允許的來自該 Pod 的連線是那些由適用於該 Pod 的某個 NetworkPolicy 的 `egress` 列表所允許的連線。這些被允許連線的回覆流量也將被隱式允許。這些 `egress` 列表的效果是累加的。
預設情況下,Pod 對入站流量是不隔離的;所有入站連線都允許。如果存在任何 NetworkPolicy 同時選擇了該 Pod 並且其 `policyTypes` 中包含 “Ingress”,則該 Pod 對入站流量是隔離的;我們稱這樣的策略適用於該 Pod 的入站流量。當 Pod 對入站流量是隔離的,唯一允許的入站連線是來自該 Pod 的節點以及由適用於該 Pod 的某個 NetworkPolicy 的 `ingress` 列表所允許的連線。這些被允許連線的回覆流量也將被隱式允許。這些 `ingress` 列表的效果是累加的。
網路策略之間不衝突;它們是累加的。如果任何一個或多個策略適用於給定 Pod 的給定方向,則該 Pod 在該方向上允許的連線是所有適用策略允許的連線的並集。因此,評估順序不影響策略結果。
要允許從源 Pod 到目標 Pod 的連線,源 Pod 上的出口策略和目標 Pod 上的入口策略都需要允許該連線。如果任何一方不允許連線,則連線將不會發生。
NetworkPolicy 資源
有關資源的完整定義,請參閱 NetworkPolicy 參考。
一個 NetworkPolicy 示例可能如下所示:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: test-network-policy
namespace: default
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Ingress
- Egress
ingress:
- from:
- ipBlock:
cidr: 172.17.0.0/16
except:
- 172.17.1.0/24
- namespaceSelector:
matchLabels:
project: myproject
- podSelector:
matchLabels:
role: frontend
ports:
- protocol: TCP
port: 6379
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 5978
注意
將此檔案 POST 到叢集的 API 伺服器將不起作用,除非你選擇的網路解決方案支援網路策略。強制欄位:與所有其他 Kubernetes 配置一樣,NetworkPolicy 需要 `apiVersion`、`kind` 和 `metadata` 欄位。有關配置檔案的通用資訊,請參見配置 Pod 使用 ConfigMap 和 物件管理。
spec: NetworkPolicy spec 包含定義給定名稱空間中特定網路策略所需的所有資訊。
podSelector:每個 NetworkPolicy 都包含一個 `podSelector`,用於選擇策略所適用的 Pod 分組。示例策略選擇帶有“role=db”標籤的 Pod。空的 `podSelector` 選擇名稱空間中的所有 Pod。
policyTypes:每個 NetworkPolicy 都包含一個 `policyTypes` 列表,其中可以包含 `Ingress`、`Egress` 或兩者。`policyTypes` 欄位指示給定策略是否適用於選定 Pod 的入站流量、選定 Pod 的出站流量,或兩者。如果在 NetworkPolicy 上未指定 `policyTypes`,則預設情況下 `Ingress` 將始終被設定,並且如果 NetworkPolicy 有任何出口規則,則 `Egress` 將被設定。
ingress: 每個 NetworkPolicy 可以包含一個允許的 `ingress` 規則列表。每條規則允許匹配 `from` 和 `ports` 部分的流量。示例策略包含一條規則,匹配單個埠上的流量,該流量來自以下三個源之一:第一個透過 `ipBlock` 指定,第二個透過 `namespaceSelector` 指定,第三個透過 `podSelector` 指定。
egress: 每個 NetworkPolicy 可以包含一個允許的 `egress` 規則列表。每條規則允許匹配 `to` 和 `ports` 部分的流量。示例策略包含一個規則,它匹配一個埠上的流量,目標是 `10.0.0.0/24` 中的任何目的地。
因此,示例 NetworkPolicy
將 `default` 名稱空間中帶有 `role=db` 標籤的 Pod 隔離起來,用於入站和出站流量(如果它們尚未隔離)
(入站規則) 允許連線到 `default` 名稱空間中所有帶有 `role=db` 標籤的 Pod 的 TCP 埠 6379,連線來源為:
- 在 `default` 名稱空間中帶有 `role=frontend` 標籤的任何 Pod
- 在帶有 `project=myproject` 標籤的名稱空間中的任何 Pod
- IP 地址範圍在 `172.17.0.0`–`172.17.0.255` 和 `172.17.2.0`–`172.17.255.255` 之間(即,`172.17.0.0/16` 中除了 `172.17.1.0/24` 的所有 IP)
(出口規則) 允許來自 `default` 名稱空間中所有帶有 `role=db` 標籤的 Pod 的連線,目標為 CIDR `10.0.0.0/24` 的 TCP 埠 5978
有關更多示例,請參見 宣告網路策略 演練。
“to”和“from”選擇器的行為
`ingress` `from` 部分或 `egress` `to` 部分可以指定四種選擇器:
podSelector:這會在與 NetworkPolicy 相同的名稱空間中選擇特定的 Pod,這些 Pod 應被允許作為入站源或出站目的地。
namespaceSelector:這選擇特定的名稱空間,其中所有 Pod 都應被允許作為入站源或出站目的地。
namespaceSelector _和_ podSelector:一個同時指定 `namespaceSelector` 和 `podSelector` 的 `to` / `from` 條目選擇特定名稱空間中的特定 Pod。請注意使用正確的 YAML 語法。例如:
...
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
podSelector:
matchLabels:
role: client
...
此策略包含一個 `from` 元素,允許來自帶有標籤 `role=client` 的 Pod(位於帶有標籤 `user=alice` 的名稱空間中)的連線。但以下策略有所不同:
...
ingress:
- from:
- namespaceSelector:
matchLabels:
user: alice
- podSelector:
matchLabels:
role: client
...
它在 `from` 陣列中包含兩個元素,允許來自本地名稱空間中帶有 `role=client` 標籤的 Pod 的連線,_或_ 來自任何名稱空間中帶有 `user=alice` 標籤的任何 Pod 的連線。
如有疑問,請使用 `kubectl describe` 檢視 Kubernetes 如何解釋該策略。
ipBlock:這選擇特定的 IP CIDR 範圍作為允許的入口源或出口目的地。這些應該是叢集外部的 IP,因為 Pod IP 是臨時的且不可預測的。
叢集入口和出口機制通常需要重寫資料包的源或目標 IP。在這種情況下,尚未明確此操作是在 NetworkPolicy 處理之前還是之後發生,並且在網路外掛、雲提供商、`Service` 實現等不同組合下,行為可能會有所不同。
對於入口而言,這意味著在某些情況下,你可能能夠根據實際的原始源 IP 過濾入站資料包,而在其他情況下,NetworkPolicy 所作用的“源 IP”可能是 `LoadBalancer` 的 IP 或 Pod 所在節點的 IP 等。
對於出口,這意味著從 Pod 到 `Service` IP 的連線(這些連線會被重寫為叢集外部 IP)可能受或可能不受基於 `ipBlock` 的策略的約束。
預設策略
預設情況下,如果名稱空間中不存在策略,則允許該名稱空間中 Pod 的所有入站和出站流量。以下示例允許你更改該名稱空間中的預設行為。
預設拒絕所有入站流量
你可以透過建立一個 NetworkPolicy 來為名稱空間建立“預設”入站隔離策略,該策略選擇所有 Pod 但不允許任何入站流量到達這些 Pod。
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
spec:
podSelector: {}
policyTypes:
- Ingress
這確保即使未被任何其他 NetworkPolicy 選中的 Pod 仍將因入站而隔離。此策略不影響任何 Pod 的出站隔離。
允許所有入站流量
如果你想允許名稱空間中所有 Pod 的所有入站連線,你可以建立一個明確允許的策略。
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-ingress
spec:
podSelector: {}
ingress:
- {}
policyTypes:
- Ingress
有了這個策略,任何附加策略都不能導致拒絕這些 Pod 的任何入站連線。此策略對任何 Pod 的出站隔離沒有影響。
預設拒絕所有出口流量
你可以透過建立一個 NetworkPolicy 來為名稱空間建立“預設”出口隔離策略,該策略選擇所有 Pod 但不允許任何來自這些 Pod 的出口流量。
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
spec:
podSelector: {}
policyTypes:
- Egress
這確保了即使未被任何其他 NetworkPolicy 選擇的 Pod 也不會被允許出口流量。此策略不會改變任何 Pod 的入站隔離行為。
允許所有出站流量
如果你想允許名稱空間中所有 Pod 的所有連線,你可以建立一個策略,明確允許該名稱空間中 Pod 的所有出站連線。
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-egress
spec:
podSelector: {}
egress:
- {}
policyTypes:
- Egress
有了這個策略,任何附加策略都不能導致任何來自這些 Pod 的出站連線被拒絕。此策略對任何 Pod 的入站隔離沒有影響。
預設拒絕所有入站和所有出站流量
你可以在該名稱空間中建立以下 NetworkPolicy,為名稱空間建立一個“預設”策略,以阻止所有入站和出站流量。
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
這確保即使未被任何其他 NetworkPolicy 選擇的 Pod 也不會被允許入站或出站流量。
網路流量過濾
NetworkPolicy 是為第四層連線(TCP、UDP 和可選的 SCTP)定義的。對於所有其他協議,行為可能因網路外掛而異。
注意
你必須使用支援 SCTP 協議網路策略的 CNI 外掛。當定義 `deny all` 網路策略時,只保證拒絕 TCP、UDP 和 SCTP 連線。對於其他協議,例如 ARP 或 ICMP,其行為是未定義的。同樣適用於允許規則:當允許特定 Pod 作為入站源或出站目的地時,對於(例如)ICMP 資料包會發生什麼情況是未定義的。ICMP 等協議可能被某些網路外掛允許,而其他外掛則拒絕。
針對一系列埠
Kubernetes v1.25 [穩定]
在編寫 NetworkPolicy 時,你可以針對一個埠範圍而不是單個埠。
這可以透過使用 `endPort` 欄位來實現,如下例所示:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: multi-port-egress
namespace: default
spec:
podSelector:
matchLabels:
role: db
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- protocol: TCP
port: 32000
endPort: 32768
上述規則允許 `default` 名稱空間中帶有 `role=db` 標籤的任何 Pod 透過 TCP 與 `10.0.0.0/24` 範圍內的任何 IP 通訊,前提是目標埠在 32000 到 32768 之間。
使用此欄位時,適用以下限制:
- `endPort` 欄位必須等於或大於 `port` 欄位。
- 只有在定義 `port` 的情況下才能定義 `endPort`。
- 兩個埠都必須是數字。
注意
你的叢集必須使用支援 NetworkPolicy 規範中 `endPort` 欄位的 CNI 外掛。如果你的網路外掛不支援 `endPort` 欄位並且你指定了帶有該欄位的 NetworkPolicy,則該策略將僅應用於單個 `port` 欄位。按標籤定位多個名稱空間
在此場景中,你的 `Egress` NetworkPolicy 使用標籤名稱定位多個名稱空間。為此,你需要為目標名稱空間新增標籤。例如:
kubectl label namespace frontend namespace=frontend
kubectl label namespace backend namespace=backend
在 NetworkPolicy 文件的 `namespaceSelector` 下新增標籤。例如:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: egress-namespaces
spec:
podSelector:
matchLabels:
app: myapp
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchExpressions:
- key: namespace
operator: In
values: ["frontend", "backend"]
注意
無法直接在 NetworkPolicy 中指定名稱空間的名稱。你必須使用帶有 `matchLabels` 或 `matchExpressions` 的 `namespaceSelector` 來根據其標籤選擇名稱空間。按名稱定位名稱空間
Kubernetes 控制平面在所有名稱空間上設定了一個不可變標籤 `kubernetes.io/metadata.name`,該標籤的值是名稱空間的名稱。
雖然 NetworkPolicy 無法透過物件欄位按其名稱定位名稱空間,但你可以使用標準化標籤來定位特定的名稱空間。
Pod 生命週期
注意
以下內容適用於具有符合要求的網路外掛和符合要求的 NetworkPolicy 實現的叢集。當一個新的 NetworkPolicy 物件被建立時,網路外掛可能需要一些時間來處理這個新物件。如果一個受 NetworkPolicy 影響的 Pod 在網路外掛完成 NetworkPolicy 處理之前被建立,該 Pod 可能會在未受保護的情況下啟動,並且隔離規則將在 NetworkPolicy 處理完成後應用。
一旦 NetworkPolicy 由網路外掛處理,
所有受給定 NetworkPolicy 影響的新建立的 Pod 都將在啟動前進行隔離。NetworkPolicy 的實現必須確保在 Pod 的整個生命週期內,甚至從 Pod 中任何容器啟動的第一刻起,過濾都是有效的。由於它們應用於 Pod 級別,NetworkPolicy 對 init 容器、sidecar 容器和普通容器同樣適用。
允許規則最終會在隔離規則之後(或可能同時)應用。在最壞的情況下,如果隔離規則已經應用但尚未應用任何允許規則,新建立的 Pod 在首次啟動時可能完全沒有網路連線。
每個建立的 NetworkPolicy 最終都會由網路外掛處理,但無法從 Kubernetes API 中準確得知何時發生。
因此,Pod 必須能夠應對以與預期不同的網路連線方式啟動的情況。如果你需要確保 Pod 在啟動前可以到達某些目的地,你可以使用 init 容器 來等待這些目的地可達,然後再由 kubelet 啟動應用程式容器。
每個 NetworkPolicy 最終都會應用於所有選定的 Pod。由於網路外掛可能以分散式方式實現 NetworkPolicy,因此當 Pod 首次建立時,或者當 Pod 或策略發生變化時,Pod 可能會看到稍微不一致的網路策略檢視。例如,一個新建立的 Pod,原本應該能夠同時訪問節點 1 上的 Pod A 和節點 2 上的 Pod B,可能會發現它能立即訪問 Pod A,但直到幾秒鐘後才能訪問 Pod B。
NetworkPolicy 和 `hostNetwork` Pod
`hostNetwork` Pod 的 NetworkPolicy 行為是未定義的,但應限制為兩種可能性:
- 網路外掛可以區分 `hostNetwork` Pod 流量與所有其他流量(包括能夠區分同一節點上不同 `hostNetwork` Pod 的流量),並將 NetworkPolicy 應用於 `hostNetwork` Pod,就像應用於 Pod 網路 Pod 一樣。
- 網路外掛無法正確區分 `hostNetwork` Pod 流量,因此在匹配 `podSelector` 和 `namespaceSelector` 時會忽略 `hostNetwork` Pod。`hostNetwork` Pod 的流量被視為與所有其他進出節點 IP 的流量相同。(這是最常見的實現方式。)
這適用於以下情況:
一個 `hostNetwork` Pod 被 `spec.podSelector` 選中。
... spec: podSelector: matchLabels: role: client ...
一個 `hostNetwork` Pod 被 `ingress` 或 `egress` 規則中的 `podSelector` 或 `namespaceSelector` 選中。
... ingress: - from: - podSelector: matchLabels: role: client ...
同時,由於 `hostNetwork` Pod 具有與其所在節點相同的 IP 地址,它們的連線將被視為節點連線。例如,你可以使用 `ipBlock` 規則允許來自 `hostNetwork` Pod 的流量。
你無法透過網路策略實現的功能(至少目前不能)
截至 Kubernetes 1.34,NetworkPolicy API 中不存在以下功能,但你可能可以透過使用作業系統元件(例如 SELinux、OpenVSwitch、IPTables 等)或 Layer 7 技術(Ingress 控制器、服務網格實現)或准入控制器來實現變通方法。如果你是 Kubernetes 網路安全的新手,值得注意的是,以下使用者場景(目前)無法使用 NetworkPolicy API 實現。
- 強制內部叢集流量透過通用閘道器(這可能最好透過服務網格或其他代理實現)。
- 任何與 TLS 相關的功能(請為此使用服務網格或 Ingress 控制器)。
- 節點特定策略(你可以使用 CIDR 符號來表示這些策略,但無法透過其 Kubernetes 識別符號專門定位節點)。
- 按名稱定位服務(但是,你可以透過其標籤定位 Pod 或名稱空間,這通常是一種可行的變通方法)。
- 建立或管理由第三方履行的“策略請求”。
- 適用於所有名稱空間或 Pod 的預設策略(有一些第三方 Kubernetes 發行版和專案可以做到這一點)。
- 高階策略查詢和可達性工具。
- 記錄網路安全事件(例如被阻止或接受的連線)的能力。
- 明確拒絕策略的能力(目前 NetworkPolicies 的模型是預設拒絕,只具有新增允許規則的能力)。
- 阻止迴環或傳入主機流量的能力(Pod 目前無法阻止本地主機訪問,也無法阻止來自其駐留節點的訪問)。
NetworkPolicy 對現有連線的影響
當適用於現有連線的 NetworkPolicy 集合發生變化時——這可能是由於 NetworkPolicy 發生變化,或者策略所選定的名稱空間/Pod(主體和對等體)的相關標籤在現有連線進行中途發生變化——該變化是否會對現有連線生效取決於具體實現。例如:建立了一個策略,導致拒絕之前允許的連線,底層網路外掛實現負責定義該新策略是否會關閉現有連線。建議不要以可能影響現有連線的方式修改策略/Pod/名稱空間。