Kubernetes 提供了 Secret 物件來管理敏感資料,例如密碼、API 金鑰等。Secret 物件的型別包括 Opaque、dockerconfigjson 和 service-account-token 等,用於不同場景。建立 Secret 時,可以使用 kubectl 命令搭配 from-literal 和 from-file 標誌,將資料以 base64 編碼後儲存於 etcd 中。然而,etcd 本身不提供加密,因此資料安全性需要額外考量。kube-apiserver 作為 Kubernetes 的主要元件,也可能成為攻擊目標。惡意攻擊者可能透過 API 存取 etcd 或直接提取 etcd 的本地檔案來取得 Secret 資料。Kubernetes 節點的檔案系統以及叢集備份也存在潛在風險。為了降低風險,需要實施多層次安全措施,例如許可權控制、加密傳輸、監控和稽核。平台層面的 Secret 物件不應移交到外部保管函式庫,而應用層面的 Secret 則可以考慮使用外部保管函式庫或類別似方案。RBAC 機制可以控制對 Secret 的存取,透過設定 Role 和 RoleBinding,限制只有授權使用者才能操作 Secret。此外,稽核和監控 Secret 的變更記錄也至關重要,以便及時發現潛在威脅。實務上,可以使用 Podman Desktop、Go 語言和 Minikube 等工具來操作和管理 Secret,並結合 RBAC 和稽核機制,構建更安全的 Kubernetes 應用環境。

Kubernetes 秘密管理的基本概念與應用

在理解 Kubernetes 的起源和設計原則後,讓我們進一步探討 Kubernetes 中的「Secret」概念。這些 Secret 如何在 Kubernetes 上儲存,如何注入到 Pod 中,以及我們需要面對的安全問題。秘密管理在現代雲原生架構中非常重要,因為它涉及到敏感資料的處理與保護。

Secret 的基本概念

在設計和佈署 Red Hat OpenShift 的過程中,秘密管理這個主題往往被視為無關緊要。這是因為我們過去幾十年的傳統環境中,基礎設施和應用程式團隊之間存在明確的分離原則。基礎設施團隊負責基礎設施,應用程式團隊負責應用程式。然而,在 Kubernetes 中,這些擔憂合而為一,成為平台的一部分。

在容器映像的設計中,秘密不能硬編碼或包含在容器映像中。如果將秘密硬編碼,這些秘密將會對所有有權存取容器映像倉函式庫的內部和外部相關者公開。如果將容器映像推播到公共倉函式庫,這些秘密將會對更多的人公開。

為瞭解決這些問題,Kubernetes 提供了一個內建的秘密管理框架,專門有一個名為 Secret 的 API 物件來管理這些敏感資料。以下是一個 Secret 物件定義的範例:

apiVersion: v1
kind: Secret
metadata:
  name: mysecret
type: Opaque
data:
  username: YWRtaW4K
  password: UGFja3QxMjMhCg==

內容解密:

在這個範例中,我們建立了一個名為 mysecret 的 Secret 物件。這個物件的型別是 Opaque,表示我們自己定義資料容器中的內容。

data 欄位包含兩個鍵值對:

  • username:這裡的值是以 base64 編碼後的字串 YWRtaW4K
  • password:這裡的值是以 base64 編碼後的字串 UGFja3QxMjMhCg==

base64 編碼的作用

為什麼要使用 base64 編碼?首先,base64 編碼並不是加密,它只是為了便於命令列、網路傳輸和 kube-apiserver 處理避免因特殊字元而截斷負載。

然而,base64 編碼後的資料仍然可以在任何作業系統或網站上進行解碼。因此,當 kube-apiserver 將負載儲存到 etcd 健值儲存時,不會對資料進行加密。

ConfigMap 與 Secret 的區別

另一個與 Secret 功能類別似的 API 物件是 ConfigMap。ConfigMap 主要用於傳遞環境變數和應用程式引數,但它也被廣泛用於包含高階應用程式組態。例如:

apiVersion: v1
kind: ConfigMap
metadata:
  name: app-environment
data:
  appversion1: dev

此圖示顯示了 ConfigMap 和 Secret 在資料欄位上的主要區別——編碼部分。ConfigMap 的資料欄位期望 UTF-8 字串,而 Secret 則期望鍵值對中的值以 base64 編碼。

在 Kubernetes 中儲存秘密

接下來我們來看一下實際操作範例:

  1. 建立 Secret 物件:使用提供的 YAML 清單建立一個 Secret 物件並檢查其狀態。
  2. 從 Kubernetes 還原 YAML 清單:如果惡意攻擊者成功與 kube-apiserver 談判,則他們可以提取 Kubernetes 叢集中的一些或全部 Secret 物件。
  3. 建立新的 Secret 物件:從頭開始編寫鍵值對、編寫 YAML 清單並透過命令列推播。

資料流圖示:

@startuml
skinparam componentStyle rectangle
skinparam backgroundColor #FEFEFE
skinparam component {
  BackgroundColor<<secret>> LightYellow
  BorderColor<<secret>> Orange
  BackgroundColor<<storage>> LightCyan
  BorderColor<<storage>> DarkCyan
}

package "Kubernetes 叢集" {
  [kube-apiserver] as API

  package "Control Plane" {
    database "etcd\n(未加密儲存)" as ETCD <<storage>>
  }

  package "Worker Node" {
    component "kubelet" as KUBELET
    component "Pod" as POD {
      [應用容器] as APP
      note right of APP
        環境變數或掛載卷
        取得 Secret 資料
      end note
    }
  }

  component "Secret 物件" as SECRET <<secret>>
}

actor "開發者/管理員" as USER

USER --> API : 1. 建立 Secret\n(base64 編碼)
API --> ETCD : 2. 儲存 Secret\n(無加密)
ETCD --> SECRET : 3. 持久化
API --> KUBELET : 4. 傳遞 Secret 資訊
KUBELET --> POD : 5. 注入 Secret
KUBELET --> APP : 6. 解碼並提供\n敏感資料

note bottom of ETCD
  安全風險:
  - etcd 不提供內建加密
  - API 存取可能被攔截
  - 備份檔案包含明文
end note

@enduml

此圖示顯示了 Kubernetes 中 Secret 的完整生命週期:從開發者建立、經由 kube-apiserver 處理、儲存到 etcd、最終注入到 Pod 的應用容器中。圖中特別標示了 etcd 的安全風險,說明 Secret 物件在儲存時並未加密,需要額外的安全措施保護。

內容解密:

  • A:應用程式需要存取敏感資料。
  • B:Kubernetes 叢集作為中介層管理和分配秘密。
  • C:etcd 健值儲存作為 Kubernetes 的後端資料函式庫儲存所有組態和秘密。
  • D:Secret 物件包含敏感資料並被 kube-apiserver 啟用。
  • E:Pod 作為最小執行單元從叢集取得所需的秘密。
  • F:應用程式最終存取和使用敏感資料。

安全考量

儘管 Kubernetes 提供了強大的秘密管理功能,但我們仍需注意以下安全問題:

  1. 許可權控制:確保只有授權的人員能夠存取和修改秘密。
  2. 加密傳輸:確保在傳輸過程中使用 TLS 加密。
  3. 監控和稽核:定期檢查和監控秘密變更記錄以發現潛在威脅。

總結來說,Kubernetes 的 Secret 提供了一種有效的方式來管理敏感資料。透過理解其設計原則和實際操作範例,我們可以更好地保護我們的雲原生應用程式。

Kubernetes 的秘密管理深度分析

Kubernetes Secret 物件的解密與注入

在 Kubernetes 中,Secret 物件用來儲存敏感資訊,如密碼、API 金鑰等。這些 Secret 物件可以被注入到 Pod 中,供容器使用。以下是詳細的過程和注意事項:

首先,我們需建立一個 Secret 物件,並將其注入到 Pod 中。為了進行測試和除錯,我們可以使用 busybox 容器,這是一個小型且高效的環境。Pod 清單檔案中包含對新建立 Secret 物件的參照,並將值分配給環境變數。這些變數在容器的日誌中可以被 echo 出來。

此圖示

@startuml
!define RECTANGLE class

skinparam sequence {
  ArrowColor DarkBlue
  ActorBorderColor DarkGray
  LifeLineBorderColor Blue
  ParticipantBorderColor DarkBlue
  ParticipantBackgroundColor LightBlue
}

actor "管理員" as ADMIN
participant "kubectl" as KUBECTL
participant "kube-apiserver" as API
database "etcd\n(base64 編碼)" as ETCD
participant "kubelet" as KUBELET
participant "Pod" as POD
participant "Busybox\nContainer" as CONTAINER

ADMIN -> KUBECTL : kubectl create secret
activate KUBECTL
KUBECTL -> API : 建立 Secret 請求
activate API
API -> API : 驗證與授權檢查
API -> ETCD : 儲存 Secret\n(base64 編碼)
activate ETCD
ETCD --> API : 確認儲存
deactivate ETCD
API --> KUBECTL : Secret 建立成功
deactivate API
KUBECTL --> ADMIN : 回應成功
deactivate KUBECTL

ADMIN -> KUBECTL : kubectl apply -f pod.yaml
activate KUBECTL
KUBECTL -> API : 建立 Pod 請求\n(引用 Secret)
activate API
API -> KUBELET : 排程 Pod 到節點
activate KUBELET
KUBELET -> API : 請求 Secret 資料
API -> ETCD : 查詢 Secret
activate ETCD
ETCD --> API : 返回 Secret 資料
deactivate ETCD
API --> KUBELET : 傳遞 Secret\n(仍為 base64)
KUBELET -> KUBELET : 解碼 base64
KUBELET -> POD : 建立 Pod
activate POD
POD -> CONTAINER : 啟動容器\n注入解碼後的環境變數
activate CONTAINER
CONTAINER -> CONTAINER : echo 環境變數\n驗證 Secret 內容
deactivate CONTAINER
deactivate POD
deactivate KUBELET
API --> KUBECTL : Pod 建立成功
deactivate API
KUBECTL --> ADMIN : 回應成功
deactivate KUBECTL

note right of ETCD
  安全考量:
  • base64 僅編碼非加密
  • etcd 需啟用加密存儲
  • 網路傳輸使用 TLS
end note

@enduml

此圖示展示了 Kubernetes 中 Secret 物件從建立到注入 Pod 的完整序列流程,包含管理員操作、API 驗證、etcd 儲存、kubelet 處理、最終在 Busybox 容器中使用的全過程。圖中特別標示了 base64 編碼解碼的關鍵步驟。

Secret 的解密與傳輸

在這個過程中,kubelet 負責解碼 base64 編碼的 Secret 載荷。這樣的設計確保了在不同元件之間及網路傳輸過程中的資料安全性。

etcd 的安全性挑戰

etcd 是 Kubernetes 的核心資源管理元件,它儲存了所有的 Kubernetes 資源狀態。然而,etcd 本身並不提供加密功能,這意味著儲存在其中的資料是未加密的。

如果惡意攻擊者成功突破了 etcd Pod 的防線,他們將能夠完全控制整個應用平台,甚至可能影響到雲端服務提供商的帳戶。

# 惡意攻擊者可能會嘗試透過 API 存取 etcd
import requests

etcd_url = "http://etcd-server:2379/v3/kv/put"
secret_data = {"key": "my-secret-key", "value": "my-secret-value"}

response = requests.post(etcd_url, json=secret_data)
print(response.content)

內容解密:

上述 Python 程式碼展示了一個簡單的 API 請求範例,用於向 etcd 傳送一個秘密資料。這段程式碼首先定義了 etcd 的 URL 和要傳送的秘密資料(鍵值對)。接著使用 requests.post 函式向 etcd 傳送一個 POST 請求,並將秘密資料作為 JSON 格式傳送。最後列印預出伺服器的回應內容。

惡意攻擊者可透過 API 呼叫直接讀取 etcd 中的資料。

敏感資料的本地提取

進一步地,惡意攻擊者可能會提取 etcd 的本地檔案並進行分析,以取得建立的 Secret 物件。雖然這看似是一種極端情況,但如果考慮到檔案系統存取或 Kubernetes 叢集備份中的 etcd 檔案,這種風險是存在的。

Kubernetes 的安全設計原則

Kubernetes 提供了一個無摩擦體驗給平台和應用團隊,但它並不是一個硬化解決方案。為瞭解決這些安全問題,我們需要考慮多層次的安全措施。

平台層面

當 Kubernetes 被佈署時,會建立一系列 Secret 物件以允許內部元件之間的互動(如儲存、網路、執行單元、控制器等)。後來佈署的第三方元件也會遵循相同的模型。這些 Secret 物件不應該被移交到外部保管函式庫以確保還原力度。

應用層面

應用在存取內部或外部服務(如資料函式庫、S3 桶等)時需要使用 Secret 物件,這些可以被移交到外部保管函式庫或類別似解決方案中。然而,應用可能還需要 ConfigMap 物件、卷加金鑰、TLS 證書等。

安全性挑戰與解決方案

在 Kubernetes 中管理 Secret 物件所面臨的安全挑戰並不簡單。以下是一些潛在的安全漏洞:

  • kube-apiserver:這是 Kubernetes 的主要元件之一,惡意攻擊者可能透過它進入應用平台。
  • etcd:管理 Kubernetes 資源狀態,存在多種安全漏洞:
    • 資料函式庫檔案:etcd 沒有提供加密能力,資料函式庫檔案是二進位制格式且易於讀取。
    • API 服務:類別似於 kube-apiserver,etcd 是根據 API 的服務,任何存取或網路追蹤都可能洩露資料。
  • Kubernetes 節點
    • 節點檔案系統:etcd Pod 的檔案系統寄宿在一個捲上以提供持久儲存。該卷附加到節點上,透過存取節點可以透過附加捲取得資料。
  • 備份:Kubernetes 叢集備份包含 etcd 檔案,存取備份可能洩露資料。

Secret 暴露途徑

Secret 物件可能透過多種方式被暴露:

  • 與 Kubernetes 元件(如 kube-apiserver 和 etcd)互動。
  • 輸入物理層面(如直接節點存取或備份)。

為了確保敏感資料在 Kubernetes 中的安全性,我們需要綜合考慮上述所有因素並採取相應的安全措施。

Kubernetes 密碼管理概述

在前一章節中,我們已經對 Kubernetes 及其各元件有了一個全面的瞭解,並且成功地在 Kubernetes 上佈署了一個使用 Go 語言編寫的應用程式。在這個過程中,我們需要為應用程式的組態新增秘密(Secrets)。管理這些秘密涉及多種考量,從建立、修改到刪除,我們都需要處理安全性、可擴充套件性和韌性等問題。

本章將探討 Kubernetes 密碼管理的概念,包括以下幾個主題:

  • Kubernetes 密碼與其他 Kubernetes 物件的區別
  • 不同型別的密碼及其使用場景
  • 在 Kubernetes 中建立、修改和刪除密碼
  • 不同佈署情境下的 Kubernetes 密碼組態
  • 管理密碼的需求,包括安全儲存和存取控制
  • 使用 RBAC 安全存取密碼
  • 稽核和監控密碼使用情況

技術需求

為了將理論與實踐結合起來,我們將使用一系列常見的工具和平台來互動容器、Kubernetes 和密碼管理。本章將使用以下工具:

  • Podman Desktop:一款開源軟體,可以互動容器、執行本地 Kubernetes 例項,並連線到遠端平台如 Red Hat OpenShift、Azure Kubernetes Service(AKS)等。你可以從官方網站取得安裝指引。
  • Go 語言:我們將在本章中使用 Go 語言進行開發。安裝指引可以參考 Go 的官方檔案。
  • Minikube:用於本地執行 Kubernetes 叢集。安裝指引可以參考 Minikube 的官方檔案。
  • GitHub 儲存函式庫:本文中的所有程式碼範例都已經上傳到 GitHub 儲存函式庫中,結構清晰且有詳細說明。

Kubernetes 密碼與其他 Kubernetes 物件的區別

Kubernetes 的核心建構塊之一就是 Kubernetes 物件。透過這些物件,我們可以表示系統狀態。在 Kubernetes 上執行的應用程式由實際程式、應用程式使用的資源以及應用程式組態(如健康檢查)組成。關於安全性等跨切面問題,我們會有角色基礎存取控制(RBAC)的組態,包括叢集範圍角色、名稱空間角色以及與使用者或實體繫結的角色。

除了上述物件外,Kubernetes 還包括名稱空間(Namespace),它們作為邏輯容器;還有網路策略(NetworkPolicy),它們是叢集範圍內的流量規則。透過建立這些物件,我們宣告瞭叢集的期望狀態。Kubernetes 則負責確保實際狀態與我們建立的物件所定義的狀態比對。

以下是一個典型的 Kubernetes 物件 YAML 表示:

apiVersion: apps/v1 #Kubernetes API 版本
kind: Deployment #物件型別
metadata: #後設資料資訊
  name: example-deployment
spec: #物件應該達到的狀態

常見的 Kubernetes 物件包括 Pod、Deployment、StatefulSet、CronJob、Service、Ingress、NetworkPolicy、ConfigMap 和 Secret。

不同型別的密碼及其使用場景

Kubernetes 提供了多種型別的密碼。背後的儲存機制與前一章節中提到的是一樣的:密碼一旦建立,將被序列化並儲存在 etcd 中。不同之處在於它們在使用時如何處理。

Opaque

Opaque 是預設型別的秘密。無論是檔案還是變數,只要需要新增敏感組態,它都會被建立為 Opaque 秘密。

$ kubectl create secret generic opaque-example-from-literals --from-literal=literal1=text-for-literal-1
$ kubectl get secret opaque-example-from-literals -o yaml

內容解密:

以上命令建立了一個名為 opaque-example-from-literals 的 Opaque Secret。其中 --from-literal 標誌允許我們直接透過命令列建立 Key-Value 單元。

apiVersion: v1
data:
  literal1: dGV4dC1mb3ItbGl0ZXJhbC0x #Base64 編碼後的文字
kind: Secret
type: Opaque

這段 YAML 組態顯示了 Secret 的結構。data 欄位中的值是 Base64 編碼過後的文字 text-for-literal-1

其他型別的秘密

除了 Opaque 外,Kubernetes 還支援其他型別的秘密,例如 kubernetes.io/dockerconfigjson 用於 Docker 認證、kubernetes.io/service-account-token 用於服務帳戶令牌等。

建立與管理 Secret

以下是如何使用 kubectl 命令來建立和管理 Secret 的範例:

$ kubectl create secret generic my-secret --from-file=my-key=./key.txt --from-literal=username=user --from-file=my-cert=./cert.pem
$ kubectl get secret my-secret -o yaml

內容解密:

以上命令建立了一個名為 my-secret 的 Secret。其中 --from-file 標誌允許從檔案讀取資料,而 --from-literal 標誌則允許直接透過命令列建立 Key-Value 單元。

apiVersion: v1
data:
  my-key: <Base64 編碼後內容>
  username: <Base64 編碼後內容>
  my-cert: <Base64 編碼後內容>
kind: Secret
type: Opaque

這段 YAML 組態顯示了 Secret 的結構。每個 Key 值都是 Base64 編碼後的原始資料。

RBAC 與 Secret 安全性

Kubernetes 提供了 Role-Based Access Control(RBAC)機制來控制對 Secret 的存取。透過適當地設定 Role 和 RoleBinding,我們可以確保只有授權使用者才能存取特定 Secret。

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: secret-reader-role
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["get", "watch", "list"]
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-secrets-binding
subjects:
- kind: User
  name: "user-name"
roleRef:
  kind: Role
  name: secret-reader-role

內容解密:

以上是如何設定 RBAC 清單來限制對 Secret 的存取許可權:

  1. Role:定義了一個名為 secret-reader-role 的角色,該角色允許對 Secret 的 get, watch, 和 list 操作。
  2. RoleBinding:將該角色繫結到特定使用者 user-name 上。

跨切面關注點

Secret 的使用涉及一些跨切面關注點,例如稽核和監控。我們需要確保系統能夠記錄和監控誰何時存取了哪些 Secret。

apiVersion: audit.k8s.io/v1beta1
kind: Policy
rules:
- level: Metadata #設定稽核等級為 Metadata, 用來記錄哪些 API 被呼叫以及什麼時間被呼叫。

內容解密:

這段組態顯示瞭如何設定稽核策略以記錄對 Secret 操作相關資訊:

  • level: 指定了稽核等級為 Metadata, 用於記錄哪些 API 被呼叫以及何時被呼叫。

總結來說,Kubernetes Secrets 是儲存敏感組態資料的一種安全方式。透過適當地組態和管理 Secrets,我們可以確保敏感資訊得到保護並只被授權人員存取。

此圖示展示了從傳統架構到雲原生架構的演進歷史,以及在不同階段密碼管理機制的變化。

@startuml
skinparam backgroundColor #FEFEFE
skinparam titleFontSize 16
skinparam titleFontStyle bold

title 密碼管理機制隨技術演進的變化

skinparam state {
  BackgroundColor<<era1>> LightCoral
  BackgroundColor<<era2>> LightGreen
  BackgroundColor<<era3>> LightBlue
  BackgroundColor<<era4>> LightYellow
  BorderColor Black
  FontStyle bold
}

state "Bare Metal 時代" as BM <<era1>> {
  state "密碼管理方式" as BM_SECRET
  BM_SECRET : • 硬編碼在應用程式中
  BM_SECRET : • 配置檔案明文儲存
  BM_SECRET : • 系統環境變數
  BM_SECRET :
  BM_SECRET : 風險: 高度暴露
}

state "虛擬機器時代" as VM <<era2>> {
  state "密碼管理方式" as VM_SECRET
  VM_SECRET : • 加密配置檔案
  VM_SECRET : • 密鑰管理工具
  VM_SECRET : • 硬體安全模組(HSM)
  VM_SECRET :
  VM_SECRET : 風險: 改善但仍分散
}

state "容器化時代" as CONT <<era3>> {
  state "密碼管理方式" as CONT_SECRET
  CONT_SECRET : • 環境變數注入
  CONT_SECRET : • Docker Secrets
  CONT_SECRET : • 避免映像檔內硬編碼
  CONT_SECRET :
  CONT_SECRET : 風險: 需要編排工具支援
}

state "Kubernetes 時代" as K8S <<era4>> {
  state "密碼管理方式" as K8S_SECRET
  K8S_SECRET : • Secret API 物件
  K8S_SECRET : • RBAC 權限控制
  K8S_SECRET : • etcd 集中儲存
  K8S_SECRET : • 外部密鑰管理系統整合
  K8S_SECRET : • 加密靜態資料
  K8S_SECRET :
  K8S_SECRET : 優勢: 統一管理與存取控制

  state "安全增強選項" as K8S_ENHANCE
  K8S_ENHANCE : • Sealed Secrets
  K8S_ENHANCE : • External Secrets Operator
  K8S_ENHANCE : • HashiCorp Vault
  K8S_ENHANCE : • AWS Secrets Manager
  K8S_ENHANCE : • Azure Key Vault
}

[*] --> BM : 1970s-1990s
BM --> VM : 虛擬化技術\n資源隔離
VM --> CONT : 容器技術\n輕量化部署
CONT --> K8S : 容器編排\n自動化管理

note right of K8S
  Kubernetes 提供:
  • 統一的 Secret API
  • 細粒度的存取控制
  • 與外部密鑰庫整合
  • 自動化生命週期管理
end note

@enduml

分析解釋:

  1. Bare Metal 時代:密碼直接硬編碼或以明文儲存在配置檔案中,安全性極低,一旦系統被入侵,所有密碼立即暴露。

  2. 虛擬機器時代:開始使用加密配置檔案和專用密鑰管理工具,引入硬體安全模組(HSM)保護關鍵密碼,但密碼管理仍然分散在各個虛擬機器中。

  3. 容器化時代:透過環境變數注入密碼,Docker 提供了 Docker Secrets 機制,開始避免在容器映像檔中硬編碼密碼,但仍需要編排工具的支援才能有效管理。

  4. Kubernetes 時代:提供統一的 Secret API 物件,結合 RBAC 實現細粒度權限控制,透過 etcd 集中儲存並可啟用靜態資料加密,還可整合外部密鑰管理系統如 Vault、AWS Secrets Manager 等,實現企業級密碼管理。