Kubernetes 網(wǎng)絡策略

2022-05-06 15:34 更新

網(wǎng)絡策略

如果你希望在 IP 地址或端口層面(OSI 第 3 層或第 4 層)控制網(wǎng)絡流量, 則你可以考慮為集群中特定應用使用 Kubernetes 網(wǎng)絡策略(NetworkPolicy)。 NetworkPolicy 是一種以應用為中心的結構,允許你設置如何允許 Pod 與網(wǎng)絡上的各類網(wǎng)絡“實體” (我們這里使用實體以避免過度使用諸如“端點”和“服務”這類常用術語, 這些術語在 Kubernetes 中有特定含義)通信。

Pod 可以通信的 Pod 是通過如下三個標識符的組合來辯識的:

  1. 其他被允許的 Pods(例外:Pod 無法阻塞對自身的訪問)
  2. 被允許的名字空間
  3. IP 組塊(例外:與 Pod 運行所在的節(jié)點的通信總是被允許的, 無論 Pod 或節(jié)點的 IP 地址)

在定義基于 Pod 或名字空間的 NetworkPolicy 時,你會使用 選擇算符 來設定哪些流量 可以進入或離開與該算符匹配的 Pod。

同時,當基于 IP 的 NetworkPolicy 被創(chuàng)建時,我們基于 IP 組塊(CIDR 范圍) 來定義策略。

前置條件 

網(wǎng)絡策略通過網(wǎng)絡插件 來實現(xiàn)。要使用網(wǎng)絡策略,你必須使用支持 NetworkPolicy 的網(wǎng)絡解決方案。 創(chuàng)建一個 NetworkPolicy 資源對象而沒有控制器來使它生效的話,是沒有任何作用的。

Pod 隔離的兩種類型

Pod 有兩種隔離: 出口的隔離和入口的隔離。它們涉及到可以建立哪些連接。 這里的“隔離”不是絕對的,而是意味著“有一些限制”。 另外的,“非隔離方向”意味著在所述方向上沒有限制。這兩種隔離(或不隔離)是獨立聲明的, 并且都與從一個 Pod 到另一個 Pod 的連接有關。

默認情況下,一個 Pod 的出口是非隔離的,即所有外向連接都是被允許的。如果有任何的 NetworkPolicy 選擇該 Pod 并在其 ?policyTypes ?中包含 “Egress”,則該 Pod 是出口隔離的, 我們稱這樣的策略適用于該 Pod 的出口。當一個 Pod 的出口被隔離時, 唯一允許的來自 Pod 的連接是適用于出口的 Pod 的某個 NetworkPolicy 的 ?egress ?列表所允許的連接。 這些 ?egress ?列表的效果是相加的。

默認情況下,一個 Pod 對入口是非隔離的,即所有入站連接都是被允許的。如果有任何的 NetworkPolicy 選擇該 Pod 并在其 ?policyTypes ?中包含 “Ingress”,則該 Pod 被隔離入口, 我們稱這種策略適用于該 Pod 的入口。 當一個 Pod 的入口被隔離時,唯一允許進入該 Pod 的連接是來自該 Pod 節(jié)點的連接和適用于入口的 Pod 的某個 NetworkPolicy 的 ?ingress ?列表所允許的連接。這些 ?ingress ?列表的效果是相加的。

網(wǎng)絡策略是相加的,所以不會產(chǎn)生沖突。如果策略適用于 Pod 某一特定方向的流量, Pod 在對應方向所允許的連接是適用的網(wǎng)絡策略所允許的集合。 因此,評估的順序不影響策略的結果。

要允許從源 Pod 到目的 Pod 的連接,源 Pod 的出口策略和目的 Pod 的入口策略都需要允許連接。 如果任何一方不允許連接,建立連接將會失敗。

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

除非選擇支持網(wǎng)絡策略的網(wǎng)絡解決方案,否則將上述示例發(fā)送到API服務器沒有任何效果。

必需字段:與所有其他的 Kubernetes 配置一樣,NetworkPolicy 需要 ?apiVersion?、 ?kind ?和 ?metadata ?字段。

spec:NetworkPolicy 規(guī)約 中包含了在一個名字空間中定義特定網(wǎng)絡策略所需的所有信息。

podSelector:每個 NetworkPolicy 都包括一個 ?podSelector?,它對該策略所 適用的一組 Pod 進行選擇。示例中的策略選擇帶有 "role=db" 標簽的 Pod。 空的 ?podSelector ?選擇名字空間下的所有 Pod。

policyTypes: 每個 NetworkPolicy 都包含一個 ?policyTypes ?列表,其中包含 ?Ingress ?或 ?Egress ?或兩者兼具。?policyTypes ?字段表示給定的策略是應用于 進入所選 Pod 的入站流量還是來自所選 Pod 的出站流量,或兩者兼有。 如果 NetworkPolicy 未指定 ?policyTypes ?則默認情況下始終設置 ?Ingress?; 如果 NetworkPolicy 有任何出口規(guī)則的話則設置 ?Egress?。

ingress: 每個 NetworkPolicy 可包含一個 ?ingress ?規(guī)則的白名單列表。 每個規(guī)則都允許同時匹配 ?from ?和 ?ports ?部分的流量。示例策略中包含一條 簡單的規(guī)則: 它匹配某個特定端口,來自三個來源中的一個,第一個通過 ?ipBlock ?指定,第二個通過 ?namespaceSelector ?指定,第三個通過 ?podSelector ?指定。

egress: 每個 NetworkPolicy 可包含一個 ?egress ?規(guī)則的白名單列表。 每個規(guī)則都允許匹配 ?to ?和 ?port ?部分的流量。該示例策略包含一條規(guī)則, 該規(guī)則將指定端口上的流量匹配到 ?10.0.0.0/24? 中的任何目的地。

所以,該網(wǎng)絡策略示例:

  1. 隔離 "default" 名字空間下 "role=db" 的 Pod (如果它們不是已經(jīng)被隔離的話)。
  2. (Ingress 規(guī)則)允許以下 Pod 連接到 "default" 名字空間下的帶有 "role=db" 標簽的所有 Pod 的 6379 TCP 端口:
    • "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.1.0/24 之外的所有 172.17.0.0/16)
  3. (Egress 規(guī)則)允許從帶有 "role=db" 標簽的名字空間下的任何 Pod 到 CIDR 10.0.0.0/24 下 5978 TCP 端口的連接。

選擇器 to 和 from 的行為 

可以在 ?ingress ?的 ?from ?部分或 ?egress ?的 ?to ?部分中指定四種選擇器:

podSelector: 此選擇器將在與 NetworkPolicy 相同的名字空間中選擇特定的 Pod,應將其允許作為入站流量來源或出站流量目的地。

namespaceSelector:此選擇器將選擇特定的名字空間,應將所有 Pod 用作其 入站流量來源或出站流量目的地。

namespaceSelector 和 podSelector: 一個指定 ?namespaceSelector ?和 ?podSelector ?的 ?to?/?from ?條目選擇特定名字空間中的特定 Pod。 注意使用正確的 YAML 語法;下面的策略:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
      podSelector:
        matchLabels:
          role: client
  ...

在 ?from ?數(shù)組中僅包含一個元素,只允許來自標有 ?role=client? 的 Pod 且 該 Pod 所在的名字空間中標有 ?user=alice? 的連接。但是 這項 策略:

  ...
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          user: alice
    - podSelector:
        matchLabels:
          role: client
  ...

在 ?from ?數(shù)組中包含兩個元素,允許來自本地名字空間中標有 ?role=client? 的 Pod 的連接,或 來自任何名字空間中標有 ?user=alice? 的任何 Pod 的連接。

如有疑問,請使用 ?kubectl describe? 查看 Kubernetes 如何解釋該策略。

ipBlock: 此選擇器將選擇特定的 IP CIDR 范圍以用作入站流量來源或出站流量目的地。 這些應該是集群外部 IP,因為 Pod IP 存在時間短暫的且隨機產(chǎn)生。

集群的入站和出站機制通常需要重寫數(shù)據(jù)包的源 IP 或目標 IP。 在發(fā)生這種情況時,不確定在 NetworkPolicy 處理之前還是之后發(fā)生, 并且對于網(wǎng)絡插件、云提供商、?Service ?實現(xiàn)等的不同組合,其行為可能會有所不同。

對入站流量而言,這意味著在某些情況下,你可以根據(jù)實際的原始源 ?IP ?過濾傳入的數(shù)據(jù)包, 而在其他情況下,NetworkPolicy 所作用的 源IP 則可能是 ?LoadBalancer ?或 Pod 的節(jié)點等。

對于出站流量而言,這意味著從 Pod 到被重寫為集群外部 IP 的 ?Service ?IP 的連接可能會或可能不會受到基于 ?ipBlock ?的策略的約束

默認策略 

默認情況下,如果名字空間中不存在任何策略,則所有進出該名字空間中 Pod 的流量都被允許。 以下示例使你可以更改該名字空間中的默認行為。

默認拒絕所有入站流量

你可以通過創(chuàng)建選擇所有容器但不允許任何進入這些容器的入站流量的 NetworkPolicy 來為名字空間創(chuàng)建 “default” 隔離策略。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
spec:
  podSelector: {}
  policyTypes:
  - Ingress

這樣可以確保即使容器沒有選擇其他任何 NetworkPolicy,也仍然可以被隔離。 此策略不會更改默認的出口隔離行為。

默認允許所有入站流量

如果要允許所有流量進入某個名字空間中的所有 Pod(即使添加了導致某些 Pod 被視為 “隔離”的策略),則可以創(chuàng)建一個策略來明確允許該名字空間中的所有流量。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-ingress
spec:
  podSelector: {}
  ingress:
  - {}
  policyTypes:
  - Ingress

默認拒絕所有出站流量

你可以通過創(chuàng)建選擇所有容器但不允許來自這些容器的任何出站流量的 NetworkPolicy 來為名字空間創(chuàng)建 “default” 隔離策略。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
spec:
  podSelector: {}
  policyTypes:
  - Egress

此策略可以確保即使沒有被其他任何 NetworkPolicy 選擇的 Pod 也不會被允許流出流量。 此策略不會更改默認的入站流量隔離行為。

默認允許所有出站流量 

如果要允許來自名字空間中所有 Pod 的所有流量(即使添加了導致某些 Pod 被視為“隔離”的策略), 則可以創(chuàng)建一個策略,該策略明確允許該名字空間中的所有出站流量。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-all-egress
spec:
  podSelector: {}
  egress:
  - {}
  policyTypes:
  - Egress

默認拒絕所有入口和所有出站流量

你可以為名字空間創(chuàng)建“默認”策略,以通過在該名字空間中創(chuàng)建以下 NetworkPolicy 來阻止所有入站和出站流量。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

此策略可以確保即使沒有被其他任何 NetworkPolicy 選擇的 Pod 也不會被 允許入站或出站流量。

SCTP 支持

FEATURE STATE: Kubernetes v1.20 [stable]

作為一個穩(wěn)定特性,SCTP 支持默認是被啟用的。 要在集群層面禁用 SCTP,你(或你的集群管理員)需要為 API 服務器指定 ?--feature-gates=SCTPSupport=false,...? 來禁用 ?SCTPSupport ?特性門控。 啟用該特性門控后,用戶可以將 NetworkPolicy 的 ?protocol ?字段設置為 ?SCTP?。

你必須使用支持 SCTP 協(xié)議網(wǎng)絡策略的 CNI 插件。

針對某個端口范圍 

FEATURE STATE: Kubernetes v1.22 [beta]

在編寫 NetworkPolicy 時,你可以針對一個端口范圍而不是某個固定端口。

這一目的可以通過使用 ?endPort ?字段來實現(xiàn),如下例所示:

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

上面的規(guī)則允許名字空間 ?default ?中所有帶有標簽 ?role=db ?的 Pod 使用 TCP 協(xié)議 與 ?10.0.0.0/24? 范圍內(nèi)的 IP 通信,只要目標端口介于 32000 和 32768 之間就可以。

使用此字段時存在以下限制:

  • 作為一種 Beta 階段的特性,端口范圍設定默認是被啟用的。要在整個集群 范圍內(nèi)禁止使用 ?endPort ?字段,你(或者你的集群管理員)需要為 API 服務器設置 ?-feature-gates=NetworkPolicyEndPort=false,... ?以禁用 ?NetworkPolicyEndPort ?特性門控。
  • ?endPort ?字段必須等于或者大于 ?port ?字段的值。
  • 兩個字段的設置值都只能是數(shù)字。

你的集群所使用的 CNI 插件 必須支持在 NetworkPolicy 規(guī)約中使用 ?endPort ?字段。 如果你的網(wǎng)絡插件 不支持 ?endPort ?字段,而你指定了一個包含 ?endPort ?字段的 NetworkPolicy, 策略只對單個 ?port ?字段生效。

基于名字指向某名字空間 

FEATURE STATE: Kubernetes 1.22 [stable]

只要 ?NamespaceDefaultLabelName ?特性門控 被啟用,Kubernetes 控制面會在所有名字空間上設置一個不可變更的標簽 ?kubernetes.io/metadata.name?。該標簽的值是名字空間的名稱。

如果 NetworkPolicy 無法在某些對象字段中指向某名字空間,你可以使用標準的 標簽方式來指向特定名字空間。

通過網(wǎng)絡策略(至少目前還)無法完成的工作

到 Kubernetes 1.24 為止,NetworkPolicy API 還不支持以下功能,不過 你可能可以使用操作系統(tǒng)組件(如 SELinux、OpenVSwitch、IPTables 等等) 或者第七層技術(Ingress 控制器、服務網(wǎng)格實現(xiàn))或準入控制器來實現(xiàn)一些 替代方案。 如果你對 Kubernetes 中的網(wǎng)絡安全性還不太了解,了解使用 NetworkPolicy API 還無法實現(xiàn)下面的用戶場景是很值得的。

  • 強制集群內(nèi)部流量經(jīng)過某公用網(wǎng)關(這種場景最好通過服務網(wǎng)格或其他代理來實現(xiàn));
  • 與 TLS 相關的場景(考慮使用服務網(wǎng)格或者 Ingress 控制器);
  • 特定于節(jié)點的策略(你可以使用 CIDR 來表達這一需求不過你無法使用節(jié)點在 Kubernetes 中的其他標識信息來辯識目標節(jié)點);
  • 基于名字來選擇服務(不過,你可以使用 標簽 來選擇目標 Pod 或名字空間,這也通常是一種可靠的替代方案);
  • 創(chuàng)建或管理由第三方來實際完成的“策略請求”;
  • 實現(xiàn)適用于所有名字空間或 Pods 的默認策略(某些第三方 Kubernetes 發(fā)行版本 或項目可以做到這點);
  • 高級的策略查詢或者可達性相關工具;
  • 生成網(wǎng)絡安全事件日志的能力(例如,被阻塞或接收的連接請求);
  • 顯式地拒絕策略的能力(目前,NetworkPolicy 的模型默認采用拒絕操作, 其唯一的能力是添加允許策略);
  • 禁止本地回路或指向宿主的網(wǎng)絡流量(Pod 目前無法阻塞 localhost 訪問, 它們也無法禁止來自所在節(jié)點的訪問請求)。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號