在現代雲端原生技術架構的演進歷程中,Kubernetes 已經成為容器編排領域的事實標準,其強大的自動化部署、彈性擴展與自我修復能力,為企業數位轉型提供了堅實的技術基礎。然而,隨著容器化技術在企業環境中的大規模採用,安全性議題也從邊緣關注點躍升為核心治理要求。Pod 作為 Kubernetes 生態系統中最基本的部署與執行單元,其安全配置的完善程度直接決定了整個叢集的防護能力與攻擊面範圍。
PodSecurityPolicy 作為 Kubernetes 原生提供的安全治理機制,透過精細化的屬性控制與強制准入驗證,為叢集管理員提供了一套完整的工具集,能夠從根本上限制 Pod 的權限範圍,防止特權容器逃逸、主機資源濫用、敏感資料洩露等常見的安全風險。這種機制的設計理念體現了縱深防禦(Defense in Depth)的安全架構思想,透過多層次的控制點建立起堅固的安全邊界。
本文將系統化地探討 Kubernetes Pod 安全治理的完整體系,從 PodSecurityPolicy 的核心概念與架構原理出發,深入剖析各個關鍵屬性的技術細節與配置策略。接著將介紹如何透過 RBAC(Role-Based Access Control)機制將安全策略與特定的服務帳號繫結,實現精細化的權限管理。為了簡化策略配置的複雜度,我們將詳細說明 kube-psp-advisor 自動化工具的使用方法,展示如何快速生成符合實際工作負載需求的安全策略。
在容器映像安全層面,我們將探討「左移安全」(Shift Left Security)在 DevSecOps 實踐中的重要性,說明如何透過 Anchore Engine 建立完整的映像漏洞掃描流程,並將其無縫整合到 CI/CD 管道中。透過在開發與建置階段就發現並修復安全漏洞,能夠大幅降低生產環境的風險暴露與修復成本。最後,我們將分享制定有效掃描策略的實務經驗,以及如何建立符合企業需求的漏洞管理流程。
Kubernetes Pod 安全治理的架構基礎
在深入探討 PodSecurityPolicy 的技術細節之前,必須先理解 Kubernetes 安全治理的整體架構與運作機制。Kubernetes 的安全模型建立在多層防護的設計理念之上,從 API 認證與授權、准入控制(Admission Control),到執行期的資源隔離與監控,每個層次都提供了相應的安全控制機制。PodSecurityPolicy 正是准入控制層中的關鍵元件,負責在 Pod 建立之前驗證其安全配置是否符合叢集的安全要求。
PodSecurityPolicy 的運作流程涉及多個核心元件的協同合作。當使用者或控制器提交 Pod 建立請求到 API Server 時,請求會先經過認證(Authentication)階段驗證請求者的身份,接著進入授權(Authorization)階段確認請求者是否有權執行該操作。通過這兩個階段後,請求會進入准入控制(Admission Control)階段,這是 PodSecurityPolicy 發揮作用的關鍵環節。
在准入控制階段,PodSecurityPolicy Admission Controller 會檢查提交的 Pod 規格是否符合已授權給請求者的 PodSecurityPolicy。這個檢查過程不僅驗證 Pod 的配置是否違反安全策略,還會根據策略定義的預設值自動為 Pod 補充缺失的安全相關設定。例如,如果策略規定必須以非 root 使用者執行,但 Pod 規格中沒有明確指定使用者 ID,Admission Controller 會根據策略配置的預設值自動設定。
需要特別說明的是,PodSecurityPolicy 在 Kubernetes 的版本演進過程中經歷了重要的變化。在 Kubernetes 1.21 版本中,PodSecurityPolicy 被正式標記為棄用(Deprecated),並在 1.25 版本中完全移除。取而代之的是更簡化的 Pod Security Admission(PSA)控制器,它定義了三個標準化的安全等級:Privileged(特權模式,無限制)、Baseline(基礎安全要求)與 Restricted(嚴格限制)。這個演進反映了 Kubernetes 社群對於簡化安全配置、提升可用性的努力。
然而,理解 PodSecurityPolicy 的設計理念與實作機制仍然具有重要的實務價值。首先,許多企業環境仍在使用 Kubernetes 1.24 或更早的版本,PodSecurityPolicy 依然是這些環境中的主要安全控制機制。其次,PodSecurityPolicy 的設計思想與配置方法為理解 Pod Security Standards 提供了堅實的基礎。最後,許多企業已經建立了基於 PodSecurityPolicy 的安全治理體系,需要平滑遷移到新的機制,因此深入理解舊機制的運作方式對於制定遷移策略至關重要。
@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 User
participant "API Server" as API
participant "認證系統" as Auth
participant "授權系統" as Authz
participant "准入控制器" as Admission
participant "PSP Controller" as PSP
database "etcd" as ETCD
User -> API : 提交 Pod 建立請求
activate API
API -> Auth : 驗證使用者身份
activate Auth
Auth --> API : 身份驗證通過
deactivate Auth
API -> Authz : 檢查操作權限
activate Authz
Authz --> API : 權限驗證通過
deactivate Authz
API -> Admission : 准入控制處理
activate Admission
Admission -> PSP : 查找授權的 PSP
activate PSP
alt 找到符合的 PSP
PSP -> PSP : 驗證 Pod 安全配置
alt 配置符合策略要求
PSP -> PSP : 補充預設值
PSP --> Admission : 驗證通過
deactivate PSP
Admission --> API : 准許建立
deactivate Admission
API -> ETCD : 持久化 Pod 資源
activate ETCD
ETCD --> API : 儲存完成
deactivate ETCD
API --> User : 回傳建立成功
else 配置違反策略
PSP --> Admission : 驗證失敗
deactivate PSP
Admission --> API : 拒絕建立
deactivate Admission
API --> User : 回傳錯誤訊息\n(違反安全策略)
end
else 無授權的 PSP
PSP --> Admission : 無可用策略
deactivate PSP
Admission --> API : 拒絕建立
deactivate Admission
API --> User : 回傳權限不足錯誤
end
deactivate API
@enduml上述流程圖清楚展示了 PodSecurityPolicy 在 Pod 建立過程中的作用時機與驗證邏輯。從中可以看出,安全策略的驗證是一個嚴格的門檻,只有同時通過身份認證、權限授權與安全策略驗證的 Pod 才能成功建立。這種多層防護的設計確保了叢集的安全性,但也要求管理員必須正確配置 RBAC 權限與 PodSecurityPolicy 的繫結關係,否則可能導致合法的工作負載無法部署。
PodSecurityPolicy 核心屬性的深度解析
PodSecurityPolicy 透過豐富的屬性集合提供了全面的安全控制能力,每個屬性都針對特定的安全面向提供精細化的管控機制。深入理解這些屬性的技術內涵與安全意義,是制定有效安全策略的基礎。以下將逐一剖析這些核心屬性的功能特性、配置方法,以及在實務場景中的應用策略。
根檔案系統唯讀控制
readOnlyRootFilesystem 屬性用於強制容器以唯讀方式掛載根檔案系統。在預設情況下,容器內的程序可以對根檔案系統進行讀寫操作,這為攻擊者提供了在容器內植入惡意程式、修改系統檔案或建立持久化後門的機會。透過將根檔案系統設定為唯讀,可以有效限制攻擊者的活動空間,即使成功入侵容器,也無法在檔案系統中留下持久化的痕跡。
這個設定特別適合無狀態應用程式,因為這類應用通常不需要在本地檔案系統中儲存資料,所有狀態資訊都保存在外部資料庫或快取系統中。對於需要寫入暫存檔案的應用程式,可以透過掛載 emptyDir 或 tmpfs 類型的 Volume 來提供可寫入的暫存空間,這些 Volume 在容器重啟時會自動清理,不會造成持久化的安全風險。
使用者與群組身份控制
runAsUser 與 runAsGroup 屬性共同控制容器程序的執行身份。在 Linux 系統中,使用者 ID(UID)0 代表 root 使用者,擁有系統的最高權限,可以執行任何操作而不受限制。大多數容器映像預設以 root 使用者執行,這在安全性上是一個巨大的隱患。當攻擊者透過應用程式漏洞取得容器內的程式碼執行能力時,如果容器以 root 身份執行,攻擊者就獲得了容器內的完整控制權,並可能進一步嘗試容器逃逸攻擊。
透過 runAsUser 屬性的 MustRunAsNonRoot 規則,可以強制要求容器必須以非 root 使用者執行。這個規則會在 Pod 建立時檢查容器的使用者設定,如果指定的 UID 為 0,API Server 會拒絕建立請求。更進一步,可以使用 MustRunAs 規則配合 ranges 參數,限定容器只能使用特定範圍內的 UID,例如 1000 到 65535 之間。這種限制可以防止不同租戶的容器使用相同的 UID,降低跨容器的權限提升風險。
runAsGroup 屬性的功能與 runAsUser 類似,但針對的是群組 ID(GID)。在 Linux 的檔案權限模型中,群組 ID 決定了程序對特定群組所有檔案的存取權限。透過限制可使用的群組 ID,可以進一步強化存取控制,確保容器內的程序只能存取特定群組擁有的資源,無法跨群組存取其他容器或主機的敏感資料。
權限提升控制機制
allowPrivilegeEscalation 屬性控制容器內的程序是否可以透過 setuid 或 setgid 機制取得比父程序更高的權限。在 Linux 系統中,某些可執行檔案可以設定 setuid 或 setgid 位元,當普通使用者執行這些檔案時,程序會以檔案擁有者的身份執行,而不是啟動程序的使用者身份。這個機制在正常情況下用於讓普通使用者執行需要特殊權限的操作,例如 passwd 命令需要以 root 權限修改密碼檔案。
然而,在容器環境中,這個機制可能被攻擊者利用來提升權限。如果攻擊者能夠在容器內放置一個帶有 setuid 位元的惡意程式,就可能透過執行該程式取得更高的權限。將 allowPrivilegeEscalation 設定為 false 可以完全禁止這種權限提升,即使容器內存在 setuid 程式,執行時也不會提升權限。這是防禦權限提升攻擊的重要防線,建議在安全要求較高的環境中將此屬性設為 false。
主機路徑掛載限制
allowedHostPaths 屬性用於限制 Pod 可以掛載的主機檔案系統路徑。hostPath 類型的 Volume 允許容器直接存取主機的檔案系統,這在某些場景下是必要的,例如需要存取主機的裝置檔案或日誌目錄。然而,不受限制的 hostPath 掛載是極大的安全風險,攻擊者可能透過掛載敏感目錄(如 /etc、/var、/root)來讀取系統組態、竊取憑證或修改系統檔案。
透過 allowedHostPaths 屬性,可以精確指定允許掛載的路徑範圍。這個屬性接受一個路徑列表,每個路徑可以額外指定 readOnly 選項。例如,可以允許掛載 /var/log 目錄用於日誌收集,但限制為唯讀模式,防止容器修改或刪除日誌檔案。如果完全不設定這個屬性,則允許掛載任意主機路徑,這在多租戶環境中是不可接受的。
Volume 類型白名單控制
volumes 屬性定義了 Pod 可以使用的 Volume 類型白名單。Kubernetes 支援多種 Volume 類型,每種類型提供不同的儲存來源與特性。某些 Volume 類型存在較高的安全風險,例如 hostPath 允許直接存取主機檔案系統,flexVolume 允許執行任意的外掛程式。透過限制可使用的 Volume 類型,可以縮小攻擊面,防止不安全的儲存配置。
在制定 Volume 類型白名單時,建議遵循最小權限原則,只允許業務實際需要的類型。對於大多數無狀態應用程式,通常只需要 configMap(用於注入組態)、secret(用於注入密碼)、emptyDir(用於暫存儲存)與 persistentVolumeClaim(用於持久化資料)就足夠了。禁止使用 hostPath 可以防止容器直接存取主機檔案系統,大幅提升叢集的安全性。
Linux 能力管理
Linux Capabilities 是一種精細化的權限控制機制,將傳統 root 使用者的無限權限拆分為多個獨立的能力單元。例如,CAP_NET_ADMIN 能力允許程序管理網路設定,CAP_SYS_TIME 能力允許修改系統時間。透過 requiredDropCapabilities、allowedCapabilities 與 defaultAddCapabilities 等屬性,可以精確控制容器可以使用的 Linux 能力。
最安全的配置是透過 requiredDropCapabilities 強制移除所有預設能力(設定為 ALL),然後根據應用程式的實際需求,透過 allowedCapabilities 選擇性地允許特定能力。這種白名單模式確保容器只擁有執行業務邏輯所需的最小權限集合。例如,如果應用程式需要監聽小於 1024 的特權連接埠,可以只允許 CAP_NET_BIND_SERVICE 能力,而不需要完整的 root 權限。
SELinux 與 AppArmor 安全模組
seLinux 與 appArmor 屬性分別對應 Linux 核心提供的兩種強制存取控制(Mandatory Access Control, MAC)機制。SELinux 透過安全標籤與策略規則限制程序對系統資源的存取,即使程序以 root 身份執行,也必須遵守 SELinux 策略的限制。這提供了額外的安全層,防止即使成功提權的攻擊者也無法任意存取系統資源。
在 Red Hat Enterprise Linux、CentOS 與 Fedora 等發行版中,SELinux 是預設啟用的安全機制。透過為容器設定適當的 SELinux 標籤,可以限制容器只能存取特定類型的檔案與資源。AppArmor 則是 Ubuntu 與 Debian 系列發行版採用的安全模組,透過設定檔定義程式可以執行的操作。這兩種機制在不同的 Linux 發行版中擇一使用,但都提供了相似的安全保護能力。
建立企業級安全策略的實務配置
理解了各個屬性的技術內涵後,接下來將透過完整的實務範例展示如何建立一個適合企業環境的 PodSecurityPolicy。這個策略採用限制性較強的配置,適用於需要較高安全等級的生產環境工作負載。在實際應用中,可能需要針對不同的應用類型與安全等級建立多個策略,透過 RBAC 機制將不同策略授權給不同的服務帳號。
以下的 YAML 配置展示了一個完整的限制性安全策略定義:
# 定義一個限制性的 PodSecurityPolicy
# 此策略實施嚴格的安全控制,適用於高安全要求的生產環境
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted-psp
labels:
app.kubernetes.io/name: restricted-psp
app.kubernetes.io/component: security-policy
security-level: high
environment: production
annotations:
# 提供策略的說明文件,方便團隊成員理解
description: "嚴格限制的安全策略,適用於無特殊權限需求的應用程式"
seccomp.security.alpha.kubernetes.io/allowedProfileNames: 'runtime/default'
apparmor.security.beta.kubernetes.io/allowedProfileNames: 'runtime/default'
spec:
# 禁止容器以特權模式執行
# 特權模式讓容器擁有主機的所有能力,包括載入核心模組、修改系統參數等
# 這是最危險的配置,應該在絕大多數情況下禁止
privileged: false
# 禁止容器透過 setuid/setgid 機制提升權限
# 這是防止權限提升攻擊的重要防線
allowPrivilegeEscalation: false
# 移除容器的所有預設 Linux 能力
# 採用白名單模式,只保留應用程式實際需要的能力
requiredDropCapabilities:
- ALL
# 可以選擇性地允許特定能力(這裡保持空白表示不允許任何額外能力)
# 例如,若應用需要綁定特權連接埠,可以加入:
# allowedCapabilities:
# - NET_BIND_SERVICE
# 禁止容器使用主機的各種命名空間
# 這些設定防止容器直接存取主機資源,維持隔離性
hostNetwork: false # 禁止使用主機網路命名空間
hostIPC: false # 禁止使用主機 IPC 命名空間
hostPID: false # 禁止使用主機 PID 命名空間
# 禁止容器綁定主機連接埠
# 若需要對外暴露服務,應透過 Service 資源
hostPorts:
- min: 0
max: 0
# 限制可以使用的 Volume 類型
# 只允許安全的 Volume 類型,明確排除 hostPath
volumes:
- 'configMap' # 允許掛載 ConfigMap 注入組態
- 'emptyDir' # 允許使用節點上的暫存目錄
- 'projected' # 允許使用 projected volume(可組合多種來源)
- 'secret' # 允許掛載 Secret 注入敏感資料
- 'downwardAPI' # 允許存取 Pod 與容器的後設資料
- 'persistentVolumeClaim' # 允許使用 PVC 掛載持久化儲存
# 明確不包含的危險類型:
# - 'hostPath':會暴露主機檔案系統
# - 'flexVolume':可能執行任意外掛程式
# - 'gcePersistentDisk', 'awsElasticBlockStore' 等:
# 雲端廠商特定的 Volume,應透過 StorageClass 與 PVC 使用
# 強制容器以非 root 使用者執行
# 這是最重要的安全設定之一,大幅降低容器逃逸的風險
runAsUser:
rule: MustRunAsNonRoot
# 注意:此規則不指定具體的 UID,只要求不是 0(root)
# 容器映像應該在 Dockerfile 中設定適當的使用者
# 限制可使用的群組 ID 範圍
# 避免使用系統預留的低 GID(通常 0-999)
runAsGroup:
rule: MustRunAs
ranges:
- min: 1000
max: 65535
# 設定補充群組的規則
# supplementalGroups 用於控制容器程序的額外群組成員資格
supplementalGroups:
rule: MustRunAs
ranges:
- min: 1000
max: 65535
# 設定檔案系統群組的規則
# fsGroup 控制 Volume 的群組擁有者,影響檔案權限
fsGroup:
rule: MustRunAs
ranges:
- min: 1000
max: 65535
# 強制使用唯讀根檔案系統
# 防止容器內的程序修改系統檔案或植入惡意程式
# 應用程式若需要寫入檔案,應使用 emptyDir 或 PVC
readOnlyRootFilesystem: true
# 配置 SELinux 規則(適用於 RHEL/CentOS/Fedora)
# RunAsAny 表示允許容器使用任意 SELinux 標籤
# 在更嚴格的環境中,可以指定特定的標籤類型
seLinux:
rule: RunAsAny
# 禁止使用不安全的 sysctl 參數
# sysctl 可以修改核心參數,某些參數會影響主機或其他容器
# 此處保持空白表示不允許任何 unsafe sysctl
allowedUnsafeSysctls: []
# 禁止使用 proc mount(實驗性功能)
# 預設值已經是最安全的 Default 模式
allowedProcMountTypes:
- Default
建立這個 PodSecurityPolicy 的指令與驗證步驟如下:
# 將上述 YAML 配置儲存為檔案
# 檔案名稱建議與策略名稱保持一致
cat > restricted-psp.yaml <<EOF
# ... (上述 YAML 內容) ...
EOF
# 套用 PodSecurityPolicy 到叢集
# 這個操作需要叢集管理員權限
kubectl apply -f restricted-psp.yaml
# 驗證 PodSecurityPolicy 是否成功建立
# 輸出應該顯示剛建立的策略
kubectl get psp restricted-psp
# 檢視 PSP 的詳細資訊
# 確認所有屬性都正確設定
kubectl describe psp restricted-psp
# 查看叢集中所有的 PodSecurityPolicy
# 可能還包含系統預設的策略
kubectl get psp
# 以 YAML 格式輸出策略內容
# 方便檢查與備份
kubectl get psp restricted-psp -o yaml
需要特別強調的是,單純建立 PodSecurityPolicy 並不會讓它生效。必須透過 RBAC 機制將策略授權給特定的使用者、群組或服務帳號,策略才會在 Pod 建立時進行驗證。這種設計提供了靈活性,讓不同的工作負載可以使用不同的安全策略,但也增加了配置的複雜度,這將在下一節詳細說明。
RBAC 權限繫結與服務帳號管理
建立 PodSecurityPolicy 只是安全治理的第一步,更關鍵的是透過 RBAC(Role-Based Access Control)機制將策略與特定的身份主體繫結。Kubernetes 的 RBAC 模型基於角色(Role)與角色繫結(RoleBinding)的概念,提供了靈活且精細的權限管理能力。在 PodSecurityPolicy 的場景中,RBAC 的作用是授予特定身份使用特定策略的權限。
RBAC 繫結的運作邏輯可以這樣理解:當 Pod 透過某個 ServiceAccount 建立時,API Server 會查找該 ServiceAccount 被授權使用的所有 PodSecurityPolicy。如果找到一個或多個策略,API Server 會嘗試用這些策略驗證 Pod 的規格。只要有任何一個策略驗證通過,Pod 就可以成功建立。如果所有策略都驗證失敗,或是 ServiceAccount 沒有被授權使用任何策略,Pod 建立請求就會被拒絕。
這種機制的設計意圖是提供彈性,讓不同安全等級的工作負載可以使用不同的策略。例如,一般應用程式使用限制性較強的策略,而系統元件或監控代理可能需要較寬鬆的策略來存取主機資源。透過為不同類型的工作負載建立專屬的 ServiceAccount,並繫結對應的策略,可以實現精細化的安全分級管理。
@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
package "Namespace: production" {
component "ServiceAccount\napp-sa" as SA
component "RoleBinding\nuse-restricted-psp" as RB
component "Pod\nmy-application" as POD
}
package "Cluster Scope" {
component "ClusterRole\nuse-restricted-psp" as CR
component "PodSecurityPolicy\nrestricted-psp" as PSP
}
POD -right-> SA : 使用此服務帳號
SA -down-> RB : 被繫結
RB -right-> CR : 參照叢集角色
CR -down-> PSP : 授權使用此策略
note right of POD
Pod 建立時,
API Server 會檢查
其 ServiceAccount
是否有權使用
符合 Pod 規格的 PSP
end note
note bottom of PSP
策略定義了
安全要求與限制
只有符合的 Pod
才能建立
end note
@enduml上圖展示了 RBAC 與 PodSecurityPolicy 之間的關聯關係。可以看到,ServiceAccount、RoleBinding、ClusterRole 與 PodSecurityPolicy 形成了一條完整的授權鏈。只有正確配置這整條鏈路,安全策略才能真正生效。
以下是完整的 RBAC 配置範例,展示如何將先前建立的 restricted-psp 策略授權給特定的 ServiceAccount:
# 第一步:建立 ClusterRole 以授權使用特定的 PodSecurityPolicy
# ClusterRole 是叢集範圍的資源,可以在多個 Namespace 中引用
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: use-restricted-psp
labels:
app.kubernetes.io/name: use-restricted-psp
app.kubernetes.io/component: rbac
security-policy: restricted
annotations:
description: "授權使用限制性 PodSecurityPolicy 的角色"
rules:
# 定義此 ClusterRole 可以執行的操作
- apiGroups: ['policy']
# 指定資源類型為 podsecuritypolicies
resources: ['podsecuritypolicies']
# 只授予 use 動詞,表示可以使用此 PSP
# 注意:這是一個特殊的動詞,只適用於 PSP
verbs: ['use']
# 限定只能使用名為 restricted-psp 的策略
# 透過 resourceNames 限制可以操作的具體資源
resourceNames:
- restricted-psp
---
# 第二步:建立 ServiceAccount 供應用程式使用
# ServiceAccount 提供 Pod 在叢集內的身份識別
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: production
labels:
app.kubernetes.io/name: app-sa
app.kubernetes.io/component: service-account
app.kubernetes.io/part-of: my-application
annotations:
description: "應用程式專用的服務帳號,已授權使用限制性安全策略"
# ServiceAccount 會自動產生對應的 Secret,包含 API token
# Pod 可以使用這個 token 與 API Server 通訊
---
# 第三步:建立 RoleBinding 將 ClusterRole 繫結到 ServiceAccount
# 使用 RoleBinding 而非 ClusterRoleBinding 可以限制在特定 Namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: use-restricted-psp-binding
namespace: production
labels:
app.kubernetes.io/name: use-restricted-psp-binding
app.kubernetes.io/component: rbac-binding
security-policy: restricted
annotations:
description: "將限制性 PSP 使用權限繫結到應用程式服務帳號"
# roleRef 指定要繫結的角色
# 這個欄位一旦設定就無法修改,只能刪除後重建
roleRef:
kind: ClusterRole
name: use-restricted-psp
apiGroup: rbac.authorization.k8s.io
# subjects 指定被授權的主體
# 可以是 User、Group 或 ServiceAccount
subjects:
- kind: ServiceAccount
name: app-sa
namespace: production
---
# 示範:使用此 ServiceAccount 的 Pod 定義
apiVersion: v1
kind: Pod
metadata:
name: secure-app
namespace: production
labels:
app: secure-app
spec:
# 指定此 Pod 使用 app-sa ServiceAccount
# Pod 會繼承 ServiceAccount 的所有權限,包括 PSP 使用權
serviceAccountName: app-sa
# 以下配置必須符合 restricted-psp 的要求
securityContext:
# Pod 層級的安全上下文
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: app
image: my-registry/my-app:1.0.0
# 容器層級的安全上下文
securityContext:
# 允許權限提升必須設為 false
allowPrivilegeEscalation: false
# 使用唯讀根檔案系統
readOnlyRootFilesystem: true
# 以非 root 使用者執行
runAsNonRoot: true
runAsUser: 1000
# 移除所有能力
capabilities:
drop:
- ALL
# 掛載可寫入的暫存目錄
# 因為根檔案系統是唯讀的,應用需要寫入的路徑要特別掛載
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /app/cache
volumes:
# 使用 emptyDir 提供暫存儲存空間
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
套用這些配置後,production Namespace 中使用 app-sa ServiceAccount 的 Pod 就必須符合 restricted-psp 的所有要求才能建立。以下是驗證與測試的指令:
# 套用所有 RBAC 配置
kubectl apply -f rbac-config.yaml
# 驗證 ClusterRole 是否建立
kubectl get clusterrole use-restricted-psp
# 驗證 ServiceAccount 是否建立
kubectl get serviceaccount app-sa -n production
# 驗證 RoleBinding 是否建立
kubectl get rolebinding use-restricted-psp-binding -n production
# 檢視 RoleBinding 的詳細資訊
# 確認繫結關係正確
kubectl describe rolebinding use-restricted-psp-binding -n production
# 嘗試建立符合策略的 Pod
kubectl apply -f secure-app-pod.yaml
# 檢查 Pod 是否成功建立
kubectl get pod secure-app -n production
# 嘗試建立違反策略的 Pod(例如以 root 執行)
# 這應該會失敗並回傳錯誤訊息
cat <<EOF | kubectl apply -f -
apiVersion: v1
kind: Pod
metadata:
name: insecure-app
namespace: production
spec:
serviceAccountName: app-sa
containers:
- name: app
image: nginx:latest
securityContext:
runAsUser: 0 # 嘗試以 root 執行
EOF
# 預期輸出錯誤訊息:
# Error from server (Forbidden): error when creating "STDIN":
# pods "insecure-app" is forbidden: unable to validate against
# any pod security policy: [spec.containers[0].securityContext.runAsUser:
# Invalid value: 0: must be non-root]
需要特別注意的是,Kubernetes 不會為 default ServiceAccount 自動授權任何 PodSecurityPolicy。這意味著,如果啟用了 PodSecurityPolicy Admission Controller,而某個 Pod 使用 default ServiceAccount 且該帳號沒有被授權使用任何 PSP,Pod 建立會失敗。這是一個常見的陷阱,初學者經常因為忽略 RBAC 配置而導致所有 Pod 都無法建立。
最佳實踐是為每個應用程式建立專屬的 ServiceAccount,而不是共用 default 帳號。這不僅能更精確地控制權限與安全策略,也符合最小權限原則與職責分離的安全設計理念。在大型叢集中,可以建立多個不同安全等級的 PodSecurityPolicy,為不同類型的工作負載提供適當的策略,透過 RBAC 機制實現分級管理。
kube-psp-advisor 自動化策略生成實戰
在生產環境中,叢集可能包含數十甚至數百個不同的工作負載,每個工作負載可能有不同的安全需求與執行特性。手動為每個工作負載編寫 PodSecurityPolicy 不僅耗時費力,而且容易出錯。配置過於寬鬆的策略無法提供足夠的安全保護,配置過於嚴格的策略則可能導致應用程式無法正常執行。
kube-psp-advisor 是 Sysdig 公司開源的自動化工具,專門用於解決這個問題。它能夠掃描叢集中現有的工作負載,深入分析每個 Pod 的安全上下文配置,包括特權模式設定、使用者與群組 ID、Linux 能力、Volume 掛載類型、命名空間使用情況等,然後根據這些實際使用的配置自動生成對應的 PodSecurityPolicy。這種基於實際需求生成策略的方法,既能確保策略的適用性,又能遵循最小權限原則,只授予工作負載實際需要的權限。
kube-psp-advisor 的運作流程可以分為幾個階段。首先是資料收集階段,工具會連接到 Kubernetes API Server,列出指定 Namespace 中的所有 Pod,並提取每個 Pod 的完整規格與安全上下文資訊。接著進入分析階段,工具會彙總所有 Pod 使用的安全特性,例如是否有 Pod 使用特權模式、使用的 UID 範圍、需要的 Linux 能力、掛載的 Volume 類型等。最後是生成階段,工具會根據分析結果生成完整的 YAML 配置,包括 PodSecurityPolicy 定義、授權使用該策略的 ClusterRole,以及將權限繫結到 ServiceAccount 的 RoleBinding。
@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
start
:安裝 kube-psp-advisor 工具;
note right
透過 kubectl krew 外掛
或直接下載二進位檔案
end note
:連接到 Kubernetes 叢集;
:指定要掃描的 Namespace;
partition "資料收集階段" {
:列出 Namespace 中所有 Pod;
:提取每個 Pod 的規格;
fork
:分析特權模式設定;
fork again
:分析使用者與群組 ID;
fork again
:分析 Volume 掛載類型;
fork again
:分析 Linux 能力配置;
fork again
:分析命名空間使用情況;
fork again
:分析主機路徑掛載;
end fork
}
partition "策略生成階段" {
:彙總所有安全屬性;
:生成 PodSecurityPolicy;
note right
根據實際使用情況
設定各個屬性
end note
:生成 ClusterRole;
note right
授權使用生成的 PSP
end note
:生成 RoleBinding;
note right
將權限繫結到
ServiceAccount
end note
}
:輸出完整的 YAML 配置;
:管理員檢視並調整;
note right
可能需要根據
安全要求進行微調
end note
:套用到叢集;
stop
@enduml安裝 kube-psp-advisor 最便捷的方式是透過 kubectl 的外掛管理工具 krew。krew 類似於作業系統的套件管理器,專門用於管理 kubectl 外掛。以下是完整的安裝與使用流程:
# 第一步:安裝 krew(kubectl 外掛管理工具)
# 如果已經安裝 krew,可以跳過這一步
# macOS 使用者可透過 Homebrew 快速安裝
brew install krew
# Linux 或其他作業系統使用者可使用官方安裝腳本
(
set -x; cd "$(mktemp -d)" &&
OS="$(uname | tr '[:upper:]' '[:lower:]')" &&
ARCH="$(uname -m | sed -e 's/x86_64/amd64/' -e 's/\(arm\)\(64\)\?.*/\1\2/' -e 's/aarch64$/arm64/')" &&
KREW="krew-${OS}_${ARCH}" &&
curl -fsSLO "https://github.com/kubernetes-sigs/krew/releases/latest/download/${KREW}.tar.gz" &&
tar zxvf "${KREW}.tar.gz" &&
./"${KREW}" install krew
)
# 將 krew 的執行路徑加入系統 PATH 環境變數
# 將以下指令加入 ~/.bashrc 或 ~/.zshrc
export PATH="${KREW_ROOT:-$HOME/.krew}/bin:$PATH"
# 重新載入 shell 設定或手動執行上述 export 指令
source ~/.bashrc # 或 source ~/.zshrc
# 驗證 krew 是否正確安裝
kubectl krew version
# 第二步:使用 krew 安裝 advise-psp 外掛
kubectl krew install advise-psp
# 更新 krew 的外掛索引(確保能取得最新版本)
kubectl krew update
# 驗證 advise-psp 是否成功安裝
kubectl advise-psp --help
# 輸出應該顯示工具的使用說明與可用參數
安裝完成後,就可以開始使用 kube-psp-advisor 掃描工作負載並生成安全策略。工具提供了多種使用模式,可以針對特定 Namespace、特定工作負載,或是整個叢集進行掃描:
# 基本用法:掃描 default Namespace 中的工作負載
# inspect 命令會分析 Namespace 中的所有 Pod
# --grant 參數表示同時生成 RBAC 相關資源
kubectl advise-psp inspect --grant --namespace default
# 將生成的配置輸出到檔案
# 這樣可以方便檢視、修改與版本控制
kubectl advise-psp inspect --grant --namespace default > default-ns-psp.yaml
# 掃描特定的 Deployment
# --name 參數指定要分析的工作負載名稱
kubectl advise-psp inspect --grant --namespace production --name my-deployment > my-deployment-psp.yaml
# 掃描整個叢集的所有 Namespace
# 注意:在大型叢集中這可能需要較長時間
kubectl advise-psp inspect --grant --all-namespaces > cluster-wide-psp.yaml
# 只生成 PodSecurityPolicy,不生成 RBAC 資源
# 適用於需要自訂 RBAC 配置的場景
kubectl advise-psp inspect --namespace production > psp-only.yaml
# 以表格格式顯示分析結果
# --format table 參數會以表格形式展示各個 ServiceAccount 需要的權限
kubectl advise-psp inspect --namespace production --format table
# 輸出範例:
# NAMESPACE SERVICE ACCOUNT PRIVILEGED HOST NETWORK HOST PID HOST IPC
# production app-sa false false false false
# production monitoring-sa false true false false
kube-psp-advisor 生成的配置是一個完整的部署套件,包含了 PodSecurityPolicy、ClusterRole 與 RoleBinding 三個部分。以下是一個實際的輸出範例,展示工具如何根據工作負載的實際需求生成策略:
# kube-psp-advisor 自動生成的配置
# 這是針對 psp-test Namespace 中 sa-1 ServiceAccount 的工作負載生成的策略
# 第一部分:PodSecurityPolicy 定義
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
# 命名格式:psp-for-<namespace>-<serviceaccount>
# 這種命名方式清楚標示了策略的適用範圍
name: psp-for-psp-test-sa-1
labels:
generated-by: kube-psp-advisor
namespace: psp-test
serviceaccount: sa-1
annotations:
# 記錄生成時間與工具版本
generated-at: "2025-11-28T03:00:00Z"
generator-version: "1.0.0"
spec:
# 根據工作負載實際使用的能力來設定
# 此工作負載需要 SYS_ADMIN 能力,可能用於執行特定的系統管理操作
# 例如掛載檔案系統或修改命名空間設定
allowedCapabilities:
- SYS_ADMIN
# 根據工作負載掛載的主機路徑來設定
# 此工作負載需要存取 /usr/bin 目錄,且限制為唯讀
# 可能是為了執行主機上的某些工具程式
allowedHostPaths:
- pathPrefix: /usr/bin
readOnly: true
# 此工作負載需要存取主機的各種命名空間
# 這些設定雖然較為寬鬆,但反映了工作負載的實際需求
# 可能是監控代理或系統管理工具
hostIPC: true # 允許使用主機 IPC 命名空間
hostNetwork: true # 允許使用主機網路命名空間
hostPID: true # 允許使用主機 PID 命名空間
# 允許綁定主機連接埠
# 這通常用於需要直接暴露在主機網路上的服務
hostPorts:
- min: 0
max: 65535
# fsGroup 設定允許任意群組 ID
# 這是因為工作負載沒有特別指定 fsGroup
fsGroup:
rule: RunAsAny
# runAsUser 設定允許任意使用者 ID
# 工作負載可能需要以 root 身份執行某些操作
runAsUser:
rule: RunAsAny
# SELinux 設定允許任意標籤
seLinux:
rule: RunAsAny
# supplementalGroups 設定允許任意補充群組
supplementalGroups:
rule: RunAsAny
# 根據工作負載使用的 Volume 類型來設定
# 此工作負載使用了 configMap、secret 與 hostPath
volumes:
- configMap
- secret
- hostPath
- emptyDir
# 允許權限提升
# 這是因為工作負載需要執行需要提權的操作
allowPrivilegeEscalation: true
# 不要求唯讀根檔案系統
# 工作負載可能需要寫入檔案
readOnlyRootFilesystem: false
---
# 第二部分:ClusterRole 定義
# 授權使用上述 PodSecurityPolicy
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: use-psp-by-psp-test:sa-1
labels:
generated-by: kube-psp-advisor
namespace: psp-test
serviceaccount: sa-1
rules:
- apiGroups:
- policy
# 指定此 ClusterRole 可以使用的 PSP
resourceNames:
- psp-for-psp-test-sa-1
resources:
- podsecuritypolicies
# use 是一個特殊的動詞,只適用於 PSP
verbs:
- use
---
# 第三部分:RoleBinding 定義
# 將 ClusterRole 繫結到 ServiceAccount
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: use-psp-by-psp-test:sa-1-binding
namespace: psp-test
labels:
generated-by: kube-psp-advisor
serviceaccount: sa-1
# 指定要繫結的角色
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: use-psp-by-psp-test:sa-1
# 指定被授權的主體
subjects:
- kind: ServiceAccount
name: sa-1
namespace: psp-test
從這個範例可以看出,kube-psp-advisor 生成的策略完全基於工作負載的實際配置。如果工作負載使用了特權模式或主機命名空間,生成的策略會允許這些設定。這種做法的優點是確保策略不會阻礙現有工作負載的執行,但缺點是可能產生過於寬鬆的策略。
因此,生成的配置應該被視為起點而非終點。管理員應該仔細檢視生成的策略,評估每個寬鬆的設定是否真的必要。例如,如果發現某個工作負載被允許使用主機網路,應該進一步調查是否真的需要這個權限,能否透過其他方式(如 Service 或 Ingress)來暴露服務。對於不必要的寬鬆設定,應該在生成的 YAML 檔案中手動調整,然後再套用到叢集。
特別值得注意的是,kube-psp-advisor 不會為 default ServiceAccount 生成 PodSecurityPolicy。這是一個刻意的設計決策,因為 default ServiceAccount 通常被 Namespace 中的多個工作負載共用,如果為其生成統一的 PSP,可能會因為需要滿足所有工作負載的需求而變得過於寬鬆,賦予某些工作負載不必要的權限。
最佳實踐是為每個應用程式建立專屬的 ServiceAccount。這樣不僅能針對每個應用的特定需求生成精確的安全策略,也符合最小權限原則與職責分離的安全設計理念。在實施 PodSecurityPolicy 之前,應該先完成 ServiceAccount 的規劃與建立,確保每個工作負載都有對應的專屬帳號。
容器映像安全與左移安全實踐
在 DevSecOps 的實踐理念中,「左移安全」(Shift Left Security)是一個核心概念,強調將安全檢測與修復工作從傳統的部署後階段提前到開發與建置階段。這種理念的價值在於,越早發現安全問題,修復成本就越低,對業務的影響也越小。在軟體開發生命週期的後期才發現安全漏洞,不僅需要投入更多的開發資源進行修復,還可能因為架構或相依性的限制而難以完全解決問題。
容器映像掃描正是實現左移安全的關鍵技術之一。容器映像是應用程式及其所有相依項的完整打包,包含了作業系統層的基礎映像、系統函式庫、執行期環境(如 Python 直譯器、Node.js 執行期)、應用程式相依套件(如 npm 模組、pip 套件),以及應用程式本身的程式碼。這個多層次的結構意味著每一層都可能引入安全漏洞。
基礎映像通常來自官方或社群維護的倉庫,如 Docker Hub 上的 ubuntu、alpine 或 debian 映像。這些映像會定期釋出安全更新以修補已知漏洞,但如果應用程式建置時使用了過時的基礎映像版本,就可能包含已知的作業系統層漏洞。系統函式庫如 glibc、openssl、curl 等,是應用程式運作的基礎元件,這些函式庫的漏洞可能被攻擊者利用來執行任意程式碼或竊取敏感資料。
應用程式的相依套件是另一個主要的風險來源。現代應用程式通常依賴大量的第三方函式庫,這些函式庫本身可能包含安全漏洞。例如,一個 Node.js 應用程式可能依賴數百個 npm 套件,而這些套件又各自依賴其他套件,形成複雜的相依樹。任何一個套件的漏洞都可能影響最終的應用程式。定期更新相依套件版本、關注安全公告,以及使用自動化工具進行漏洞掃描,是管理相依套件風險的重要手段。
漏洞的嚴重程度評估對於決定修復優先順序至關重要。業界普遍採用 CVSS(Common Vulnerability Scoring System)評分系統來量化漏洞的嚴重性。CVSS 評分從 0.0 到 10.0,根據攻擊向量、攻擊複雜度、所需權限、使用者互動需求、影響範圍等多個維度綜合計算。評分結果通常分為四個等級:0.0-3.9 為低風險(Low),4.0-6.9 為中風險(Medium),7.0-8.9 為高風險(High),9.0-10.0 為嚴重風險(Critical)。
在制定映像掃描策略時,可以根據環境的重要性與風險承受能力設定不同的閾值。開發環境通常可以容許中低風險的漏洞存在,因為這個階段的主要目標是快速迭代與功能驗證。測試環境應該對中高風險漏洞進行把關,確保進入準生產環境的映像已經修復了大部分的安全問題。生產環境則要求最嚴格的標準,通常不允許存在高風險以上的漏洞,某些特別敏感的系統甚至要求零已知漏洞。
透過在 CI/CD 流程中整合映像掃描,可以在建置階段就自動檢測漏洞。如果發現超過閾值的漏洞,建置流程會自動失敗並通知開發團隊,防止有漏洞的映像被推送到生產環境。這種自動化的安全門檻確保了每個部署到生產的映像都經過了安全驗證,大幅降低了生產環境的風險暴露。
Anchore Engine 企業級映像掃描平台
Anchore Engine 是一個功能完整且開源的容器映像分析與掃描平台,在眾多映像掃描工具中以其全面性與深度著稱。相較於其他掃描工具,Anchore Engine 的優勢不僅在於能夠偵測已知漏洞(透過與 CVE 資料庫比對),還能夠執行自訂的安全策略檢查,例如驗證映像是否以非 root 使用者執行、檢查是否包含敏感檔案(如私鑰或密碼)、確認必要的標籤是否存在等。
Anchore Engine 的架構由多個元件組成。核心引擎(Core Engine)負責接收 API 請求、協調各個元件的工作,以及管理系統狀態。映像分析器(Analyzer)負責下載映像、提取每一層的內容、識別已安裝的套件與檔案清單。漏洞掃描器(Vulnerability Scanner)將識別出的套件與已知漏洞資料庫(主要是 CVE 資料庫)進行比對,找出存在漏洞的套件版本。策略引擎(Policy Engine)根據預先定義或自訂的策略規則,評估映像是否符合組織的安全要求。
Anchore Engine 提供完整的 REST API,使其易於與各種 CI/CD 工具整合。無論是 Jenkins、GitLab CI/CD、GitHub Actions 還是 Azure DevOps,都可以透過呼叫 API 或使用官方提供的外掛來整合映像掃描流程。這種靈活性讓 Anchore Engine 能夠無縫嵌入到現有的開發工作流程中,不需要大幅度改變團隊的工作方式。
以下展示如何透過 Docker Compose 快速部署 Anchore Engine:
# 建立 Anchore Engine 的工作目錄
# 所有相關檔案都會放在這個目錄中
mkdir -p ~/anchore-engine && cd ~/anchore-engine
# 下載官方提供的 docker-compose.yaml 檔案
# 這個檔案定義了 Anchore Engine 的所有元件與相依服務
curl -O https://engine.anchore.io/docs/quickstart/docker-compose.yaml
# 檢視 docker-compose.yaml 的內容
# 瞭解會啟動哪些服務
cat docker-compose.yaml
# docker-compose.yaml 通常包含以下服務:
# - anchore-engine:核心引擎
# - anchore-db:PostgreSQL 資料庫,儲存分析結果與策略
# - catalog:負責映像倉庫的整合
# - simplequeue:內部訊息佇列
# - analyzer:映像分析服務
# - policy-engine:策略評估服務
# 啟動 Anchore Engine
# -d 參數表示在背景執行
docker-compose up -d
# 檢查所有服務的執行狀態
# 所有服務都應該顯示為 Up 且狀態為 healthy
docker-compose ps
# 預期輸出範例:
# Name Command State Ports
# ---------------------------------------------------------------------------------
# anchore_engine_1 /docker-entrypoint.sh anch ... Up 8228/tcp
# anchore_db_1 docker-entrypoint.sh postgres Up 5432/tcp
# 查看服務日誌
# 確認啟動過程沒有錯誤
docker-compose logs -f anchore-engine
# 等待服務完全啟動(可能需要 2-3 分鐘)
# Anchore Engine 需要初始化資料庫並下載漏洞資料
# 安裝 Anchore CLI 工具
# CLI 工具提供了便捷的命令列介面來操作 Anchore Engine
pip install anchorecli
# 或使用 pip3 安裝(Python 3 環境)
pip3 install anchorecli
# 驗證安裝是否成功
anchore-cli --version
# 設定環境變數以連接 Anchore Engine
# 這些變數告訴 CLI 如何連接到 Engine 以及使用什麼憑證
export ANCHORE_CLI_URL=http://localhost:8228/v1
export ANCHORE_CLI_USER=admin
export ANCHORE_CLI_PASS=foobar
# 將這些設定加入 shell 組態檔以持久化
cat >> ~/.bashrc <<EOF
# Anchore Engine Configuration
export ANCHORE_CLI_URL=http://localhost:8228/v1
export ANCHORE_CLI_USER=admin
export ANCHORE_CLI_PASS=foobar
EOF
# 重新載入 shell 設定
source ~/.bashrc
# 確認 Anchore Engine 已就緒
# 這個命令會顯示各個服務的狀態
anchore-cli system status
# 預期輸出範例:
# Service | Host | State | Version
# ----------------+--------------+-------+--------
# catalog | anchore | up | 1.0.0
# analyzer | anchore | up | 1.0.0
# policy_engine | anchore | up | 1.0.0
# 等待漏洞資料庫同步完成
# 初次啟動時,Anchore Engine 需要從上游同步 CVE 資料
# 這個過程可能需要 10-15 分鐘
anchore-cli system feeds list
# 當所有 feed 的狀態都顯示為 'synced' 時,表示同步完成
# 預期輸出範例:
# Feed | Group | LastSync | RecordCount
# ------------------+---------------+---------------------------+-------------
# vulnerabilities | ubuntu:20.04 | 2025-11-28T03:00:00 | 15234
# vulnerabilities | debian:11 | 2025-11-28T03:00:00 | 23456
完成部署與初始化後,就可以開始使用 Anchore Engine 掃描容器映像。以下展示完整的掃描工作流程:
# 新增映像到 Anchore Engine 進行分析
# Anchore Engine 會下載映像(如果本地不存在)並提取各層內容
anchore-cli image add docker.io/library/nginx:latest
# 輸出範例:
# Image Digest: sha256:abc123...
# Analysis Status: analyzing
# Image ID: def456...
# 列出所有已加入的映像
anchore-cli image list
# 等待分析完成
# --timeout 參數設定最長等待時間(秒)
# 大型映像的分析可能需要數分鐘
anchore-cli image wait docker.io/library/nginx:latest --timeout 600
# 分析完成後會顯示:
# Status: analyzed
# 查看映像的基本資訊
# 包括大小、建立時間、標籤等
anchore-cli image get docker.io/library/nginx:latest
# 掃描作業系統層的漏洞
# 這會比對 Debian/Ubuntu/Alpine/RHEL 等系統的漏洞資料庫
anchore-cli image vuln docker.io/library/nginx:latest os
# 輸出範例:
# Vulnerability ID Package Severity Fix
# CVE-2021-33574 glibc-2.31-13 High 2.31-14
# CVE-2021-35942 glibc-2.31-13 High 2.31-14
# CVE-2022-23218 glibc-2.31-13 Low None
# 掃描非作業系統的套件漏洞
# 例如 Python pip、Node.js npm、Ruby gem、Java maven 等
anchore-cli image vuln docker.io/library/nginx:latest non-os
# 查看所有類型的漏洞
# 這會同時顯示 OS 與 non-OS 的漏洞
anchore-cli image vuln docker.io/library/nginx:latest all
# 以 JSON 格式輸出漏洞報告
# 方便程式化處理或整合到其他系統
anchore-cli --json image vuln docker.io/library/nginx:latest all > nginx-vulnerabilities.json
# 執行策略評估
# 這會根據預設或自訂的策略檢查映像是否符合規範
anchore-cli evaluate check docker.io/library/nginx:latest --detail
# 策略評估會檢查多個面向:
# - 是否存在高風險漏洞
# - 是否以 root 使用者執行
# - 是否包含敏感檔案
# - 套件版本是否符合要求
# - 等等
# 查看映像中安裝的所有作業系統套件
# 這可以幫助瞭解映像的組成
anchore-cli image content docker.io/library/nginx:latest os
# 查看映像的完整檔案清單
# 可以用來檢查是否包含不應該存在的檔案
anchore-cli image content docker.io/library/nginx:latest files
# 搜尋特定檔案或路徑
# 例如搜尋是否包含私鑰檔案
anchore-cli image content docker.io/library/nginx:latest files | grep -i "\.key$"
# 比較兩個映像的差異
# 這在更新基礎映像或升級版本時很有用
anchore-cli image diff docker.io/library/nginx:1.20 docker.io/library/nginx:latest
# 刪除不再需要的映像分析結果
# 釋放資料庫空間
anchore-cli image del docker.io/library/nginx:latest
Anchore Engine 的掃描結果非常詳細,不僅會列出每個發現的漏洞,還會提供豐富的上下文資訊協助理解與修復。對於每個漏洞,報告會包含 CVE 編號(如 CVE-2021-33574)、受影響的套件名稱與版本、漏洞的嚴重程度評級(Critical/High/Medium/Low)、建議的修復版本(如果有的話),以及漏洞的詳細資訊連結(通常指向 NVD 或廠商的安全公告)。
這些資訊讓開發團隊能夠快速理解漏洞的影響範圍與修復方法。如果存在修復版本,最直接的解決方案是更新受影響的套件。如果沒有修復版本,可能需要考慮使用替代套件、實施額外的安全控制措施,或是評估該漏洞在實際應用場景中是否會構成真實風險。
CI/CD 流程的映像掃描整合實戰
將映像掃描整合到 CI/CD 流程是實現自動化安全防護的關鍵步驟。透過在建置管道中加入掃描任務,可以確保每次程式碼變更、每次映像建置都經過嚴格的安全檢測。這種做法不僅能夠及早發現漏洞,還能透過流程自動化降低人為疏失的風險,確保安全檢查的一致性與完整性。
@startuml
!define DISABLE_LINK
!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
start
:開發人員提交程式碼;
:觸發 CI/CD 流程;
:執行單元測試;
if (測試通過?) then (是)
:建置容器映像;
:推送映像到暫存 Registry;
:呼叫 Anchore Engine 掃描映像;
:等待掃描完成;
if (發現嚴重漏洞?) then (是)
:生成漏洞報告;
:發送通知給開發團隊;
:流程失敗;
stop
else (否)
if (策略評估通過?) then (是)
:標記映像為已驗證;
:推送映像到正式 Registry;
if (目標環境為開發環境?) then (是)
:自動部署到開發環境;
stop
else (否)
if (目標環境為測試環境?) then (是)
:自動部署到測試環境;
stop
else (否)
:等待人工核准;
:手動觸發部署到生產環境;
stop
endif
endif
else (否)
:記錄違規項目;
:發送策略違規通知;
:流程失敗;
stop
endif
endif
else (否)
:發送測試失敗通知;
:流程失敗;
stop
endif
@enduml以下是一個完整的 GitLab CI/CD 配置範例,展示如何在建置流程中整合 Anchore Engine 映像掃描。這個配置涵蓋了從程式碼建置、映像掃描,到條件式部署的完整流程:
# GitLab CI/CD 完整配置範例
# 此配置展示如何建立包含映像掃描的安全 CI/CD 流程
# 定義流程的各個階段
# 嚴格的階段順序確保安全檢查在部署前完成
stages:
- build # 建置階段:編譯程式碼、建置映像
- scan # 掃描階段:執行安全掃描與策略評估
- deploy # 部署階段:部署到目標環境
# 設定全域變數
variables:
# 映像名稱,使用 CI 環境變數組成完整的映像標籤
# CI_REGISTRY_IMAGE:專案的 Registry 路徑
# CI_COMMIT_SHA:Git commit 的 SHA 值,確保映像版本可追溯
IMAGE_NAME: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHA}
# Anchore Engine 的連線資訊
ANCHORE_CLI_URL: "http://anchore-engine.internal:8228/v1"
ANCHORE_CLI_USER: "admin"
# 注意:密碼應該透過 GitLab CI/CD 保護變數設定
# 而不是直接寫在配置檔中
# ANCHORE_CLI_PASS 應在 Settings > CI/CD > Variables 中設定
# Docker 建置參數
DOCKER_DRIVER: overlay2
DOCKER_TLS_CERTDIR: "/certs"
# 建置階段:建置並推送容器映像
build-image:
stage: build
# 使用 Docker-in-Docker 服務
# 這允許在 CI 環境中執行 Docker 命令
image: docker:latest
services:
- docker:dind
# 只在特定分支或標籤時執行
only:
- main
- develop
- /^release-.*$/
- tags
script:
# 登入容器 Registry
# CI_REGISTRY_USER 和 CI_REGISTRY_PASSWORD 是 GitLab 自動提供的變數
- docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
# 建置映像並加上 commit SHA 作為標籤
# --pull 確保使用最新的基礎映像
# --build-arg 可以傳遞建置時需要的參數
- docker build
--pull
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
--build-arg VCS_REF=${CI_COMMIT_SHA}
--tag ${IMAGE_NAME}
.
# 推送映像到 Registry
- docker push ${IMAGE_NAME}
# 如果是 main 分支,額外標記為 latest
- |
if [ "${CI_COMMIT_BRANCH}" == "main" ]; then
docker tag ${IMAGE_NAME} ${CI_REGISTRY_IMAGE}:latest
docker push ${CI_REGISTRY_IMAGE}:latest
fi
# 掃描階段:使用 Anchore Engine 掃描映像
scan-image:
stage: scan
# 使用包含 Anchore CLI 的官方映像
image: anchore/engine-cli:latest
# 需要建置階段先完成
needs:
- build-image
# 只在特定分支執行
only:
- main
- develop
- /^release-.*$/
- tags
before_script:
# 確認環境變數已正確設定
- echo "Anchore CLI URL:${ANCHORE_CLI_URL}"
- echo "Scanning image:${IMAGE_NAME}"
# 驗證 Anchore Engine 是否可連接
- anchore-cli system status || exit 1
script:
# 新增映像到 Anchore Engine 進行分析
- anchore-cli image add ${IMAGE_NAME}
# 等待映像分析完成
# --timeout 600 設定最長等待 10 分鐘
- anchore-cli image wait ${IMAGE_NAME} --timeout 600
# 執行漏洞掃描並輸出報告
- echo "=== 漏洞掃描結果 ==="
- anchore-cli image vuln ${IMAGE_NAME} all
# 將漏洞報告儲存為 JSON 格式
- anchore-cli --json image vuln ${IMAGE_NAME} all > vulnerabilities.json
# 執行策略評估
# 若評估失敗會回傳非零的結束碼,導致流程中斷
- echo "=== 策略評估結果 ==="
- anchore-cli evaluate check ${IMAGE_NAME} --detail
# 產生詳細的評估報告
- anchore-cli --json evaluate check ${IMAGE_NAME} --detail > policy-evaluation.json
# 檢查是否存在高風險或嚴重漏洞
# 這個檢查會在策略評估通過但仍有漏洞時額外執行
- |
CRITICAL_COUNT=$(jq '[.vulnerabilities[] | select(.severity=="Critical")] | length' vulnerabilities.json)
HIGH_COUNT=$(jq '[.vulnerabilities[] | select(.severity=="High")] | length' vulnerabilities.json)
echo "嚴重漏洞數量:${CRITICAL_COUNT}"
echo "高風險漏洞數量:${HIGH_COUNT}"
# 如果存在嚴重漏洞,中斷流程
if [ ${CRITICAL_COUNT} -gt 0 ]; then
echo "錯誤:發現 ${CRITICAL_COUNT} 個嚴重漏洞,中斷部署"
exit 1
fi
# 如果高風險漏洞超過閾值,發出警告但不中斷(可視需求調整)
if [ ${HIGH_COUNT} -gt 5 ]; then
echo "警告:發現 ${HIGH_COUNT} 個高風險漏洞,超過建議閾值"
fi
# 失敗時不允許繼續
allow_failure: false
# 保留掃描報告作為成品
artifacts:
name: "security-scan-reports-${CI_COMMIT_SHA}"
when: always
paths:
- vulnerabilities.json
- policy-evaluation.json
reports:
# GitLab 可以解析這個報告並在 MR 中顯示
container_scanning: vulnerabilities.json
expire_in: 30 days
# 部署階段:部署到 Kubernetes
deploy-to-dev:
stage: deploy
image: bitnami/kubectl:latest
# 只在掃描通過後執行
needs:
- scan-image
# 只在 develop 分支自動部署到開發環境
only:
- develop
environment:
name: development
url: https://dev.example.com
before_script:
# 設定 kubectl 連線資訊
- kubectl config set-cluster ${KUBE_CLUSTER} --server=${KUBE_SERVER}
- kubectl config set-credentials deployer --token=${KUBE_TOKEN}
- kubectl config set-context default --cluster=${KUBE_CLUSTER} --user=deployer
- kubectl config use-context default
script:
# 更新 Deployment 的映像版本
- kubectl set image deployment/${APP_NAME}
${APP_NAME}=${IMAGE_NAME}
-n development
# 等待部署完成
- kubectl rollout status deployment/${APP_NAME} -n development --timeout=5m
# 驗證新 Pod 是否正常運作
- kubectl get pods -n development -l app=${APP_NAME}
deploy-to-prod:
stage: deploy
image: bitnami/kubectl:latest
needs:
- scan-image
# 只在 main 分支或 release 標籤時可用
only:
- main
- /^v\d+\.\d+\.\d+$/
environment:
name: production
url: https://www.example.com
before_script:
- kubectl config set-cluster ${KUBE_CLUSTER} --server=${KUBE_SERVER}
- kubectl config set-credentials deployer --token=${KUBE_TOKEN}
- kubectl config set-context default --cluster=${KUBE_CLUSTER} --user=deployer
- kubectl config use-context default
script:
# 生產環境部署前的額外檢查
- echo "準備部署到生產環境"
- echo "映像:${IMAGE_NAME}"
# 更新 Deployment
- kubectl set image deployment/${APP_NAME}
${APP_NAME}=${IMAGE_NAME}
-n production
# 等待部署完成並驗證
- kubectl rollout status deployment/${APP_NAME} -n production --timeout=10m
# 執行部署後的健康檢查
- curl -f https://www.example.com/health || exit 1
# 生產環境部署需要手動觸發
when: manual
這個完整的 CI/CD 配置展示了如何建立一個安全的部署流程。關鍵的安全控制點包括:在建置階段使用 commit SHA 作為映像標籤確保可追溯性、在掃描階段執行全面的漏洞檢測與策略評估、在部署階段根據環境的重要性設定不同的觸發條件(開發環境自動部署,生產環境需要人工核准)。
除了 GitLab CI/CD,Anchore Engine 也能夠整合到其他主流的 CI/CD 平台。Jenkins 使用者可以安裝 Anchore Container Image Scanner 外掛,GitHub Actions 可以使用 anchore/scan-action,Azure DevOps 可以透過 REST API 整合。無論使用哪種 CI/CD 工具,整合的核心概念都是相同的:在建置後、部署前加入掃描步驟,並根據掃描結果決定是否允許繼續流程。
制定企業級映像掃描策略
成功整合映像掃描到 CI/CD 流程後,下一個關鍵挑戰是制定合理且可執行的掃描策略。策略制定需要在安全性與開發效率之間取得平衡。過於嚴格的策略會導致大量的建置失敗與開發延遲,降低團隊的生產力與士氣。過於寬鬆的策略則無法提供足夠的安全保護,讓有漏洞的映像進入生產環境。
策略制定的第一個原則是分層管理。不同環境應該有不同的安全要求與閾值設定。開發環境的主要目標是快速驗證功能與發現程式錯誤,因此可以容許較多的安全漏洞,讓開發人員能夠專注於功能開發。在這個階段,映像掃描的主要作用是提供可見性與意識培養,讓開發人員了解他們的程式碼與相依項存在哪些安全問題。
測試環境是安全檢查開始收緊的階段。在這個環境中,應該對中高風險的漏洞進行把關。允許低風險漏洞存在,但要求修復所有高風險與嚴重漏洞。這個閾值設定確保進入準生產環境的映像已經解決了大部分的安全問題,同時不會過度阻礙開發進度。
生產環境要求最嚴格的安全標準。通常不允許存在任何已知的高風險或嚴重漏洞。對於特別敏感的系統,如處理財務資料、個人隱私資訊或關鍵基礎設施的服務,可能要求零已知漏洞。這種嚴格的要求確保生產環境的風險暴露降到最低,但也意味著需要投入更多資源在漏洞修復與映像維護上。
第二個重要原則是建立漏洞例外清單機制。在實務中,某些漏洞可能因為特定原因無法立即修復。例如,漏洞所在的套件是應用程式的核心相依項,但修復版本引入了不相容的變更,需要大量的程式碼重構。或者,漏洞的觸發條件在應用程式的實際使用情境中不可能發生,因此不構成真實風險。對於這類情況,應該建立正式的例外清單審核流程。
例外清單的申請應該包含詳細的風險評估與補償控制措施。例如,如果某個網路協定函式庫存在遠端程式碼執行漏洞,但應用程式只在內部網路中使用且外部無法存取,可以申請例外並在網路層實施額外的存取控制。每個例外項目都應該設定有效期限並定期重新評估,當修復版本釋出或環境變化導致風險增加時,應該及時移除例外並要求修復。
第三個原則是建立明確的漏洞修復 SLA(Service Level Agreement)。不同嚴重程度的漏洞應該有不同的修復時程。嚴重風險的漏洞(CVSS 評分 9.0-10.0)必須在 24 小時內修復並重新部署。高風險的漏洞(CVSS 評分 7.0-8.9)必須在 7 天內修復。中風險的漏洞(CVSS 評分 4.0-6.9)必須在 30 天內修復。低風險的漏洞(CVSS 評分 0.0-3.9)應該在下一個定期維護窗口修復。
這種明確的時程表能夠確保漏洞得到及時處理,而不會被無限期擱置。同時,它也為開發團隊提供了明確的優先順序指引,幫助他們在功能開發與安全維護之間做出合理的資源分配。為了確保 SLA 的執行,應該建立自動化的追蹤與提醒機制,在漏洞接近或超過 SLA 時通知相關人員。
最後,應該建立持續的監控與回顧機制。映像掃描不是一次性的活動,而是持續的過程。即使某個映像在建置時通過了所有安全檢查,新的漏洞資訊可能在部署後才被揭露。因此,應該定期重新掃描已部署的映像,確保沒有新發現的漏洞。同時,應該定期回顧掃描策略的有效性,根據實際執行情況調整閾值與流程,在安全性與開發效率之間找到最佳平衡點。
總結與企業安全治理建議
本文系統化地探討了 Kubernetes Pod 安全治理與容器映像掃描的完整實踐體系。從 PodSecurityPolicy 的架構原理與屬性配置,到 RBAC 權限繫結與服務帳號管理,再到 kube-psp-advisor 自動化工具的應用,以及 Anchore Engine 映像掃描平台的部署與整合,每個環節都提供了詳盡的技術說明與實務範例。
在 Pod 安全治理方面,核心概念是透過策略定義與強制准入控制,在 Pod 建立之前就驗證其安全配置是否符合組織要求。PodSecurityPolicy 雖然在新版本 Kubernetes 中已被 Pod Security Standards 取代,但其設計理念與配置方法依然具有重要的參考價值。透過合理配置 runAsUser、allowPrivilegeEscalation、volumes、capabilities 等屬性,可以有效限制 Pod 的權限範圍,降低容器逃逸與權限提升的風險。
RBAC 權限繫結是策略生效的關鍵環節。透過建立 ClusterRole 授權使用特定的 PodSecurityPolicy,然後使用 RoleBinding 將權限繫結到 ServiceAccount,可以實現精細化的安全分級管理。最佳實踐是為每個應用程式建立專屬的 ServiceAccount,而不是共用 default 帳號,這樣既能針對每個應用的特定需求制定合適的策略,也符合最小權限原則與職責分離的安全設計理念。
在容器映像安全方面,「左移安全」的理念強調將安全檢測提前到開發與建置階段。透過 Anchore Engine 這類企業級掃描平台,可以在映像建置後立即進行全面的漏洞檢測與策略評估。將掃描整合到 CI/CD 流程中,能夠確保每個部署到生產環境的映像都經過嚴格的安全驗證,大幅降低生產環境的風險暴露。
制定有效的掃描策略需要在安全性與開發效率之間取得平衡。分層管理原則建議為不同環境設定不同的安全閾值,開發環境可以較寬鬆以保持開發速度,生產環境則要求最嚴格的標準。漏洞例外清單機制為特殊情況提供了彈性,但必須經過正式的審核流程並定期重新評估。明確的漏洞修復 SLA 確保安全問題得到及時處理,不會被無限期擱置。
實施這些安全措施時,建議遵循以下最佳實踐:建立完整的安全策略文件並確保團隊成員理解;為不同類型的工作負載制定適當的安全策略,避免一刀切的做法;使用自動化工具簡化配置流程並降低人為錯誤;建立持續的監控與告警機制,及時發現與回應安全事件;定期進行安全審計與滲透測試,驗證防護措施的有效性;持續關注 Kubernetes 安全領域的最新發展,適時調整策略與工具。
隨著雲端原生技術的不斷演進,安全工具與方法也在持續發展。雖然 PodSecurityPolicy 已被新的 Pod Security Standards 取代,但縱深防禦、最小權限、職責分離等核心安全原則始終適用。持續學習、不斷改進、將安全融入開發與維運的每個環節,是維護雲端原生環境安全的長期承諾。透過系統化地實施本文介紹的安全措施,企業能夠建立堅固的容器安全防護體系,在享受容器技術帶來的敏捷性與效率提升的同時,確保業務系統的安全性與可靠性。