隨著Kubernetes已成為容器協調的主流技術,其安全問題也愈發重要。作為一個管理大規模容器化工作負載的平台,Kubernetes既提供了豐富的功能,也帶來了複雜的安全挑戰。在我多年的雲原生安全實踐中,發現許多組織在採用Kubernetes時,往往專注於功能實作而忽略了安全防護。

本文將從威脅驅動分析的角度,探討Kubernetes環境中的安全風險與防禦策略,幫助雲原生安全實踐者更有效地保護其Kubernetes叢集與工作負載。

誰需要關注Kubernetes安全?

如果你是以下角色之一,這篇文章將對你特別有價值:

  • DevOps工程師或平台團隊成員
  • 雲原生架構師
  • 網站可靠性工程師(SRE)
  • 資訊安全長(CISO)或安全團隊成員
  • 需要實際操作Kubernetes安全的技術人員

值得注意的是,本文假設你已對Kubernetes有基本瞭解,並對容器技術有一定認識。我們將聚焦於進階安全主題,而非基礎概念解釋。

Pod層級安全:第一道防線

Pod作為Kubernetes中最小的佈署單位,是安全考量的起點。從安全形度看,Pod層級的威脅可能導致整個叢集的妥協。

Pod安全風險分析

在我分析過的許多安全事件中,Pod設定不當是最常見的入侵起點。以下是幾個關鍵風險點:

特權容器的危險

特權容器(privileged containers)能夠存取主機的所有資源,這實際上消除了容器與主機之間的隔離。

apiVersion: v1
kind: Pod
metadata:
  name: privileged-pod
spec:
  containers:
  - name: security-risk-container
    image: nginx
    securityContext:
      privileged: true  # 危險設定

這個Pod定義包含了一個高風險的安全設定:privileged: true。當容器以特權模式執行時,它基本上獲得了與主機相同的許可權,能夠存取所有裝置、修改核心引數,甚至可以存取主機網路堆積積疊。這完全破壞了容器的隔離性,使攻擊者可以輕易地從容器逃逸到主機系統。在實際環境中,除非絕對必要(如需要直接存取主機硬體的系統工具),否則應該始終避免使用特權容器。

容器能力濫用

Linux能力(capabilities)允許更細粒度的許可權控制,但過度寬鬆的能力設定同樣危險。

apiVersion: v1
kind: Pod
metadata:
  name: capability-pod
spec:
  containers:
  - name: cap-container
    image: nginx
    securityContext:
      capabilities:
        add: ["NET_ADMIN", "SYS_ADMIN"]  # 過度寬鬆的能力

這個Pod設定中,容器被授予了兩個高風險的Linux能力:NET_ADMINSYS_ADMINNET_ADMIN允許容器執行網路設定操作,包括修改防火牆規則、網路介面設定等。而SYS_ADMIN是一個極其強大的能力,幾乎等同於完全的root許可權,允許執行多種系統管理操作。這種設定為攻擊者提供了廣泛的攻擊面,使他們可以操縱網路流量、修改系統設定,甚至可能實作容器逃逸。在生產環境中,應該遵循最小許可權原則,僅授予容器完成其功能所必需的最小能力集。

Pod安全策略最佳實踐

為了減輕Pod層級的安全風險,我建議實施以下防禦措施:

1. 實施Pod安全標準(PSS)

Kubernetes 1.25之後,Pod Security Policies(PSP)被Pod Security Standards取代,提供了三種預定義的安全級別:

apiVersion: v1
kind: Namespace
metadata:
  name: secure-namespace
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted

這個設定展示瞭如何在名稱空間級別應用Pod安全標準(PSS)。透過在名稱空間的標籤中設定pod-security.kubernetes.io/enforce: restricted,系統會強制執行最嚴格的安全策略,拒絕建立不符合安全標準的Pod。auditwarn標籤則分別控制安全稽核和警告行為。這種方法允許管理員在整個名稱空間範圍內強制執行一致的安全標準,無需為每個Pod單獨設定。「restricted」設定檔案禁止了特權容器、主機名稱空間分享、不安全的能力等高風險設定,為工作負載提供了較高的安全保障。

2. 使用安全上下文限制容器許可權

apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  containers:
  - name: secure-container
    image: nginx
    securityContext:
      runAsNonRoot: true
      allowPrivilegeEscalation: false
      capabilities:
        drop: ["ALL"]
      seccompProfile:
        type: RuntimeDefault

這個Pod定義展示了一系列安全最佳實踐的實作。首先,runAsNonRoot: true確保容器以非root使用者執行,減少潛在的許可權濫用。allowPrivilegeEscalation: false防止容器取得比其父程式更高的許可權,阻止許可權提升攻擊。capabilities: drop: ["ALL"]移除了所有預設的Linux能力,遵循最小許可權原則。最後,seccompProfile: type: RuntimeDefault應用了容器執行時的預設Seccomp設定檔案,限制容器可以執行的系統呼叫,進一步減少攻擊面。這種設定為容器提供了深度防禦,即使容器被入侵,攻擊者能夠執行的操作也會受到嚴格限制。

3. 實施網路政策隔離Pod通訊

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress

這個網路政策實作了一個「預設拒絕」的安全模型,阻止名稱空間中所有Pod的入站和出站通訊。空的podSelector: {}表示策略適用於名稱空間中的所有Pod,而同時包含IngressEgresspolicyTypes確保了雙向流量控制。這種策略為「零信任」安全模型奠定了基礎,預設情況下所有通訊都被阻止,然後可以透過增加更具體的網路政策來明確允許必要的通訊路徑。這種方法大減少了橫向移動的可能性,即使攻擊者成功入侵了一個Pod,也難以存取其他服務。

容器執行時隔離:深層防禦

容器執行時是Kubernetes安全架構中的關鍵元件。在實際的安全事件中,容器逃逸攻擊往往是最具破壞性的。

容器隔離技術比較

在我的安全諮詢工作中,經常被問到不同容器隔離技術的優缺點。以下是主要技術的比較:

傳統容器vs安全容器

傳統容器(如Docker)依賴Linux名稱空間和cgroups提供隔離,但這種隔離存在固有限制。安全容器技術則提供更強的邊界保護:

隔離技術安全性效能開銷相容性適用場景
Docker (runc)中等可信工作負載
gVisor不可信工作負載
Kata Containers很高高安全需求場景
Firecracker無伺服器工作負載

實施gVisor執行時隔離

gVisor提供了一個使用者空間的核心實作,攔截容器的系統呼叫,減少對宿主機內核的直接存取:

apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: gvisor
handler: runsc

---
apiVersion: v1
kind: Pod
metadata:
  name: gvisor-pod
spec:
  runtimeClassName: gvisor
  containers:
  - name: untrusted-app
    image: potentially-malicious-image

這個例子展示瞭如何在Kubernetes中使用gVisor強化容器隔離。首先,定義了一個RuntimeClass資源,指定使用runsc處理程式(gVisor的執行時處理程式)。然後,在Pod規範中透過runtimeClassName: gvisor參照這個執行時類別。當這個Pod被建立時,Kubernetes會使用gVisor而非預設的容器執行時來執行它。gVisor在容器和主機核心之間增加了一層抽象,實作了一個使用者空間核心,攔截並處理容器的系統呼叫。這種設計大減少了容器對主機內核的直接存取,即使容器中執行著惡意程式碼或遭受漏洞利用,攻擊者也難以利用核心漏洞實作容器逃逸。這種技術特別適合執行來源不可信的工作負載。

Seccomp設定檔案定製

Seccomp(Secure Computing Mode)允許限制容器可以執行的系統呼叫,顯著減少攻擊面:

{
  "defaultAction": "SCMP_ACT_ERRNO",
  "architectures": ["SCMP_ARCH_X86_64"],
  "syscalls": [
    {
      "names": [
        "read", "write", "open", "close", "stat", "fstat",
        "mmap", "munmap", "brk", "rt_sigaction", "rt_sigprocmask",
        "rt_sigreturn", "ioctl", "getpid", "socket", "connect"
      ],
      "action": "SCMP_ACT_ALLOW"
    }
  ]
}

這個Seccomp設定檔案展示瞭如何精確控制容器可以執行的系統呼叫。設定從極端限制開始:"defaultAction": "SCMP_ACT_ERRNO"表示預設情況下拒絕所有系統呼叫,回傳錯誤。然後,透過"syscalls"部分明確列出允許的系統呼叫,如基本的檔案操作(read、write、open)、記憶體管理(mmap、munmap)和網路功能(socket、connect)等。這種「預設拒絕」的方法遵循最小許可權原則,只允許容器執行其功能所必需的系統呼叫,大減少了攻擊面。當容器嘗試執行未列入白名單的系統呼叫時(如可能被用於漏洞利用的危險呼叫),操作會被拒絕,從而阻止潛在的攻擊。這種設定需要仔細測試,確保不會意外阻止應用程式所需的合法系統呼叫。

應用程式與供應鏈安全

在我多年的安全實踐中,發現供應鏈攻擊已成為雲原生環境中最具挑戰性的威脅之一。攻擊者不再直接攻擊執行中的系統,而是將目標轉向系統的構建和佈署過程。

容器映像安全

容器映像是供應鏈安全的核心環節。我經常看到組織使用未經驗證的公共映像,這些映像可能包含惡意程式碼或嚴重漏洞。

映像掃描與驗證

實施自動化的映像掃描流程是必要的:

apiVersion: v1
kind: Pod
metadata:
  name: secure-image-pod
  annotations:
    image-policy.k8s.io/break-glass: "false"
spec:
  containers:
  - name: trusted-app
    image: registry.internal.example.com/verified/app:v1.2.3
    imagePullPolicy: Always

這個Pod定義展示了幾個容器映像安全最佳實踐。首先,使用了私有登入檔(registry.internal.example.com)而非公共登入檔,這提供了更好的供應鏈控制。其次,映像路徑中的verified表明這個映像已經透過了安全掃描和驗證流程。標籤使用具體的版本號(v1.2.3)而非latest,確保了可重複的佈署結果。imagePullPolicy: Always確保每次Pod建立時都會提取最新的映像,即使本地已有同名映像。註解image-policy.k8s.io/break-glass: "false"表示不允許繞過映像策略檢查,這可以與准入控制器結合使用,確保只有符合安全標準的映像才能佈署。這種設定為容器映像提供了多層防護,減少了透過受損映像進行攻擊的風險。

使用Sigstore保護軟體供應鏈

Sigstore提供了一套用於簽名、驗證和發現軟體成品的工具,幫助確保供應鏈完整性:

# 使用Cosign簽名容器映像
cosign sign --key cosign.key registry.example.com/myapp:v1.0.0

# 在佈署前驗證映像簽名
cosign verify --key cosign.pub registry.example.com/myapp:v1.0.0

這個例子展示瞭如何使用Sigstore生態系統中的Cosign工具進行容器映像簽名和驗證。第一條命令使用私鑰(cosign.key)對容器映像進行簽名,生成一個加密簽名並將其附加到映像中繼資料中。這個簽名能證明映像確實來自預期的建立者,與自簽名後未被修改。第二條命令使用公鑰(cosign.pub)驗證映像的簽名,確認其真實性和完整性。這種機制建立了一個可信任的軟體供應鏈,能夠防止攻擊者植入惡意程式碼或修改合法映像。在實際佈署中,這種驗證可以整合到CI/CD管道或Kubernetes准入控制器中,自動拒絕未經簽名或簽名無效的映像,從而防止受損映像進入生產環境。

實施軟體物料清單(SBOM)

軟體物料清單提供了應用程式依賴項的完整清單,有助於識別潛在的漏洞:

apiVersion: tekton.dev/v1beta1
kind: Task
metadata:
  name: generate-sbom
spec:
  steps:
  - name: syft-sbom
    image: anchore/syft
    script: |
      syft packages -o spdx-json=sbom.json $(params.IMAGE)
      # 儲存SBOM以便日後漏洞掃描
      curl -X POST -F file=@sbom.json $(params.SBOM_REGISTRY)/upload

這個Tekton任務展示瞭如何在CI/CD管道中生成軟體物料清單(SBOM)。任務使用Anchore的Syft工具掃描容器映像,識別其中包含的所有軟體包和依賴項,並以SPDX JSON格式輸出結果。生成的SBOM檔案隨後被上載到專門的SBOM登入檔中儲存。這種做法為安全團隊提供了容器內容的透明檢視,使他們能夠:1) 快速識別新發現漏洞影響的容器;2) 進行軟體合規稽核;3) 實施更精確的風險評估。在實際使用中,SBOM通常與漏洞掃描工具(如Grype、Trivy)整合,持續監控已知漏洞。當新漏洞公佈時,安全團隊可以立即查詢SBOM登入檔,確定哪些工作負載受到影響,大縮短漏洞回應時間。

Kubernetes網路安全

網路安全是保護Kubernetes環境的關鍵方面。預設情況下,Kubernetes的網路設定相對寬鬆,需要額外的安全措施。

網路策略實施

Kubernetes網路策略允許控制Pod之間的通訊,實作微分段:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-allow
spec:
  podSelector:
    matchLabels:
      app: api-service
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 8080
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: database
    ports:
    - protocol: TCP
      port: 5432

這個網路政策定義了精確的流量控制規則,實作了最小許可權原則。它適用於標記為app: api-service的Pod,同時控制入站和出站流量。入站規則(ingress)只允許來自標記為app: frontend的Pod透過TCP 8080連線埠存取API服務,有效阻止了其他來源的流量。出站規則(egress)僅允許API服務透過TCP 5432連線埠連線到標記為app: database的Pod,防止API服務存取其他不必要的服務。這種精細的網路分段大減少了攻擊面和橫向移動的可能性。如果攻擊者成功入侵了前端服務,他們只能存取API服務的8080連線埠,無法直接存取資料函式庫或其他服務。同樣,如果API服務被入侵,攻擊者也只能連線到資料函式庫,無法存取其他目標。

加密叢集通訊

確保Kubernetes叢集內部通訊的加密是防止中間人攻擊的重要措施:

apiVersion: v1
kind: Service
metadata:
  name: secure-service
  annotations:
    service.beta.kubernetes.io/aws-load-balancer-backend-protocol: "https"
    service.beta.kubernetes.io/aws-load-balancer-ssl-cert: "arn:aws:acm:region:account:certificate/cert-id"
    service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443"
spec:
  ports:
  - port: 443
    targetPort: 8443
  selector:
    app: secure-app
  type: LoadBalancer

這個Service定義展示瞭如何在AWS環境中設定帶TLS終止的負載平衡器。透過註解aws-load-balancer-backend-protocol: "https",負載平衡器會使用HTTPS協定與後端Pod通訊,確保叢集內部流量加密。aws-load-balancer-ssl-cert指定了用於前端連線的SSL證書ARN,而aws-load-balancer-ssl-ports確保443連線埠上的連線使用SSL/TLS加密。Service設定為將443連線埠上的流量轉發到Pod的8443連線埠,假設Pod已設定為在該連線埠上接受HTTPS連線。這種設定實作了端對端加密:客戶端到負載平衡器的通訊透過公共證書加密,而負載平衡器到Pod的通訊則使用內部TLS。這防止了網路層的竊聽和中間人攻擊,即使攻擊者能夠存取叢集網路,也無法解密或修改流量內容。

使用服務網格增強安全性

服務網格提供了額外的安全功能,包括透明的流量加密、身份驗證和授權:

apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system
spec:
  mtls:
    mode: STRICT

---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: httpbin-policy
  namespace: default
spec:
  selector:
    matchLabels:
      app: httpbin
  rules:
  - from:
    - source:
        principals: ["cluster.local/ns/default/sa/sleep"]
    to:
    - operation:
        methods: ["GET"]
        paths: ["/info"]

這個例子展示瞭如何使用Istio服務網格實施高階安全控制。第一個資源是PeerAuthentication,設定為mode: STRICT的mTLS(相互TLS),強制要求服務間的所有通訊必須使用TLS加密並相互認證。由於它應用在istio-system名稱空間,這個策略會全域生效,確保整個網格內的加密通訊。第二個資源是AuthorizationPolicy,實施了精細的存取控制。它指定只有使用sleep服務帳號的服務才能向httpbin應用傳送GET請求到/info路徑。這種設定實作了根據身份的授權,而非僅根據網路位置,這是零信任安全模型的核心原則。服務網格的這種安全功能對應用透明,不需要修改應用程式碼,大簡化了複雜微服務架構中的安全實施。在實際佈署中,可以逐步細化這些策略,實作最小許可權原則。

儲存與資料安全

資料安全是Kubernetes環境中經常被忽視的方面。特別是在處理敏感資訊時,適當的加密和存取控制至關重要。

加密Secret資源

Kubernetes Secret預設以未加密的形式儲存在etcd中,這構成了重大安全風險:

apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
metadata:
  name: encryption-config
resources:
  - resources:
    - secrets
    providers:
    - aescbc:
        keys:
        - name: key1
          secret: <base64編碼的32位元組金鑰>
    - identity: {}

這個設定檔案啟用了Kubernetes API伺服器的靜態加密功能,專門針對Secret資源。resources部分指定了需要加密的資源型別,這裡僅包括secretsproviders部分定義了加密提供者,按優先順序排列。首先是aescbc提供者,使用AES-CBC加密演算法和指定的金鑰。identity提供者(不執行加密)作為後備選項列出。當這個設定生效後,所有新建或更新的Secret會使用AES-CBC演算法加密後再儲存到etcd中,而現有Secret則需要手動重建才能加密。這種設定解決了一個重要的安全風險:如果攻擊者能夠存取etcd資料函式庫(例如透過備份檔案或直接存取etcd伺服器),他們將無法輕易讀取加密後的Secret內容。在生產環境中,金鑰管理是一個關鍵考慮因素,通常會使用外部金鑰管理系統(如HashiCorp Vault或AWS KMS)而非直接在設定檔案中儲存金鑰。

使用外部金鑰管理系統

將敏感資訊儲存在專門的金鑰管理系統中是更安全的做法:

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: vault-database
spec:
  provider: vault
  parameters:
    vaultAddress: "https://vault.example.com:8200"
    roleName: "database-role"
    objects: |
      - objectName: "db-password"
        secretPath: "secret/data/database"
        secretKey: "password"

---
apiVersion: v1
kind: Pod
metadata:
  name: app-using-vault
spec:
  containers:
  - name: app
    image: myapp:1.0.0
    volumeMounts:
    - name: secrets-store
      mountPath: "/mnt/secrets"
      readOnly: true
  volumes:
  - name: secrets-store
    csi:
      driver: secrets-store.csi.k8s.io
      readOnly: true
      volumeAttributes:
        secretProviderClass: "vault-database"

這個例子展示瞭如何使用Secrets Store CSI驅動從HashiCorp Vault取得敏感訊息。第一個資源SecretProviderClass定義瞭如何從Vault取得金鑰,包括Vault伺服器地址、使用的角色和要檢索的金鑰物件。它指定從secret/data/database路徑取得password欄位,並將其對映為db-password物件。第二個資源是使用這些金鑰的Pod定義。它透過CSI卷掛載金鑰,指定使用前面定義的SecretProviderClass。當Pod啟動時,CSI驅動會連線到Vault,檢索指定的金鑰,並將其作為檔案掛載到容器的/mnt/secrets目錄。這種方法有幾個重要優勢:

  1. 敏感訊息永遠不會儲存在Kubernetes中;
  2. 利用Vault的高階功能如金鑰輪換、存取稽核和精細許可權控制;
  3. 集中管理所有環境的金鑰。在實際佈署中,還需要設定Vault認證和適當的存取策略,確保只有授權的Pod才能取得特定金鑰。