Kubernetes 的最小許可權原則實踐,核心在於限制 Pod 對系統資源、網路資源以及應用程式資源的存取。安全上下文允許開發者設定 Pod 和容器的許可權,例如 UID、GID 和檔案系統許可權等,以限制對系統資源的存取。Pod 安全策略則是由叢集管理員定義,用於限制 Pod 的規格,例如是否允許使用主機網路、主機 PID 和特權模式等,進一步強化安全性。除了系統資源外,網路資源的存取控制也至關重要。預設情況下,Pod 之間可以互相通訊,這可能帶來安全隱患。網路策略允許定義 Pod 之間的通訊規則,限制 Pod 僅能存取必要的服務,實作網路層的最小許可權。最後,資源限制控制 Pod 的資源使用量,避免單個 Pod 耗盡節點資源,影響其他 Pod 的正常運作,確保叢集的穩定性。

Kubernetes 工作負載的最小許可權原則

在 Kubernetes 環境中,通常會為工作負載關聯一個服務帳戶(預設為 default)。這樣,Pod 中的程式就可以使用服務帳戶令牌與 kube-apiserver 進行通訊。DevOps 應該謹慎地授予服務帳戶必要的許可權,以實作最小許可權原則。除了存取 kube-apiserver 以操作 Kubernetes 物件之外,Pod 中的程式還可以存取工作節點上的資源和其他 Pod/微服務。在本文中,我們將討論如何實作對系統資源、網路資源和應用資源的最小許可權存取。

最小許可權存取系統資源

在 Kubernetes 中,執行在容器或 Pod 中的微服務本質上是執行在工作節點上的一個程式,該程式被隔離在自己的名稱空間中。Pod 或容器可以根據組態存取工作節點上的不同型別的資源。這是由安全上下文(Security Context)控制的,可以在 Pod 級別和容器級別進行組態。組態 Pod/容器的安全上下文應該是開發人員的任務之一(在安全設計和審查的幫助下),而 Pod 安全策略(Pod Security Policy)則是 DevOps 的任務之一,用於限制 Pod/容器對系統資源的存取。

安全上下文

安全上下文提供了一種方式來定義 Pod 和容器的許可權和存取控制設定,以實作對系統資源的最小許可權存取。在 Kubernetes 中,Pod 級別的安全上下文與容器級別的安全上下文是不同的,儘管有一些重疊的屬性可以在兩個級別上進行組態。一般來說,安全上下文提供了以下功能:

  • 選擇性存取控制(DAC):組態容器中的程式繫結的使用者 ID(UID)或群組 ID(GID),以及容器的根檔案系統是否為唯讀等。
  • 加強 Linux 安全(SELinux):組態 SELinux 安全上下文,定義 Pod 或容器的級別標籤、角色標籤、型別標籤和使用者標籤。
  • 許可權模式:組態容器是否以許可權模式執行。
  • Linux 功能:組態容器的 Linux 功能。
  • AppArmor:組態 Pod 或容器的 AppArmor 組態檔。
  • 安全計算模式(seccomp):組態 Pod 或容器的 seccomp 組態檔。
  • AllowPrivilegeEscalation:組態程式是否可以獲得比其父程式更多的許可權。
apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    fsGroup: 2000
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: [ "sleep", "1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
    securityContext:
      allowPrivilegeEscalation: false

內容解密:

此 YAML 組態檔定義了一個名為 security-context-demo 的 Pod,並設定了安全上下文。在 spec.securityContext 中,設定了 runAsUser1000,表示容器內的程式以 UID 1000 執行;fsGroup 設定為 2000,表示掛載的磁碟區 /data/demo 的擁有者為 GID 2000。在容器的 securityContext 中,設定了 allowPrivilegeEscalationfalse,表示禁止程式提升許可權。

Pod 安全策略

Pod 安全策略是 Kubernetes 叢集級別的資源,用於控制 Pod 規格中與安全相關的屬性。它定義了一組規則。當在 Kubernetes 叢集中建立 Pod 時,Pod 需要符合 Pod 安全策略中定義的規則,否則將無法啟動。Pod 安全策略控制或應用以下屬性:

  • 是否允許執行許可權容器
  • 是否允許使用主機級別的名稱空間
  • 是否允許使用主機連線埠
  • 是否允許使用不同型別的磁碟區
  • 是否允許存取主機的檔案系統
  • 是否要求容器使用唯讀根檔案系統
  • 限制容器的使用者 ID 和群組 ID
  • 限制容器的許可權提升
  • 限制容器的 Linux 功能
  • 要求使用 SELinux 安全上下文
  • 應用 seccomp 和 AppArmor 組態檔到 Pod
  • 限制 Pod 可以執行的 sysctls
  • 是否允許使用 proc 掛載型別
  • 限制 FSGroup 對磁碟區的存取
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted-psp
spec:
  privileged: false
  allowPrivilegeEscalation: false
  requiredDropCapabilities:
    - ALL
  volumes:
    - 'configMap'
    - 'emptyDir'
    - 'projected'
    - 'secret'
    - 'downwardAPI'
    - 'persistentVolumeClaim'
  hostNetwork: false
  hostIPC: false
  hostPID: false
  runAsUser:
    rule: 'MustRunAsNonRoot'
  seLinux:
    rule: 'RunAsAny'
  supplementalGroups:
    rule: 'MustRunAs'
    ranges:
      - min: 1
        max: 65535
  fsGroup:
    rule: 'MustRunAs'
    ranges:
      - min: 1
        max: 65535

內容解密:

此 YAML 組態檔定義了一個名為 restricted-psp 的 Pod 安全策略。該策略禁止建立特權容器、不允許許可權提升、要求丟棄所有 Linux 功能、限制可使用的磁碟區型別、不允許使用主機網路、主機 IPC 和主機 PID、要求容器以非 root 使用者執行、允許任意 SELinux 標籤、限制補充群組和 FSGroup 的範圍。

資源限制控制

預設情況下,一個容器可以使用節點上的所有記憶體和 CPU 資源。一個包含加密貨幣挖礦二進位檔案的容器可能會輕易地耗盡節點上的 CPU 資源,這些資源是由其他 Pod 共用的。為工作負載設定資源請求和限制始終是一個好的做法。資源請求會影響排程器將 Pod 分配到哪個節點,而資源限制則設定了容器被終止的條件。為工作負載分配更多的資源請求和限制總是安全的,以避免驅逐或終止。然而,請記住,如果將資源請求或限制設定得太高,就會造成叢集上的資源浪費,並且分配給工作負載的資源可能無法充分利用。

apiVersion: v1
kind: Pod
metadata:
  name: resource-demo
spec:
  containers:
  - name: resource-demo-ctr
    image: busybox
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

內容解密:

此 YAML 組態檔定義了一個名為 resource-demo 的 Pod,並為其容器設定了資源請求和限制。requests 部分指定了容器需要的最低資源量,包括記憶體(64Mi)和 CPU(250m);limits 部分指定了容器可以使用的最大資源量,包括記憶體(128Mi)和 CPU(500m)。這樣可以防止容器無限制地使用資源,從而影響節點上的其他 Pod。

在 Kubernetes 中應用最小許可權原則

網路資源存取的最小許可權

預設情況下,同一 Kubernetes 叢集中的任意兩個 Pod 可以相互通訊,如果沒有在 Kubernetes 叢集外部設定代理規則或防火牆規則,Pod 也可能能夠與網際網路通訊。Kubernetes 的開放性模糊了微服務的安全邊界,我們不能忽視網路資源,如其他微服務提供的 API 端點。假設 Namespace X 中的一個工作負載(Pod X)只需要存取 Namespace NS1 中的微服務 A,而 Namespace NS2 中有微服務 B。兩個微服務都公開了 RESTful 端點。預設情況下,工作負載可以存取兩個微服務,假設微服務層級沒有驗證或授權,也沒有在 Namespace NS1 和 NS2 中強制實施網路策略。可以參考下面的圖表來說明這種情況。

圖 4.1 – 無網路策略的網路存取

在上圖中,Pod X 可以存取兩個微服務,儘管它們位於不同的名稱空間中。另請注意,Pod X 只需要存取 Namespace NS1 中的微服務 A。那麼,我們可以做些什麼來限制 Pod X 僅存取微服務 A,以實作最小許可權?是的,Kubernetes 網路策略可以提供幫助。我們將在第 5 章《設定 Kubernetes 安全邊界》中更詳細地介紹網路策略。一般來說,Kubernetes 網路策略定義了一組 Pod 之間以及與其他網路端點之間允許通訊的規則。可以為工作負載定義入口規則和出口規則。

注意
  • 入口規則:定義允許哪些來源與受網路策略保護的 Pod 通訊的規則。
  • 出口規則:定義允許哪些目的地與受網路策略保護的 Pod 通訊的規則。

網路策略範例

在下面的範例中,為了在 Pod X 中實施最小許可權原則,需要在 Namespace X 中定義一個網路策略,其中包含一個出口規則,指定只允許存取微服務 A:

圖 4.2 – 網路策略阻止存取微服務 B

在上圖中,Namespace X 中的網路策略阻止了 Pod X 對微服務 B 的任何請求,而 Pod X 仍然可以存取微服務 A。在網路策略中定義出口規則將有助於確保工作負載存取網路資源的最小許可權。最後但同樣重要的是,我們仍然需要從最小許可權的角度關注應用程式資源層級。

應用程式資源存取的最小許可權

雖然這個主題屬於應用程式安全的範疇,但在此提及是有價值的。如果工作負載存取的應用程式支援多個具有不同許可權級別的使用者,最好檢查代表工作負載授予使用者的許可權是否必要。例如,負責稽核的使用者不需要任何寫入許可權。應用程式開發人員在設計應用程式時應該牢記這一點。這有助於確保工作負載存取應用程式資源的最小許可權。

組態 Kubernetes 安全邊界

安全邊界將具有相同安全問題和存取級別的實體劃分到不同的安全域中,而信任邊界則是程式執行和資料更改信任級別的分界線。安全邊界中的控制措施確保了在不同邊界之間移動的執行不會在沒有適當驗證的情況下提升信任級別。當資料或執行在沒有適當控制的情況下跨越安全邊界時,安全漏洞就會出現。

在本章中,我們將討論安全和信任邊界的重要性。首先,我們將澄清安全和信任邊界之間的混淆。然後,我們將探討 Kubernetes 生態系統中的安全域和安全邊界。最後,我們將研究一些增強在 Kubernetes 中佈署的應用程式的安全邊界的 Kubernetes 功能。

您應該瞭解安全域和安全邊界的概念,並瞭解根據底層容器技術以及內建安全功能(如 PodSecurityPolicy 和 NetworkPolicy)圍繞 Kubernetes 構建的安全邊界。

本章涵蓋以下主題

  • 安全邊界簡介
  • 安全邊界與信任邊界
  • Kubernetes 安全域
  • Kubernetes 實體作為安全邊界
  • 系統層的安全邊界
  • 網路層的安全邊界

安全邊界簡介

安全邊界存在於資料層、網路層和系統層。安全邊界取決於 IT 部門或基礎設施團隊使用的技術。例如,公司使用虛擬機器來管理其應用程式 - 虛擬機器的安全邊界是 hypervisor。Hypervisor 確保在虛擬機器中執行的程式碼不會逃離虛擬機器或影響實體節點。當公司開始採用微服務並使用協調器來管理其應用程式時,容器是其中一個安全邊界。然而,與 hypervisor 相比,容器並不能提供強大的安全邊界,它們也不旨在這樣做。容器在應用層強制執行限制,但不能防止攻擊者從核心層繞過這些限制。

在網路層,傳統上,防火牆為應用程式提供了強大的安全邊界。在微服務架構中,Kubernetes 中的 Pod 可以相互通訊。網路策略用於限制 Pod 和 Service 之間的通訊。資料層的安全邊界眾所周知。核心限制對系統或 bin 目錄的寫入存取,只允許 root 或系統使用者進行存取,這是資料層安全邊界的一個簡單例子。在容器化環境中,chroot 防止容器篡改其他容器的檔案系統。Kubernetes 以一種可以在網路和系統層強制實施強大安全邊界的方式重構應用程式佈署。

安全邊界與信任邊界

安全邊界和信任邊界經常被用作同義詞。儘管相似,但這兩個術語之間存在微妙的差異。信任邊界是系統更改其信任級別的地方。執行信任邊界是指令需要不同許可權才能執行的地方。例如,資料函式庫伺服器執行 /bin 中的程式碼是跨越信任邊界的執行的例子。同樣,資料信任邊界是資料在具有不同信任級別的實體之間移動的地方。由終端使用者插入到受信任的資料函式庫中的資料是跨越信任邊界的資料的例子。

而安全邊界是不同安全域之間的劃分線,安全域是一組具有相同存取級別的實體。例如,在傳統的 Web 架構中,導向使用者的應用程式是安全域的一部分,內部網路是不同安全域的一部分。安全邊界具有與之相關聯的存取控制。可以將信任邊界視為一堵牆,將安全邊界視為圍繞牆的圍欄。

識別生態系統中的安全和信任邊界非常重要。它有助於確保在指令和資料跨越邊界之前進行適當的驗證。在 Kubernetes 中,元件和物件跨越不同的安全邊界。瞭解這些邊界對於在攻擊者跨越安全邊界時制定風險緩解計劃非常重要。CVE-2018-1002105 是由於跨信任邊界驗證缺失而導致的攻擊的一個典型例子;API 伺服器中的代理請求處理允許未經身份驗證的使用者獲得對叢集的管理員許可權。同樣,CVE-2018-18264 允許使用者跳過儀錶板上的身份驗證過程,從而允許未經身份驗證的使用者存取敏感的叢集資訊。

現在讓我們來看看不同的 Kubernetes 安全域。

Kubernetes 安全域

Kubernetes 叢集可以大致分為三個安全域:

  • Kubernetes 主元件:Kubernetes 主元件定義了 Kubernetes 生態系統的控制平面。主元件負責叢集平滑執行所需的決策,例如排程。主元件包括 kube-apiserver、etcd、kube-controller-manager、DNS 伺服器和 kube-scheduler。Kubernetes 主元件的洩露可能會危及整個 Kubernetes 叢集。

內容解密:

本文介紹了 Kubernetes 的三個主要安全域,包括 Kubernetes 主元件、節點和 Pod。Kubernetes 主元件負責叢集的管理和控制,而節點和 Pod 則執行應用程式。

詳細分析

Kubernetes 的安全性和信任邊界對於確保叢集的安全至關重要。透過瞭解不同的安全域和安全邊界,可以更好地制定風險緩解計劃,以防止攻擊者跨越安全邊界。

@startuml
skinparam backgroundColor #FEFEFE
skinparam componentStyle rectangle

title Kubernetes最小許可權原則實踐

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

此圖示展示了安全邊界、信任邊界和 Kubernetes 安全域之間的關係。Kubernetes 安全域包括 Kubernetes 主元件、節點和 Pod。

內容解密:

本圖示使用 Plantuml 圖表呈現了安全邊界、信任邊界和 Kubernetes 安全域之間的關係。透過此圖示,可以清晰地瞭解 Kubernetes 的不同安全域及其之間的關係。

Kubernetes 安全邊界設定

Kubernetes 元件與安全邊界

在 Kubernetes 環境中,多個元件共同工作以確保叢集的安全運作。這些元件包括工作節點(worker nodes)上的 Kubernetes 元件、Kubernetes 物件(如 Pods、Services、Volumes 和 Namespaces)等。瞭解這些元件及其安全邊界對於保護叢集至關重要。

Kubernetes 工作元件

Kubernetes 工作元件佈署在每個工作節點上,確保 Pods 和容器正常運作。這些元件透過授權和 TLS 通道與主元件進行通訊。即使工作元件被洩露,叢集仍可正常運作,但需要及時識別並移除受損節點。

Kubernetes 物件

Kubernetes 物件是叢集狀態的持久實體,包括 Pods、Services、Volumes 和 Namespaces。這些物件由開發人員或 DevOps 佈署。透過定義 Pod 的 SecurityContext、網路規則等,可以為物件設定額外的安全邊界。

Kubernetes 實體作為安全邊界

在 Kubernetes 叢集中,與其互動的實體(如物件和元件)具有內建的安全邊界。瞭解這些安全邊界至關重要:

  • 容器(Containers):容器提供基本隔離,使用 cgroups、Linux Namespaces、AppArmor 組態檔案和 seccomp 組態檔案來隔離應用程式。
  • Pods:Pod 是一組一個或多個容器。Pod 相較於容器提供了更多的資源隔離,如網路和 IPC。SecurityContext、NetworkPolicy 和 PodSecurityPolicy 等功能在 Pod 級別工作,以確保更高的隔離級別。
  • 節點(Nodes):Kubernetes 中的節點也是安全邊界。可以使用 nodeSelectors 指定 Pod 在特定節點上執行。核心和虛擬機器管理程式為在節點上執行的 Pod 強制實施安全控制。
  • 叢集(Cluster):叢集是 Pod、容器以及主節點和工作節點上的元件的集合。叢集提供強大的安全邊界。在叢集內執行的 Pod 和容器與其他叢集在網路和系統層面隔離。
  • Namespaces:Namespaces 是虛擬叢集,用於隔離 Pod 和服務。LimitRanger 准入控制器在 Namespace 級別應用,以控制資源利用和拒絕服務攻擊。網路策略也可以應用於 Namespace 級別。
  • Kubernetes API 伺服器:Kubernetes API 伺服器與所有 Kubernetes 元件互動,包括 etcd、controller-manager 和 kubelet,用於組態叢集。它調解與主元件的通訊,因此叢集管理員無需直接與叢集元件互動。

不同威脅行為者的安全邊界

不同威脅行為者(如特權攻擊者、內部攻擊者和終端使用者)與 Kubernetes 實體互動時,面臨不同的安全邊界:

  • 終端使用者:終端使用者與 ingress、暴露的 Kubernetes 服務或直接與節點上的開放埠互動。節點、Pods、kube-apiserver 和外部防火牆保護叢集元件免受損害。
  • 內部攻擊者:內部攻擊者具有對 Pods 和容器的存取許可權。Namespaces 和由 kube-apiserver 強制執行的存取控制可以防止這些攻擊者提升許可權或損害叢集。網路策略和 RBAC 控制可以防止橫向移動。
  • 特權攻擊者:kube-apiserver 是保護主元件免受特權攻擊者損害的唯一安全邊界。如果特權攻擊者損害了 kube-apiserver,則意味著叢集已經完全暴露。

系統層的安全邊界

微服務在 Pod 中執行,而 Pod 被排程到叢集中的工作節點上執行。瞭解系統層的安全邊界以及如何加強它至關重要。

Linux Namespaces 作為安全邊界

Linux Namespaces 是 Linux 核心的一個功能,用於分割資源以實作隔離。預設情況下,每個 Pod 都有自己的網路 Namespace 和 IPC Namespace。同一個 Pod 中的每個容器都有自己的 PID Namespace,因此一個容器無法知道其他容器在同一個 Pod 中執行。

組態 Host Namespaces 的風險

在 Kubernetes 工作負載中,可以組態使用主機的 Namespaces。這樣做會使微服務使用主機級別的 Namespaces,從而削弱其他微服務的安全邊界。例如,使用 HostNetwork、HostIPC 或 HostPID 設定,會使 Pod 使用主機的網路、IPC 或 PID Namespace。

apiVersion: v1
kind: Pod
metadata:
  name: example-pod
spec:
  hostNetwork: true
  containers:
  - name: example-container
    image: nginx

程式碼解析:

此 YAML 組態檔案定義了一個名為 example-pod 的 Pod,並設定 hostNetwork: true,使該 Pod 使用主機的網路 Namespace。這意味著該 Pod 將分享主機的網路堆積疊,可以看到主機上的所有網路介面。

內容解密:

  1. hostNetwork: true:此設定使 Pod 使用主機的網路 Namespace,而不是擁有自己的網路 Namespace。這樣做會增加安全風險,因為 Pod 可以存取主機的網路介面。
  2. 安全性影響:使用主機的網路 Namespace 可能會使 Pod 存取敏感的網路資源,增加潛在的安全風險。