返回文章列表

Kubernetes RBAC 網路政策進階應用

本文探討 Kubernetes RBAC 和網路政策的進階應用,涵蓋如何結合 RBAC 和網路政策、使用 Calico 等進階網路外掛、設定 externalTrafficPolicy:local

Kubernetes 網路安全

Kubernetes 的 RBAC 協助控管資源存取許可權,但缺乏對資源內容的限制。網路政策中,Pod 標籤成為識別微服務的關鍵,若標籤管理不當,可能導致未授權存取。為降低風險,建議在網路政策中使用名稱空間選擇器,並嚴格控管名稱空間標籤的修改許可權。此外,Calico 等進階網路外掛提供更豐富的網路政策功能,例如根據服務帳戶的匹配和優先順序排序。結合 RBAC 和 Calico 網路政策,可更有效地劃分叢集管理員和微服務團隊的職責,提升整體安全性。更進一步,階層式網路政策和准入控制器能實作更精細的存取控制和策略管理。對於外部流量,瞭解 NodePort、LoadBalancer 服務的運作原理及 kube-proxy 的替代方案至關重要。善用 externalTrafficPolicy:local 可最佳化流量路由並保留客戶端來源 IP。網路外掛的網路策略擴充功能則提供更細緻的存取控制。最後,直接伺服器回傳(DSR)能提升效能,但需注意反向路徑過濾(RPF)設定以確保安全性。

Kubernetes RBAC 與網路政策的進階應用

Kubernetes 的 Role-Based Access Control (RBAC) 能夠有效地控制對資源的存取許可權,但它並未限制資源的內容。在網路政策的情境中,Pod 的標籤(labels)尤其重要,因為它們是用來識別其他微服務的主要機制。因此,如果某個團隊編寫了一項網路政策,允許來自具有特定標籤的 Pod 的流量,那麼理論上任何有權管理 Pod 的團隊都可以為自己的 Pod 新增該標籤,從而獲得對該微服務的存取許可權。

降低風險的方法

為了減少這種風險,建議在政策規則中始終使用名稱空間(namespace)選擇器,並謹慎選擇有權更改名稱空間標籤的團隊。如果已經定義了標準化的政策和標籤架構,並且各團隊都值得信賴地遵循這些規定,那麼這些限制更多是理論上的問題,而非實際問題。然而,對於某些組織來說,這些問題可能代表著真正的資安需求。因此,這些組織可能會考慮利用 Kubernetes RBAC 和 Kubernetes 網路政策以外的其他功能。

豐富的網路政策實作

一些網路政策的實作不僅支援 Kubernetes 網路政策,還支援自定義的網路政策資源,這些資源可以與 Kubernetes 網路政策一同使用或取代它們。根據實作的不同,這些可能會提供更多選項,用於如何在團隊之間劃分職責和 RBAC。有廠商提供更豐富的網路政策實作,支援 Kubernetes 網路政策並新增更多功能,例如 Weave Net、Kube-router、Antrea 和 Calico。以下將以 Calico 為例進行說明,因為它是目前最廣泛佈署的容器網路外掛之一。

Calico 網路政策

Calico 支援 Kubernetes 網路政策的功能集,同時也支援其自身的 Calico 網路政策資源,這些資源可以與 Kubernetes 網路政策一同使用。Calico 網路政策有兩種型別,均屬於 projectcalico.org/v3 API 群組:

  • NetworkPolicy:這些政策是名稱空間範圍內的(與 Kubernetes 網路政策類別似)。
  • GlobalNetworkPolicy:這些政策適用於整個叢集,不受名稱空間限制。

這兩種型別的 Calico 網路政策都支援比 Kubernetes 網路政策更豐富的功能,包括:

  • 更豐富的匹配條件,例如能夠匹配 Kubernetes 服務帳戶(service accounts)。
  • 明確的允許、拒絕或記錄動作,用於政策規則,而非 Kubernetes 網路政策中隱含的允許動作。
  • 優先順序排序,用於定義多個政策適用於同一工作負載時的評估順序。

範例:使用 Calico 控制網路存取

以下範例展示瞭如何使用 Calico 根據服務帳戶的標籤來控制網路存取。在 Kubernetes 中,Pod 都有與之相關聯的服務帳戶,因此可以透過服務帳戶來識別 Pod。應該使用 RBAC 來控制哪些使用者可以為服務帳戶分配標籤。

apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
  name: restrict-intern-access
  namespace: prod-engineering
spec:
  serviceAccountSelector: 'role == "intern"'
  ingress:
  - action: Allow
    source:
      serviceAccounts:
        selector: 'role == "intern"'
  egress:
  - action: Allow
    destination:
      serviceAccounts:
        selector: 'role == "intern"'

內容解密:

此 Calico 網路政策範例限制了具有 intern 服務帳戶的 Pod 的網路存取。根據此政策,具有 intern 角色的服務帳戶所代表的 Pod,只能與同樣具有 intern 角色服務帳戶的 Pod 進行通訊。

  1. serviceAccountSelector:此欄位用於根據服務帳戶的標籤選擇符合條件的 Pod。
  2. ingressegress:這兩個欄位定義了輸入和輸出的網路流量規則。在此範例中,僅允許來自具有 intern 角色服務帳戶的 Pod 的流量進入或流出。
  3. action: Allow:明確指定允許符合條件的流量。

結合 RBAC 與網路政策

透過結合 RBAC 和網路政策,可以更乾淨地在更高層級的叢集維運或安全團隊與個別微服務團隊之間劃分職責。例如:

  • 給予叢集維運或安全團隊管理叢集範圍內 Calico 網路政策的 RBAC 許可權,使他們能夠定義基本的、更高層級的規則,從而設定叢集的整體安全態勢。
  • 給予每個微服務團隊在其微服務名稱空間中定義 Kubernetes 網路政策的 RBAC 許可權,使他們能夠為自己負責的微服務定義細粒度的約束。

分層網路政策

在某些組織中,團隊之間的職責劃分可能更加複雜。為了更容易滿足這些需求,可以使用支援階層式網路政策概念的網路政策實作。商業實作中已經有支援使用政策層(policy tiers)的階層式網路政策,而 Kubernetes 社群也在討論類別似的概念(階層式名稱空間和政策)。透過定義每個層級的 RBAC,可以限制誰可以與該層級互動。網路政策在這些層級中按定義的順序進行評估,可以有多個層級以匹配組織中的職責劃分。

使用階層式網路政策管理跨團隊信任

在 Kubernetes 環境中,網路政策(Network Policy)是管理不同團隊間信任關係的重要工具。透過定義清晰的網路政策,可以有效地控制不同微服務之間的通訊,從而提高整體安全性。

網路政策的分層實作

Figure 8-2 提供了一個簡化的範例,展示如何使用階層式網路政策將責任劃分到組織內的三個不同層級。這種分層方法可以有效地將網路政策的管理責任分配給不同的團隊。

准入控制器(Admission Controllers)

Kubernetes 本身並不具備在細粒度層級上強制執行限制的能力,但它支援准入控制器 API,允許第三方准入控制器插入 Kubernetes API 流程中,以在建立、更新和刪除操作期間對物件進行語義驗證。

准入控制器的應用範例

  1. 驗證網路政策:確保網路政策同時包含 ingress 和 egress 規則,以符合組織的最佳實踐。
  2. 標籤標準化:確保每個 pod 都具有特定的標籤,以符合組織的標籤標準。
  3. 限制使用者群組:限制不同使用者群組使用特定的標籤值。

准入控制器的實作選項

  1. 使用現有的第三方准入控制器
  2. 編寫自定義准入控制器
  3. 使用通用准入控制器,例如 Kyverno 或根據 Open Policy Agent 的准入控制器。

管理跨團隊信任的最佳實踐

  1. 使用 RBAC 和網路政策 定義邊界,以協助管理跨團隊的活動。
  2. 利用服務帳戶(Service Accounts) 在網路政策中控制網路存取,以協助管理信任。
  3. 使用准入控制器 控制存取並實作跨團隊的信任邊界。
  4. 開發、平台和安全團隊之間的協作 對於實施安全措施至關重要。

將服務暴露給外部客戶

在前面的章節中,我們探討了網路政策是保護 Kubernetes 的主要工具之一。這對於叢集內 pod-to-pod 流量(東西向流量)和 pod 與叢集外部實體之間的流量(南北向流量)都是正確的。

網路政策的最佳實踐

  1. 限制連線埠:僅允許微服務預期的連線埠接收連線。
  2. 限制客戶端:僅允許特定的客戶端連線到微服務。

實作網路政策的考量

  1. 網路外掛程式的選擇
  2. Kubernetes 原語的使用,例如 Service 對外暴露微服務。

客戶端原始IP位址的儲存與Kubernetes服務暴露

在Kubernetes叢集中,將應用程式或微服務暴露給外部客戶端時,客戶端的原始IP位址是否能被保留是一個重要的考量。客戶端原始IP位址的儲存對於安全控制、流量管理和稽核等場景至關重要。本章將探討在不同場景下客戶端原始IP位址的行為,包括直接連線到Pod、使用Kubernetes服務以及Kubernetes Ingress。

直接連線到Pod

直接連線到Pod是一種相對少見的場景,但某些情況下是必要的,例如某些分散式資料儲存系統需要特定的Pod IP位址來進行資料分佈或客戶端對等連線。要支援從叢集外部直接連線到Pod IP位址,需要Pod網路使得Pod IP位址可以在叢集邊界之外路由。這通常意味著使用特定的網路外掛,如雲端提供者的網路外掛(例如Amazon VPC CNI外掛)或能夠使用BGP與本地企業網路整合的網路外掛(例如Kube-router、Calico CNI外掛)。

程式碼範例:組態Calico CNI外掛

apiVersion: projectcalico.org/v3
kind: FelixConfiguration
metadata:
  name: default
spec:
  ipipEnabled: true
  logSeverityScreen: Info

內容解密:

  1. apiVersionkind 指定了Calico的組態版本和型別。
  2. metadata.name 指定了組態的名稱為"default"。
  3. spec.ipipEnabled 設定為 true 以啟用IPIP(IP in IP)隧道,這對於跨網段的Pod間通訊是必要的。
  4. logSeverityScreen 設定為 Info 以控制日誌的詳細程度。

從安全形度來看,直接連線到Pod的連線具有原始客戶端IP位址,這使得網路策略可以輕易地限制對特定客戶端的存取。然而,這也意味著需要更加註意網路策略的最佳實踐,以避免未經授權的存取。

Kubernetes服務

Kubernetes服務提供了一種方便的機制,用於從叢集外部存取Pod,使用NodePort或LoadBalancer型別的服務,或透過明確組態服務的外部IP。預設情況下,Kubernetes服務由kube-proxy實作。kube-proxy執行在叢集中的每個節點上,負責攔截到Kubernetes服務的連線並在後端Pod之間進行負載平衡。

Cluster IP服務

對於源自叢集內部的連線,kube-proxy可以使用目標網路位址轉換(DNAT)將連線到服務的Cluster IP對映到後端Pod。這種對映對於連線上的任何傳回封包都是相反的。對映是在不改變源IP位址的情況下完成的,如圖9-1所示。

NodePort服務

使用NodePort型別的Kubernetes服務是從叢集外部存取服務的最基本方式。NodePort是在叢集中的每個節點上保留的一個埠,透過該埠可以存取服務。在典型的Kubernetes佈署中,kube-proxy負責攔截到NodePort的連線並在後端Pod之間進行負載平衡。

作為這個過程的一部分,NAT被用來將目標IP位址和埠從節點IP和NodePort對映到所選擇的後端Pod和服務埠。然而,與連線到Cluster IP不同的是,在NodePort的情況下,源IP位址也被對映從客戶端IP到位址。

程式碼範例:建立NodePort服務

apiVersion: v1
kind: Service
metadata:
  name: example-service
spec:
  type: NodePort
  selector:
    app: example-app
  ports:
  - name: http
    port: 80
    targetPort: 8080
    protocol: TCP
    nodePort: 30080

內容解密:

  1. apiVersionkind 指定了Kubernetes資源的版本和型別。
  2. metadata.name 指定了服務的名稱為"example-service"。
  3. spec.type 設定為 NodePort 以將服務暴露在每個節點的特定埠上。
  4. selector 指定了服務選擇的Pod標籤。
  5. ports 部分定義了服務的埠組態,包括服務埠、目標埠和協定。
  6. nodePort 指定了在每個節點上暴露的埠號。

由於NAT改變了源IP位址,因此任何適用於目標Pod的網路策略都無法匹配原始客戶端IP位址。這意味著任何這樣的策略都僅限於限制目標協定和埠,而無法根據外部客戶端的IP位址進行限制。

Kubernetes 服務的外部存取限制與最佳化

在預設情況下,Kubernetes 的 NodePort 服務會對外部流量進行負載平衡,但這種實作方式會改變客戶端的來源 IP 位址,使得根據來源 IP 的網路策略限制變得困難。然而,有幾種方法可以解決這個問題,包括設定 externalTrafficPolicy:local、使用支援 NodePort 感知網路策略擴充的網路外掛程式,以及使用替代 kube-proxy 的服務負載平衡實作來保留客戶端來源 IP 位址。

LoadBalancer 服務的運作原理

LoadBalancer 服務建立在 NodePort 服務的基礎上,並與外部網路負載平衡器整合。外部網路負載平衡器可以根據不同的雲端供應商或硬體負載平衡器進行整合。客戶端可以透過負載平衡器的特定 IP 位址存取服務,而負載平衡器預設會將流量均勻分配到服務的 NodePort 上。

圖示:LoadBalancer 服務的網路路徑

此圖示展示了客戶端如何透過負載平衡器存取 Kubernetes 服務。

內容解密:

  1. 客戶端發起請求到負載平衡器。
  2. 負載平衡器根據負載平衡策略將請求轉發到某個節點的 NodePort。
  3. 節點上的 kube-proxy 將請求進一步轉發到後端的 Pod。

使用 externalTrafficPolicy:local 最佳化流量路由

設定 externalTrafficPolicy:local 可以指定只將連線負載平衡到本地節點上的服務後端 Pod。這樣做有兩個主要好處:減少額外的網路跳躍;保留客戶端的來源 IP 位址,因為 kube-proxy 不需要在本地進行來源網路位址轉換(SNAT)。

圖示:使用 externalTrafficPolicy:local 的網路路徑

此圖示展示了使用 externalTrafficPolicy:local 後,流量如何直接路由到具有後端 Pod 的節點。

內容解密:

  1. 設定 externalTrafficPolicy:local 後,負載平衡器只會將流量轉發到具有後端 Pod 的節點。
  2. kube-proxy 在本地節點上進行負載平衡時不會改變客戶端的來源 IP 位址。
  3. 網路策略可以直接根據客戶端的來源 IP 位址進行限制。

網路策略擴充

一些 Kubernetes 網路外掛程式(如 Calico)提供了網路策略擴充功能,可以用來進一步保護服務的外部存取。這些擴充允許在節點層級應用網路策略,而不僅限於 Pod 層級,從而實作更細粒度的存取控制。

圖示:Calico 網路策略擴充

此圖示展示了 Calico 如何在節點層級應用網路策略。

內容解密:

  1. Calico 的網路策略擴充允許在主機端點上應用策略。
  2. 這種方式提供了額外的安全層,可以控制哪些客戶端可以存取特定的 NodePort。
  3. 結合 externalTrafficPolicy:local 使用,可以實作根據來源 IP 的精細存取控制。

Kubernetes 服務實作與安全考量

Kubernetes 服務(Service)是將多個 Pod 透過特定網路規則進行抽象化的結果,提供穩定的網路介面供客戶端存取。在預設情況下,Kubernetes 使用 kube-proxy 來實作服務功能。不過,某些網路外掛(Network Plugin)會提供替代實作方案,以取代 kube-proxy。

kube-proxy 的替代方案

某些網路外掛由於其特定的 Pod 網路實作方式,無法與 kube-proxy 的資料平面(dataplane)相容,因此需要使用替代方案。例如,使用 Linux eBPF 資料平面的 CNI 外掛可能會選擇替換 kube-proxy,使用其原生服務實作。

保留客戶端來源 IP 位址

相較於 kube-proxy,一些替代實作提供了額外的功能,例如在負載平衡外部客戶端時保留客戶端的來源 IP 位址。Figure 9-6 展示了根據 eBPF 的資料平面如何實作此行為。

此圖示

這種實作方式允許在不依賴 externalTrafficPolicy: Local 的情況下,將原始客戶端來源 IP 位址保留至後端 Pod。這使得套用在後端 Pod 的網路政策(Network Policy)能夠根據客戶端 IP 位址進行存取控制。

直接伺服器回傳(DSR)

DSR 允許目標 Pod 的回應流量直接傳回客戶端,而無需經過原始的入口節點。某些網路外掛,如根據 eBPF 的資料平面,支援 DSR 功能。Figure 9-7 展示了使用 DSR 的 Kubernetes 服務網路路徑。

此圖示
@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Kubernetes RBAC 網路政策進階應用

package "Kubernetes Cluster" {
    package "Control Plane" {
        component [API Server] as api
        component [Controller Manager] as cm
        component [Scheduler] as sched
        database [etcd] as etcd
    }

    package "Worker Nodes" {
        component [Kubelet] as kubelet
        component [Kube-proxy] as proxy
        package "Pods" {
            component [Container 1] as c1
            component [Container 2] as c2
        }
    }
}

api --> etcd : 儲存狀態
api --> cm : 控制迴圈
api --> sched : 調度決策
api --> kubelet : 指令下達
kubelet --> c1
kubelet --> c2
proxy --> c1 : 網路代理
proxy --> c2

note right of api
  核心 API 入口
  所有操作經由此處
end note

@enduml

DSR 的優缺點

DSR 減少了一個網路跳躍,進而降低了:

  • 整體服務延遲
  • 原始入口節點的 CPU 負載
  • 叢集內的東-西向網路流量

然而,DSR 也帶來了一些安全隱患。特別是,需要在底層網路中放寬反向路徑過濾(RPF)的設定,以允許 DSR 正常運作。

反向路徑過濾(RPF)

RPF 是一種網路路由機制,用於阻止來自特定來源 IP 位址的流量,如果該 IP 位址沒有對應的路由透過相同的鏈路。RPF 可以防止攻擊者「偽造」IP 位址。

RPF 在 DSR 中的考量

在 DSR 的情境下,若服務透過 NodePort 存取,則回應流量會攜帶來源 IP 位址為 Node 1。因此,底層網路需要放寬 RPF 設定,否則回應流量將被過濾。若服務透過服務 IP 公告存取,則回應流量攜帶來源 IP 位址為服務 IP,此時不需要放寬 RPF 設定。

程式碼範例與解析

以下是一個 Kubernetes 服務的 YAML 定義範例:

apiVersion: v1
kind: Service
metadata:
  name: example-service
spec:
  selector:
    app: example-app
  ports:
  - name: http
    port: 80
    targetPort: 8080
  type: LoadBalancer

內容解密:

  1. apiVersionkind 定義了該資源的 API 版本和型別,在此為 v1 版本的 Service。
  2. metadata.name 指定了 Service 的名稱為 example-service
  3. spec.selector 定義了選擇哪些 Pod 作為該 Service 的後端,在此選擇標籤為 app: example-app 的 Pod。
  4. spec.ports 定義了 Service 對外提供的連線埠和協定,以及對應到後端 Pod 的目標連線埠。
  5. spec.type 指定了 Service 的型別為 LoadBalancer,表示該 Service 將由雲端供應商的負載平衡器提供外部存取能力。