返回文章列表

Kubernetes Helm 安全佈署實踐:資源限制、Secrets 管理與 RBAC 策略

深入探討 Helm 圖表中的安全最佳實踐,從資源限制設定、Secrets 安全處理到 RBAC 精細權限控管,提供台灣企業在 Kubernetes 環境中的完整安全佈署策略與實作範例。

Kubernetes 實務 DevSecOps 雲端原生資安

在 Kubernetes 環境中透過 Helm 進行應用程式佈署時,若未採取適當的安全措施,即便是最便利的工具也可能成為潛在的資安破口。現代企業面臨的威脅日益複雜,從資源濫用導致的服務中斷,到敏感資料外洩造成的商業損失,每一個環節都需要嚴謹的安全控管機制。本文將深入探討如何在 Helm 圖表中實作完整的安全最佳實踐,涵蓋資源限制的精確設定、Secrets 的安全處理流程,以及 RBAC 策略的細緻權限控管,確保您的 Kubernetes 叢集能夠在維持營運效率的同時,達到企業級的安全標準。

首先,我們會深入探討如何使用 LimitRange 與 ResourceQuota 這兩項核心機制來設定資源限制。這不僅能防止單一應用程式或命名空間過度消耗叢集資源,更能避免因資源競爭導致的服務中斷或效能瓶頸。接著,將詳細說明如何妥善處理 Helm 圖表中的敏感資訊,包含密碼、API 金鑰等 Secrets 資料,確保這些關鍵資訊不會以明文形式儲存或傳輸。最後,我們將介紹如何運用 RBAC 策略來管理不同使用者與 ServiceAccount 的權限,貫徹最小權限原則,降低內部與外部的安全風險。透過這些實作範例與組態說明,協助您在真實企業環境中建立穩固的安全基礎。

資源限制的策略性設定與實作

在 Helm 圖表中設定合理的資源限制,是防止單一應用程式耗盡整個叢集資源的根本措施。當多個團隊共享同一個 Kubernetes 叢集時,若缺乏適當的資源管制機制,單一應用程式的資源濫用可能導致其他服務效能下降,甚至引發連鎖性的服務中斷。Kubernetes 提供了兩項互補的資源限制機制:LimitRange 與 ResourceQuota,這兩者共同構成了從 Pod 層級到命名空間層級的完整資源管控體系。

LimitRange 允許您在特定範圍內設定細緻的資源限制規則,這些規則可以針對 CPU、記憶體或儲存空間進行精確控制。舉例而言,當您的應用程式需要使用 PersistentVolumeClaim 來儲存資料時,可以透過 LimitRange 設定單一 PVC 的儲存空間上限,防止某個應用程式無限制地申請儲存資源。這種限制不僅保護了叢集的整體資源池,更為應用程式開發者提供了明確的資源使用邊界。

ResourceQuota 則從更高層次進行資源管控,它允許您在命名空間層級設定資源使用的總量上限。不同於 LimitRange 的單一資源限制,ResourceQuota 是針對整個命名空間內所有工作負載的累計使用量進行管制。透過這種機制,您可以為不同團隊或專案分配合適的資源配額,確保每個命名空間都能在公平的基礎上使用叢集資源。此外,ResourceQuota 還能限制命名空間內 Secrets、ConfigMaps 等 Kubernetes 資源的總數量,從多個維度防止資源濫用。

# 這個 YAML 檔案示範如何建立 LimitRange 來限制單一 PVC 的儲存空間
# LimitRange 會強制執行資源使用的絕對最大值
# 使用者可以在 values.yaml 中設定預設值,但不得超過此限制

apiVersion: v1
kind: LimitRange
metadata:
  # LimitRange 物件的名稱,建議使用具有描述性的名稱
  name: limits-per-pvc
  # 指定此 LimitRange 作用的命名空間
  # 建議為每個專案或團隊建立獨立的命名空間
  namespace: production-apps
spec:
  # 定義限制的規則清單,可包含多個項目
  limits:
    # 對 PersistentVolumeClaim 類型資源的限制
    - type: PersistentVolumeClaim
      # max 定義資源可申請的最大值
      # 這裡限制單一 PVC 最多只能申請 4Gi 儲存空間
      max:
        storage: 4Gi
      # min 定義資源可申請的最小值
      # 可以防止過度保守的資源申請
      min:
        storage: 1Gi
      # defaultRequest 定義未明確指定時的預設申請值
      defaultRequest:
        storage: 2Gi
      # default 定義未明確指定時的預設限制值
      default:
        storage: 2Gi
# 這個 YAML 檔案示範如何建立 ResourceQuota 來限制整個命名空間的資源使用總量
# ResourceQuota 從命名空間層級防止資源過度消耗

apiVersion: v1
kind: ResourceQuota
metadata:
  # ResourceQuota 物件的名稱
  name: pod-and-pvc-quota
  # 指定此 ResourceQuota 作用的命名空間
  namespace: production-apps
spec:
  # hard 定義資源使用的硬性上限,不可超過
  hard:
    # 限制整個命名空間的 CPU 使用總量為 4 核心
    # 這是所有 Pod 的 limits.cpu 總和上限
    limits.cpu: "4"
    # 限制整個命名空間的記憶體使用總量為 8GiB
    # 這是所有 Pod 的 limits.memory 總和上限
    limits.memory: 8Gi
    # 限制整個命名空間的儲存申請總量為 20GiB
    # 這是所有 PVC 的 requests.storage 總和上限
    requests.storage: 20Gi
    # 限制 Secrets 的總數量為 10 個
    # 防止過多 Secrets 造成管理困難
    secrets: "10"
    # 限制 ConfigMaps 的總數量為 10 個
    configmaps: "10"
    # 限制 PersistentVolumeClaims 的總數量為 5 個
    persistentvolumeclaims: "5"
@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

node "Kubernetes 叢集" as cluster {
  node "production-apps 命名空間" as namespace {
    [ResourceQuota] as quota
    [LimitRange] as limitrange
    
    node "應用程式 Pod" as pod1 {
      [容器 1] as container1
    }
    
    node "應用程式 Pod" as pod2 {
      [容器 2] as container2
    }
    
    [PVC 1] as pvc1
    [PVC 2] as pvc2
    
    [Secrets] as secrets
    [ConfigMaps] as configmaps
  }
}

quota --> namespace : 限制命名空間總資源
limitrange --> namespace : 限制單一資源大小

quota --> pod1 : 限制 CPU/記憶體總和
quota --> pod2 : 限制 CPU/記憶體總和
quota --> pvc1 : 限制 storage 總和
quota --> pvc2 : 限制 storage 總和
quota --> secrets : 限制數量
quota --> configmaps : 限制數量

limitrange --> pvc1 : 限制單一 PVC 大小
limitrange --> pvc2 : 限制單一 PVC 大小

note right of quota
  ResourceQuota 從巨觀角度
  控制命名空間整體資源使用
end note

note left of limitrange
  LimitRange 從微觀角度
  控制單一資源實例大小
end note

@enduml

Secrets 的安全處理機制與最佳實踐

在 Helm 圖表中處理 Secrets 是資安管理中最為關鍵也最為複雜的環節。敏感資訊如資料庫密碼、API 金鑰、TLS 憑證等,若處理不當極易成為攻擊者的首要目標。傳統的錯誤做法是在 values.yaml 檔案中直接設定這些敏感值的預設內容,這不僅讓應用程式暴露在極高的風險中,更可能因為版本控制系統的提交記錄而永久留下敏感資訊的痕跡。

為了根本解決這個問題,Helm 提供了 required 函式來強制使用者必須提供敏感值。這個函式會在安裝或升級 Chart 時檢查指定值是否存在,若值為空或不符要求,將立即終止佈署流程並顯示明確的錯誤訊息。這種機制確保了敏感資訊必須由佈署人員明確提供,而非使用不安全的預設值。雖然 Helm 也提供了 randAlphaNum 函式來生成隨機字串,但這個函式在每次 Chart 升級時都會產生新的隨機值,可能導致資料庫密碼等需要持久性的憑證在升級後失效,因此不建議用於生產環境的敏感值設定。

當使用者在安裝 Chart 時提供敏感值,這些資訊應該被妥善儲存在 Kubernetes Secrets 物件中,而非 ConfigMaps。ConfigMaps 會以明文形式儲存資料,完全不適合用於存放憑證或其他敏感資訊。相對地,Secrets 會將內容進行 Base64 編碼,雖然這不是真正的加密,但至少提供了基本的安全性。更重要的是,Secrets 允許將內容以 tmpfs 的方式掛載到 Pod 的記憶體中,這意味著敏感資料不會被寫入磁碟,大大降低了資料外洩的風險。

對於 Chart 使用者而言,提供敏感值的方式也需要格外謹慎。雖然可以透過 --values 標誌從檔案讀取值,但這個檔案必須確保不會被提交至 Git 儲存庫或其他公開位置。為了避免敏感資訊暴露,建議使用 --set 標誌從命令列直接傳遞值,這樣可以減少中間檔案造成的風險。不過需要特別注意的是,命令列輸入可能會被記錄在 bash 歷史紀錄中,因此建議在執行後立即清理歷史紀錄或在私密環境中操作。

更進階的安全實踐是使用加密工具來保護包含敏感值的檔案。工具如 Mozilla SOPS、git-crypt 或 BlackBox 能夠將整個 values 檔案加密,只有持有正確金鑰的使用者才能解密並使用這些值。這種方法讓團隊可以安全地將加密的 values 檔案儲存在 Git 儲存庫中,同時確保未經授權的人員無法讀取敏感內容。對於企業級應用,專業的密碼管理解決方案如 HashiCorp Vault 或 CyberArk Conjur 提供了更為完整的安全機制,包括動態密碼生成、自動輪換與詳細的稽核記錄功能。

# 這個 YAML 檔案示範如何在 Helm Chart 中安全地處理敏感資訊
# 使用 required 函式強制使用者提供密碼,避免使用不安全的預設值

apiVersion: v1
kind: Secret
metadata:
  # Secret 物件的名稱,建議包含應用程式名稱以便識別
  name: {{ .Release.Name }}-database-credentials
  # Secret 所在的命名空間
  namespace: {{ .Release.Namespace }}
type: Opaque
data:
  # 使用 required 函式確保使用者必須提供密碼
  # 若未提供,安裝流程會立即終止並顯示錯誤訊息
  # b64enc 函式將字串轉換為 Base64 編碼,符合 Kubernetes Secret 格式要求
  password: {{ required "資料庫密碼為必填項目,請使用 --set db.password=YOUR_PASSWORD 提供" .Values.db.password | b64enc }}
  # 使用者名稱同樣建議由使用者提供,而非使用預設值
  username: {{ required "資料庫使用者名稱為必填項目" .Values.db.username | b64enc }}
# 這個 YAML 檔案示範如何從外部密碼管理服務動態取得敏感值
# 使用 Helm 的 lookup 函式在安裝時查詢已存在的 Secret

{{- if .Values.externalSecrets.enabled }}
apiVersion: v1
kind: Secret
metadata:
  name: {{ .Release.Name }}-external-api-key
  namespace: {{ .Release.Namespace }}
type: Opaque
data:
  # 使用 lookup 函式查詢外部 Secret 管理服務中的值
  # 這個範例查詢 HashiCorp Vault 注入的 Secret
  # 若找不到對應值,使用 required 函式強制提供
  api-key: {{ required "API Key is required when externalSecrets is enabled" (index (lookup "v1" "Secret" "vault-secrets" "external-api-credentials").data "api-key") | default "" | b64enc }}
{{- end }}
@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

actor "佈署人員" as deployer
participant "Helm CLI" as helm
participant "Chart 模板引擎" as template
participant "Kubernetes API" as k8s
participant "Secret 儲存" as etcd

deployer -> helm : 執行 helm install 並提供敏感值
helm -> template : 渲染 Secret 模板

template -> template : 執行 required 函式檢查
template -> template : 使用 b64enc 函式編碼

alt 值未提供
  template --> helm : 回傳錯誤訊息
  helm --> deployer : 顯示錯誤並終止安裝
else 值已提供
  template --> helm : 回傳編碼後的 Secret 定義
  helm -> k8s : 建立 Secret 物件
  
  k8s -> etcd : 儲存 Base64 編碼的敏感值
  etcd --> k8s : 確認儲存成功
  
  k8s -> deployer : 回報 Secret 建立完成
  
  deployer -> k8s : Pod 啟動並請求 Secret
  k8s -> etcd : 讀取 Secret 資料
  etcd --> k8s : 傳回編碼資料
  k8s -> k8s : 解碼並以 tmpfs 掛載至 Pod
  k8s --> deployer : 應用程式取得敏感值
end

note right of deployer
  敏感值絕不應該以
  明文形式出現在
  版本控制系統中
end note

note left of etcd
  etcd 中的 Secret 雖為
  Base64 編碼,但仍需
  啟用靜態加密保護
end note

@enduml

RBAC 策略的精細化設計與實作

Role-Based Access Control (RBAC) 是 Kubernetes 提供的核心權限管理機制,能夠協助企業實現精細化的存取控制策略。透過 RBAC,您可以定義角色(Role)與叢集角色(ClusterRole),並透過角色綁定(RoleBinding)與叢集角色綁定(ClusterRoleBinding)將這些權限指派給特定的使用者或服務帳戶(ServiceAccount)。這種機制讓企業能夠嚴格遵循最小權限原則,確保每個身份僅擁有完成工作所需的最小權限集合,從而大幅降低安全風險。

在 Helm 的使用情境中,RBAC 的設計需要考量兩個關鍵身份:首先是安裝 Helm Chart 的操作用戶,這通常與 Kubernetes 的使用者身份綁定;其次是執行工作負載的 Pod 所使用的服務帳戶。在大多數企業環境中,負責安裝 Chart 的人員會擁有較高的權限,但實際運行的應用程式則應該使用權限受限的服務帳戶。這種權限分離的設計是確保叢集安全的基本原則。

Kubernetes 叢集中的使用者和服務帳戶預設擁有最小權限,無法執行任何關鍵操作。要授予額外權限,必須明確定義角色並建立綁定關係。角色可以設定為單一命名空間範圍或整個叢集範圍,透過對應的綁定機制與使用者或服務帳戶建立關聯。雖然 Kubernetes 提供了多種內建角色,如 view、edit、admin 等,但企業應盡可能建立自訂角色,實現真正的最小權限原則。

舉例來說,假設我們開發的 guestbook 應用需要新增一項功能來查詢命名空間中的 Pod 後設資料,雖然可以使用內建的 view 角色來達成,但該角色同時也授予了 ConfigMaps、Deployments 等其他資源的讀取權限,這超出了應用程式的實際需求。為了最小化授予的存取級別,我們應該建立一個自訂政策,僅提供查詢 Pod 清單的必要權限。這種精細化的權限設計不僅提升了安全性,也讓權限管理更加透明且易於維護。

# 這個 YAML 檔案示範如何建立自訂的 RBAC 角色
# 此角色僅授予查詢 Pod 的權限,遵循最小權限原則

apiVersion: rbac.authorization.k8s.io/v1
# 使用 Role 而非 ClusterRole,限制在特定命名空間內
kind: Role
metadata:
  # 角色名稱應該具備描述性,表明其用途
  name: guestbook-pod-viewer
  # 角色作用的命名空間
  namespace: production-apps
# rules 定義角色擁有的權限規則
rules:
  # apiGroups 指定 Kubernetes API 群組
  # 空字串表示核心 API 群組
  - apiGroups: [""]
    # resources 指定可存取的資源類型
    resources: ["pods"]
    # verbs 指定允許執行的操作
    # 這裡只授予讀取相關操作
    verbs: ["get", "watch", "list"]
# 這個 YAML 檔案示範如何建立 ServiceAccount
# ServiceAccount 是 Pod 使用的身份認證機制

apiVersion: v1
kind: ServiceAccount
metadata:
  # ServiceAccount 名稱,與角色名稱保持一致性
  name: guestbook
  # ServiceAccount 所在的命名空間
  namespace: production-apps
# automountServiceAccountToken 控制是否自動掛載憑證
# 設為 false 可提升安全性,手動控制憑證掛載
automountServiceAccountToken: false
# 這個 YAML 檔案示範如何建立 RoleBinding
# 將角色與 ServiceAccount 綁定,授予實際權限

apiVersion: rbac.authorization.k8s.io/v1
# 使用 RoleBinding 將命名空間內的角色綁定
kind: RoleBinding
metadata:
  # 綁定名稱,建議包含角色與主體資訊
  name: guestbook-pod-viewers
  # 綁定所在的命名空間,需與角色相同
  namespace: production-apps
# subjects 定義要授予權限的對象
subjects:
  # kind 指定主體類型
  - kind: ServiceAccount
    # name 指定 ServiceAccount 名稱
    name: guestbook
    # namespace 指定 ServiceAccount 所在命名空間
    namespace: production-apps
# roleRef 定義要綁定的角色
roleRef:
  # kind 指定角色類型
  kind: Role
  # name 指定角色名稱
  name: guestbook-pod-viewer
  # apiGroup 指定角色的 API 群組
  apiGroup: rbac.authorization.k8s.io
@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"
skinparam defaultFontSize 16
skinparam minClassWidth 100

actor "叢集管理員" as admin
participant "kubectl CLI" as kubectl
participant "Kubernetes RBAC API" as rbac
participant "Pod" as pod
participant "ServiceAccount" as sa

admin -> kubectl : 建立自訂角色 Role
kubectl -> rbac : POST /apis/rbac.authorization.k8s.io/v1/namespaces/production-apps/roles
rbac -> rbac : 驗證角色定義
rbac --> kubectl : 回報角色建立成功

admin -> kubectl : 建立 ServiceAccount
kubectl -> rbac : POST /api/v1/namespaces/production-apps/serviceaccounts
rbac --> kubectl : 回報 ServiceAccount 建立成功

admin -> kubectl : 建立 RoleBinding
kubectl -> rbac : POST /apis/rbac.authorization.k8s.io/v1/namespaces/production-apps/rolebindings
rbac -> rbac : 建立角色與主體的關聯
rbac --> kubectl : 回報綁定建立成功

pod -> sa : 使用 ServiceAccount 身份認證
sa -> rbac : 請求查詢 Pod 列表
rbac -> rbac : 檢查角色權限
rbac -> rbac : 驗證綁定關係

alt 權限不足
  rbac --> pod : 拒絕存取 (403 Forbidden)
else 權限符合
  rbac -> rbac : 查詢 Pod 資源
  rbac --> pod : 回傳 Pod 列表
end

note right of admin
  建立最小權限角色
  僅授予必要操作
end note

note left of rbac
  RBAC 檢查流程:
  1. 驗證身份
  2. 查找角色綁定
  3. 檢查角色規則
  4. 決定允許或拒絕
end note

@enduml

安全性檢查與驗證流程

在 Helm Chart 開發與佈署的生命週期中,建立完整的驗證機制是確保安全性的最後一道防線。Kubernetes 提供了強大的授權檢查功能,讓叢集管理員能夠在不實際執行操作的情況下,驗證特定使用者或服務帳戶是否擁有執行某項操作的權限。這種能力對於除錯 RBAC 政策、驗證權限設定特別有價值。

kubectl auth can-i 指令是進行這類檢查的核心工具。透過這個指令,您可以模擬特定身份的授權檢查流程,系統會回傳簡潔的 yes 或 no 答案,表明該身份是否被允許執行指定的操作。這種模擬檢查不會在叢集中留下任何痕跡,也不會觸發實際的 API 操作,因此是安全且高效的驗證方式。

為了完整驗證 RBAC 設定是否正確運作,建議在開發環境中建立一個完整的測試流程。這個流程應該包含建立測試命名空間、部署最小可用的應用程式、驗證服務帳戶的權限範圍,以及確認應用程式能夠在受限的權限下正常運作。透過這種系統性的驗證,可以在正式佈署前發現潛在的權限問題,避免在生產環境中發生權限不足導致的功能異常。

#!/usr/bin/env bash
# 這個腳本提供完整的 RBAC 設定驗證流程
# 從環境準備到權限驗證,確保安全設定正確運作

# 設定錯誤處理,任何錯誤都會立即終止腳本執行
set -euo pipefail

# 定義顏色輸出函數,用於顯示不同等級的訊息
# 這些顏色定義符合多數終端機的 ANSI 色彩碼
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m' # No Color

# 步驟 1:準備測試環境
# 建立獨立的命名空間,避免影響現有應用程式
# chapter9 作為測試用途的命名空間名稱

echo "步驟 1:建立測試命名空間 chapter9..."
kubectl create ns chapter9

# 步驟 2:建立自訂 RBAC 角色
# 建立名為 guestbook-pod-viewer 的角色
# 僅授予查詢 Pod 的權限,遵循最小權限原則
# --resource=pods 指定可操作資源
# --verb=get,list 指定允許的操作:取得與列出

echo "步驟 2:建立自訂角色 guestbook-pod-viewer..."
kubectl create role guestbook-pod-viewer \
  --resource=pods \
  --verb=get,list \
  -n chapter9

# 步驟 3:建立 ServiceAccount
# ServiceAccount 是 Pod 使用的身份認證機制
# guestbook 服務帳戶將被綁定到自訂角色

echo "步驟 3:建立 ServiceAccount guestbook..."
kubectl create sa guestbook -n chapter9

# 步驟 4:建立 RoleBinding
# 將角色與服務帳戶綁定,授予實際權限
# --role 指定要綁定的角色名稱
# --serviceaccount 指定目標服務帳戶,格式為 namespace:serviceaccount

echo "步驟 4:建立 RoleBinding guestbook-pod-viewers..."
kubectl create rolebinding guestbook-pod-viewers \
  --role=guestbook-pod-viewer \
  --serviceaccount=chapter9:guestbook \
  -n chapter9

# 步驟 5:驗證權限設定
# 使用 kubectl auth can-i 檢查服務帳戶權限
# --as 標誌模擬特定身份進行授權檢查
# 這個測試驗證 serviceaccount 是否可以列出 Pod

echo "步驟 5:驗證 ServiceAccount 權限..."
if kubectl auth can-i list pods \
  --as=system:serviceaccount:chapter9:guestbook \
  -n chapter9; then
  echo -e "${GREEN}✓ ServiceAccount 具有 list pods 權限${NC}"
else
  echo -e "${RED}✗ 權限驗證失敗${NC}"
  exit 1
fi

# 步驟 6:測試拒絕未授權操作
# 確認服務帳戶無法執行未授權操作
# 這個測試檢查 serviceaccount 是否無法刪除 Pod

echo "步驟 6:測試未授權操作被拒絕..."
if ! kubectl auth can-i delete pods \
  --as=system:serviceaccount:chapter9:guestbook \
  -n chapter9; then
  echo -e "${GREEN}✓ ServiceAccount 正確地沒有 delete pods 權限${NC}"
else
  echo -e "${RED}✗ 安全性設定錯誤,不應具有 delete 權限${NC}"
  exit 1
fi

# 步驟 7:安裝測試應用程式
# 使用 Helm 安裝 guestbook 應用
# --set serviceAccount.name 指定使用自訂服務帳戶
# --set serviceAccount.create=false 避免 Helm 建立新的服務帳戶

echo "步驟 7:安裝測試應用程式 my-guestbook..."
helm install my-guestbook \
  Learn-Helm/helm-charts/charts/guestbook \
  --set serviceAccount.name=guestbook \
  --set serviceAccount.create=false \
  -n chapter9

# 步驟 8:等待 Pod 啟動
# 使用 kubectl wait 等待 Pod 進入 Ready 狀態
# --for=condition=Ready 指定等待條件
# --timeout=300s 設定超時時間為 300 秒

echo "步驟 8:等待 Pod 啟動..."
kubectl wait --for=condition=Ready pod \
  -l app.kubernetes.io/name=guestbook \
  -n chapter9 \
  --timeout=300s

# 步驟 9:驗證應用程式功能
# 檢查應用程式是否能正常存取服務
# 這個測試驗證在受限權限下應用程式仍能正常運作

echo "步驟 9:驗證應用程式功能..."
POD_NAME=$(kubectl get pods -l app.kubernetes.io/name=guestbook \
  -n chapter9 -o jsonpath='{.items[0].metadata.name}')

if kubectl exec -n chapter9 $POD_NAME -- \
  curl -s http://localhost:8080 | grep -q "Guestbook"; then
  echo -e "${GREEN}✓ 應用程式在受限權限下正常運作${NC}"
else
  echo -e "${RED}✗ 應用程式功能驗證失敗${NC}"
fi

# 步驟 10:清理測試環境
# 刪除測試應用程式與命名空間
# 保持環境整潔

echo "步驟 10:清理測試環境..."
helm uninstall my-guestbook -n chapter9
kubectl delete ns chapter9

echo "所有測試完成!RBAC 設定驗證成功。"

# 額外提示:顯示如何使用加密工具
echo
echo "額外提示:在生產環境中,建議使用 SOPS 加密 sensitive values 檔案:"
echo "  $ sops --encrypt --in-place secrets.yaml"
echo "  $ helm install my-app . --values secrets.yaml"

安全性強化的進階考量

在企業級 Kubernetes 環境中,僅依賴基本的 RBAC 與資源限制往往不足以應對複雜的安全威脅。為了建構更為堅固的安全防禦體系,需要從多個層面進行強化。服務帳戶權杖的自動掛載機制就是一個需要特別關注的安全細節。雖然預設情況下 Kubernetes 會自動將 ServiceAccount 的權杖掛載到 Pod 中,但這個機制在某些情境下可能成為安全隱患。對於不需要與 Kubernetes API 伺服器通訊的應用程式,建議在 ServiceAccount 定義中將 automountServiceAccountToken 設為 false,並在 Pod 層級明確控制憑證的掛載行為。

命名空間層級的資源隔離是另一個重要的安全層。透過 NetworkPolicy 的精細設定,可以限制 Pod 之間的網路通訊,防止橫向移動攻擊。當某個應用程式遭到入侵時,嚴格的網路政策能夠有效阻止攻擊者存取叢集內的其他服務。這種零信任網路模型與 RBAC 的權限控管相輔相成,共同建構了縱深防禦的安全架構。

在權限管理方面,除了傳統的 RBAC 角色定義外,現代的 Kubernetes 叢集還可以整合動態權限管理系統。例如,透過 Open Policy Agent (OPA) 與 Gatekeeper 的整合,可以在 API 請求層級實施更為複雜的政策檢查。這種方式允許企業定義高階安全規則,如強制所有容器映像必須來自信任的註冊中心,或禁止具有特權模式的 Pod 被建立。這類動態政策引擎彌補了靜態 RBAC 規則的不足,提供了更靈活且強大的安全管控能力。

# 這個 YAML 檔案示範如何建立 NetworkPolicy 強化網路隔離
# NetworkPolicy 與 RBAC 共同構成縱深防禦

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  # 政策名稱,描述其用途
  name: guestbook-isolation
  # 政策作用的命名空間
  namespace: production-apps
spec:
  # podSelector 選擇要套用政策的 Pod
  # 這個範例選擇所有標籤為 app=guestbook 的 Pod
  podSelector:
    matchLabels:
      app: guestbook
  # policyTypes 定義政策類型
  policyTypes:
    - Ingress
    - Egress
  # ingress 定義入站規則
  ingress:
    # 允許來自 frontend 的連線
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080
  # egress 定義出站規則
  egress:
    # 允許連線到資料庫
    - to:
        - podSelector:
            matchLabels:
              app: database
      ports:
        - protocol: TCP
          port: 5432
    # 允許連線到 DNS 服務
    - to:
        - namespaceSelector:
            matchLabels:
              name: kube-system
        - podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
@startuml
!define PLANTUML_FORMAT svg
!theme _none_

skinparam dpi auto
skinparam shadowing false
skinparam linetype ortho
skinparam roundcorner 5
skinparam defaultFontName "Microsoft JhengHei UI"

skinparam defaultFontSize 16
skinparam minClassWidth 100

node "Kubernetes 叢集" as cluster {
  node "production-apps 命名空間" as ns {
    [RBAC 策略] as rbac
    [NetworkPolicy] as netpol
    
    [guestbook Pod] as app
    [frontend Pod] as frontend
    [database Pod] as db
    
    [ServiceAccount] as sa
  }
}

node "kube-system 命名空間" as kube {
  [DNS 服務] as dns
}

sa --> app : 身份認證
rbac --> sa : 授予查詢 Pod 權限

netpol --> app : 限制網路流量
netpol --> frontend : 允許前端連線
netpol --> db : 允許資料庫連線

app --> db : 允許的出站連線
app --> dns : 允許的 DNS 查詢
frontend --> app : 允許的入站連線

note right of rbac
  RBAC 控制 API 存取權限
  防止未授權操作
end note

note left of netpol
  NetworkPolicy 控制網路流量
  防止橫向移動攻擊
end note

@enduml

台灣企業導入建議與實務考量

在台灣的企業環境中導入 Helm 安全實踐時,必須充分考量本地的法規要求與產業特性。金融業受到金管會的嚴格監管,對於容器化應用程式的安全性有極高的要求,必須確保所有 Helm Chart 都經過完整的簽章驗證與安全掃描。高科技製造業則面臨複雜的供應鏈資安挑戰,必須防止惡意程式碼透過 Helm Chart 滲透至生產環境,影響關鍵製程。

建議企業採取分階段導入策略。第一階段應從建立內部的 Helm Chart 開發規範開始,強制要求所有 Chart 必須包含資源限制設定與安全上下文組態。第二階段將 Secrets 管理整合至現有的密碼管理系統,如導入 HashiCorp Vault 或採用雲端原生的密碼管理服務。第三階段則建立企業級的 Helm 儲存庫,強制執行簽章驗證政策,並整合自動化的安全掃描流程。

同時,應建立跨部門的雲端安全工作小組,納入開發、維運、資安與法務等相關單位,共同制定符合企業實際需求的 Helm 安全政策。透過這種協作模式,才能確保技術方案不僅能提升安全性,更能符合企業的營運需求與法規遵循義務。對於正在進行數位轉型的傳統產業,建議先從非關鍵的測試環境開始,逐步建立團隊的技術能力與操作經驗,再擴展至正式生產環境。

#!/usr/bin/env bash
# 這個腳本示範台灣企業如何進行 Helm Chart 安全檢查清單
# 符合金融業與高科技製造業的法規要求

# 設定錯誤處理
set -euo pipefail

# 定義檢查項目陣列
declare -a CHECKLIST_ITEMS

# 檢查函數:驗證 Chart 是否包含資源限制
check_resource_limits() {
  echo "檢查 Chart 是否包含資源限制設定..."
  
  # 檢查 values.yaml 中是否有 resources 區段
  if grep -q "resources:" values.yaml 2>/dev/null; then
    # 檢查是否包含 limits 與 requests
    if grep -A5 "resources:" values.yaml | grep -q "limits:" && \
       grep -A5 "resources:" values.yaml | grep -q "requests:"; then
      echo "✓ 資源限制設定完整"
      CHECKLIST_ITEMS+=("PASS: 資源限制")
      return 0
    else
      echo "✗ 資源限制設定不完整,缺少 limits 或 requests"
      CHECKLIST_ITEMS+=("FAIL: 資源限制")
      return 1
    fi
  else
    echo "✗ 未設定資源限制,不符合企業安全規範"
    CHECKLIST_ITEMS+=("FAIL: 資源限制")
    return 1
  fi
}

# 檢查函數:驗證 Secret 處理是否安全
check_secret_handling() {
  echo "檢查 Chart 是否安全處理 Secrets..."
  
  # 檢查是否使用 required 函式
  if grep -r "required" templates/ | grep -q "secret\|password\|key"; then
    echo "✓ 使用 required 函式強制提供敏感值"
    CHECKLIST_ITEMS+=("PASS: Secret 處理")
    return 0
  else
    echo "✗ 未正確使用 required 函式處理 Secrets"
    CHECKLIST_ITEMS+=("FAIL: Secret 處理")
    return 1
  fi
}

# 檢查函數:驗證 RBAC 設定
check_rbac_configuration() {
  echo "檢查 Chart 是否包含適當的 RBAC 設定..."
  
  # 檢查是否存在 RBAC 相關模板檔案
  if [ -f "templates/rbac.yaml" ] || [ -f "templates/role.yaml" ]; then
    # 檢查是否遵循最小權限原則
    if grep -q "verbs:" templates/rbac.yaml 2>/dev/null && \
       ! grep "verbs:" templates/rbac.yaml | grep -q "\*"; then
      echo "✓ RBAC 設定遵循最小權限原則"
      CHECKLIST_ITEMS+=("PASS: RBAC 設定")
      return 0
    else
      echo "✗ RBAC 設定使用過於寬鬆的權限"
      CHECKLIST_ITEMS+=("FAIL: RBAC 設定")
      return 1
    fi
  else
    echo "✗ 缺少 RBAC 設定檔案"
    CHECKLIST_ITEMS+=("FAIL: RBAC 設定")
    return 1
  fi
}

# 檢查函數:驗證 NetworkPolicy
check_network_policy() {
  echo "檢查 Chart 是否包含網路隔離政策..."
  
  if [ -f "templates/networkpolicy.yaml" ]; then
    echo "✓ 包含 NetworkPolicy 設定"
    CHECKLIST_ITEMS+=("PASS: 網路隔離")
    return 0
  else
    echo "⚠ 缺少 NetworkPolicy,建議新增以強化安全"
    CHECKLIST_ITEMS+=("WARN: 網路隔離")
    return 1
  fi
}

# 檢查函數:驗證容器安全上下文
check_security_context() {
  echo "檢查 Chart 是否設定安全上下文..."
  
  if grep -r "securityContext:" templates/ | grep -q "runAsNonRoot\|runAsUser"; then
    echo "✓ 設定容器安全上下文"
    CHECKLIST_ITEMS+=("PASS: 安全上下文")
    return 0
  else
    echo "✗ 未設定安全上下文,容器可能以 root 執行"
    CHECKLIST_ITEMS+=("FAIL: 安全上下文")
    return 1
  fi
}

# 主執行流程
echo "================================================"
echo "  台灣企業 Helm Chart 安全檢查清單"
echo "================================================"
echo ""

# 依序執行各項檢查
check_resource_limits
echo ""

check_secret_handling
echo ""

check_rbac_configuration
echo ""

check_network_policy
echo ""

check_security_context
echo ""

# 產生檢查報告
echo "================================================"
echo "檢查結果摘要:"

# 統計各類結果的數量
PASS_COUNT=0
WARN_COUNT=0
FAIL_COUNT=0

for result in "${CHECKLIST_ITEMS[@]}"; do
  if [[ $result == PASS:* ]]; then
    ((PASS_COUNT++))
  elif [[ $result == WARN:* ]]; then
    ((WARN_COUNT++))
  elif [[ $result == FAIL:* ]]; then
    ((FAIL_COUNT++))
  fi
done

# 計算合規率
TOTAL=$((PASS_COUNT + WARN_COUNT + FAIL_COUNT))
if [ $TOTAL -gt 0 ]; then
  COMPLIANCE_RATE=$((PASS_COUNT * 100 / TOTAL))
else
  COMPLIANCE_RATE=0
fi

echo "通過項目:$PASS_COUNT 項"
echo "警告項目:$WARN_COUNT 項"
echo "失敗項目:$FAIL_COUNT 項"
echo "合規率:$COMPLIANCE_RATE%"

# 根據檢查結果提供建議
echo ""
echo "建議措施:"

if [ $FAIL_COUNT -gt 0 ]; then
  echo "- 立即修正所有失敗項目,否則不應部署至生產環境"
fi

if [ $WARN_COUNT -gt 0 ]; then
  echo "- 優先處理警告項目,強化整體安全架構"
fi

if [ $COMPLIANCE_RATE -ge 80 ]; then
  echo "- 合規率良好,可考慮部署至測試環境驗證"
elif [ $COMPLIANCE_RATE -ge 60 ]; then
  echo "- 合規率尚可,建議完成所有修正後再部署"
else
  echo "- 合規率偏低,需要大幅改善安全設定"
fi

echo ""
echo "注意:本檢查清單符合台灣金融業與高科技製造業資安規範要求。"
echo "實際部署前請依據企業內部政策進行完整資安審查。"

# 根據結果決定結束碼
if [ $FAIL_COUNT -gt 0 ]; then
  echo ""
  echo -e "${RED}安全檢查未通過,請優先處理失敗項目!${NC}"
  exit 1
else
  echo ""
  echo -e "${GREEN}安全檢查通過,符合基本安全要求。${NC}"
  exit 0
fi

總結

在 Kubernetes 生態系中,Helm 作為強大的應用程式管理工具,其安全性實踐直接影響整個叢集的資安狀態。透過本文探討的資源限制設定、Secrets 安全處理機制與 RBAC 精細化策略,企業能夠在建構自動化佈署流程的同時,維持高標準的安全防護。這些實踐不僅是技術上的必要措施,更是現代雲端原生應用資安治理的重要基石。

隨著 Kubernetes 生態系的持續演進,Helm 的安全性實踐也將面臨新的挑戰與機遇。政策即代碼(Policy as Code)的概念將更加普及,透過 OPA 與 Gatekeeper 這類工具,企業能夠在叢集層級強制執行安全規範,確保每個 Helm Chart 都符合組織的安全標準。同時,Supply Chain Security 的議題將日益重要,Helm Chart 的簽章驗證與來源可追溯性將成為企業導入的必要條件。

對於台灣的企業而言,結合在地法規要求與國際安全標準,建立符合本地產業特性的 Helm 安全實踐框架,將是提升整體資安防護能力的關鍵一步。透過持續的教育訓練、流程優化與技術升級,企業能夠在享受雲端原生技術便利性的同時,有效管控安全風險,為數位轉型奠定堅實的資安基礎。