在容器化技術迅速發展的今日,Kubernetes 已成為企業佈署雲端應用的核心平台。然而,隨著其普及,Kubernetes 的安全問題也日益凸顯。在處理敏感資料和關鍵業務應用時,安全防護不再是可選項,而是必要條件。

安全從來不是絕對的概念。作為技術工作者,我從不會簡單地宣稱某個系統「安全」,而是會評估其相對安全程度。本文將分享如何提升 Kubernetes 叢集的安全性,並根據不同風險情境提供相應的防護策略。

Kubernetes 安全威脅模型

在深入討論具體安全措施前,我們需要了解 Kubernetes 環境中的主要攻擊向量:

  1. 叢集設定漏洞 - 不安全的 API Server 設定、etcd 暴露等
  2. 身分驗證缺陷 - 弱密碼、過期憑證、許可權管理不當
  3. 容器映像問題 - 未修補的漏洞、惡意程式碼、供應鏈攻擊
  4. 執行時安全 - 容器逃逸、許可權提升、資源濫用
  5. 網路安全 - 未加密通訊、網路政策缺失、服務暴露
  6. 機密資訊管理 - 未加密的 Secret、硬編碼憑證

本文將逐一探討這些安全領域,提供實用的防護策略和最佳實踐。

叢集安全基礎:保護 Kubernetes 核心元件

Kubernetes 叢集的安全性首先取決於其核心元件的設定。不當的設定可能導致整個環境遭受攻擊。

API Server 安全加固

API Server 是 Kubernetes 的核心控制點,所有操作都需經過它處理,因此它是首要的安全防護目標。

apiVersion: v1
kind: Pod
metadata:
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - name: kube-apiserver
    image: k8s.gcr.io/kube-apiserver:v1.21.0
    command:
    - kube-apiserver
    - --authorization-mode=Node,RBAC
    - --enable-admission-plugins=NodeRestriction,PodSecurityPolicy
    - --anonymous-auth=false
    - --insecure-port=0
    - --audit-log-path=/var/log/kubernetes/audit.log
    - --audit-log-maxage=30
    - --tls-cert-file=/etc/kubernetes/pki/apiserver.crt
    - --tls-private-key-file=/etc/kubernetes/pki/apiserver.key

這個設定範例展示了 API Server 的安全設定:

  • 啟用 Node 和 RBAC 授權模式,確保所有操作都受到適當的許可權控制
  • 啟用 NodeRestriction 和 PodSecurityPolicy 准入控制器,限制 Pod 的許可權
  • 停用匿名存取,要求所有請求都必須經過身分驗證
  • 關閉不安全的 HTTP 連線埠,只允許 HTTPS 連線
  • 啟用稽核日誌,記錄所有操作以便追蹤可能的安全事件
  • 設定 TLS 憑證,確保通訊加密

在實際佈署中,我發現許多團隊忽略了 API Server 的安全設定,直到發生安全事件才意識到問題。建議在初始設定時就應用這些安全引數,並定期檢查設定是否符合最新的安全建議。

Kubelet 安全設定

Kubelet 是每個節點上的代理,負責管理容器的生命週期。不安全的 Kubelet 設定可能允許攻擊者在節點上執行惡意容器。

apiVersion: kubelet.config.k8s.io/v1beta1
kind: KubeletConfiguration
authentication:
  anonymous:
    enabled: false
  webhook:
    enabled: true
  x509:
    clientCAFile: /etc/kubernetes/pki/ca.crt
authorization:
  mode: Webhook
readOnlyPort: 0
tlsCertFile: /etc/kubernetes/pki/kubelet.crt
tlsPrivateKeyFile: /etc/kubernetes/pki/kubelet.key

這個 Kubelet 設定範例實施了以下安全措施:

  • 停用匿名存取,確保所有連線都需要身分驗證
  • 啟用 Webhook 身分驗證,與 API Server 協調驗證請求
  • 設定 X.509 憑證認證,確保只有受信任的客戶端可以連線
  • 設定授權模式為 Webhook,將授權決策委託給 API Server
  • 關閉唯讀連線埠,減少攻擊面
  • 設定 TLS 憑證,確保通訊加密

在我的實踐中,發現許多安全事件源於 Kubelet 設定不當。特別是允許匿名存取或開放唯讀連線埠,這些設定在開發環境中可能方便除錯,但在生產環境中卻是嚴重的安全隱患。

安全執行 etcd

etcd 儲存了 Kubernetes 的所有設定和狀態訊息,包括敏感的 Secret 資料。保護 etcd 至關重要。

# 啟動 etcd 的安全設定範例
etcd \
  --cert-file=/etc/kubernetes/pki/etcd/server.crt \
  --key-file=/etc/kubernetes/pki/etcd/server.key \
  --client-cert-auth \
  --trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt \
  --peer-cert-file=/etc/kubernetes/pki/etcd/peer.crt \
  --peer-key-file=/etc/kubernetes/pki/etcd/peer.key \
  --peer-client-cert-auth \
  --peer-trusted-ca-file=/etc/kubernetes/pki/etcd/ca.crt \
  --data-dir=/var/lib/etcd

這個 etcd 啟動設定實施了多層安全防護:

  • 啟用 TLS 加密,保護客戶端與 etcd 之間的通訊
  • 啟用客戶端憑證認證,確保只有授權客戶端可以存取
  • 設定受信任的 CA 證書,用於驗證客戶端憑證
  • 啟用節點間通訊的 TLS 加密和憑證認證
  • 指定資料儲存目錄,便於實施檔案系統級別的保護

etcd 的安全設定常被忽視,但它實際上是 Kubernetes 安全架構中的關鍵環節。我建議將 etcd 佈署在專用節點上,並限制對這些節點的網路存取,同時定期備份 etcd 資料。

Kubernetes Dashboard 安全防護

Kubernetes Dashboard 提供了便捷的 Web 介面,但也可能成為安全風險。2018年,特斯拉雲環境被入侵就是因為暴露了未受保護的 Kubernetes Dashboard。

# Dashboard 安全存取設定
apiVersion: v1
kind: Service
metadata:
  name: kubernetes-dashboard
  namespace: kube-system
spec:
  type: ClusterIP  # 不要使用 LoadBalancer 或 NodePort
  ports:
  - port: 443
    targetPort: 8443
  selector:
    k8s-app: kubernetes-dashboard
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: dashboard-minimal
  namespace: kube-system
rules:
- apiGroups: [""]
  resources: ["secrets"]
  verbs: ["create"]
- apiGroups: [""]
  resources: ["configmaps"]
  verbs: ["get", "update", "delete"]
  resourceNames: ["kubernetes-dashboard-settings"]

這個設定範例展示了 Dashboard 的安全佈署方式:

  • 使用 ClusterIP 服務型別,避免直接從外部存取
  • 限制 Dashboard 的 RBAC 許可權,遵循最小許可權原則
  • 未顯示但建議:設定存取令牌或身分驗證代理

在實際佈署中,我通常建議透過 kubectl proxy 或設定安全的 Ingress 來存取 Dashboard,而不是直接暴露服務。此外,永遠不要給 Dashboard 分配 cluster-admin 許可權,這是一個常見但危險的錯誤。

驗證叢集設定

定期檢查叢集設定是否符合安全最佳實踐至關重要。kube-bench 是一個根據 CIS Kubernetes 基準的開放原始碼工具,可以幫助評估叢集安全性。

# 在主節點上執行 kube-bench
docker run --pid=host -v /etc:/etc:ro -v /var:/var:ro -t aquasec/kube-bench:latest master

# 在工作節點上執行 kube-bench
docker run --pid=host -v /etc:/etc:ro -v /var:ro -t aquasec/kube-bench:latest node

kube-bench 會根據 CIS Kubernetes 安全基準檢查叢集設定,並生成詳細報告,指出潛在的安全問題和改進建議。這個工具對於:

  • 識別不符合安全最佳實踐的設定
  • 提供具體的修復建議
  • 生成可用於合規報告的結果

我經常在新佈署的叢集上執行 kube-bench,並將其納入持續整合流程,確保安全設定不會隨著時間推移而降級。值得注意的是,不是所有的建議都適用於每個環境,需要根據具體情況進行評估。

身分驗證:誰可以存取你的叢集

身分驗證是 Kubernetes 安全的第一道防線,它確定誰可以與叢集互動。

身分識別基礎

Kubernetes 中的身分可以分為兩大類別:

  1. 服務帳戶:供 Pod 內應用程式使用
  2. 使用者:供人類使用者和外部系統使用

與許多系統不同,Kubernetes 本身不管理普通使用者,而是依賴外部身分提供者。

# 服務帳戶範例
apiVersion: v1
kind: ServiceAccount
metadata:
  name: app-service-account
  namespace: production
---
# 將服務帳戶與 Pod 關聯
apiVersion: v1
kind: Pod
metadata:
  name: app-pod
  namespace: production
spec:
  serviceAccountName: app-service-account
  containers:
  - name: app
    image: myapp:1.0.0

這個範例展示瞭如何建立服務帳戶並將其分配給 Pod:

  • 在 production 名稱空間中建立名為 app-service-account 的服務帳戶
  • 將該服務帳戶分配給 app-pod
  • Pod 中的應用程式可以使用此服務帳戶的憑證與 API Server 通訊

服務帳戶是 Kubernetes 中最常用的身分型別,每個名稱空間都有一個預設服務帳戶。然而,我強烈建議為不同的應用程式建立專用的服務帳戶,並限制其許可權,這樣可以減少潛在攻擊的影響範圍。

身分驗證策略

Kubernetes 支援多種身分驗證策略,可以同時啟用多種方式:

  1. X.509 客戶端憑證
  2. 靜態令牌檔案
  3. OpenID Connect (OIDC)
  4. Webhook 令牌認證
  5. 身分驗證代理

在企業環境中,OIDC 是最常用的方法,因為它可以與現有的身分管理系統整合。

# 使用 X.509 客戶端憑證生成 kubeconfig
kubectl config set-credentials developer \
  --client-certificate=developer.crt \
  --client-key=developer.key \
  --embed-certs=true

這個命令設定了使用 X.509 客戶端憑證進行身分驗證:

  • 建立名為 “developer” 的使用者憑證
  • 指定客戶端憑證和私鑰檔案
  • 將憑證嵌入到 kubeconfig 檔案中,而不是參照外部檔案

X.509 憑證認證是最基本的方法,適用於小型團隊或開發環境。對於大型組織,我建議實施 OIDC 認證,將 Kubernetes 身分驗證與企業身分提供者(如 Azure AD、Okta 或 Google)整合。

身分驗證最佳實踐

在設計 Kubernetes 身分驗證策略時,我建議遵循以下最佳實踐:

  1. 避免使用靜態令牌:靜態令牌難以復原,與容易洩露
  2. 實施短期憑證:使用有限生命週期的憑證,減少憑證洩露的風險
  3. 整合企業身分系統:利用現有的身分管理基礎設施
  4. 啟用多因素認證:增加額外的安全層
  5. 稽核身分驗證事件:記錄並監控所有身分驗證嘗試
# API Server 設定 OIDC 範例
apiVersion: v1
kind: Pod
metadata:
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - name: kube-apiserver
    image: k8s.gcr.io/kube-apiserver:v1.21.0
    command:
    - kube-apiserver
    - --oidc-issuer-url=https://accounts.google.com
    - --oidc-client-id=kubernetes
    - --oidc-username-claim=email
    - --oidc-groups-claim=groups

這個設定範例展示瞭如何設定 API Server 使用 Google 作為 OIDC 提供者:

  • 指定 OIDC 發行者 URL
  • 設定客戶端 ID
  • 設定使用 email 宣告作為使用者名
  • 設定使用 groups 宣告對映到 Kubernetes 組

在實際佈署中,我發現 OIDC 整合是最靈活與安全的身分驗證方法,特別是對於已經使用雲端服務提供商的組織。它允許利用現有的使用者管理、多因素認證和單點登入功能。

授權:控制使用者可以做什麼

身分驗證確定誰可以存取叢集,而授權則決定他們可以做什麼。Kubernetes 提供了多種授權模式,其中根據角色的存取控制 (RBAC) 是最強大與靈活的。

授權模式

Kubernetes 支援以下授權模式:

  1. Node:專用於 kubelet 存取
  2. ABAC:根據屬性的存取控制
  3. RBAC:根據角色的存取控制
  4. Webhook:委託給外部 REST 服務
  5. AlwaysDeny/AlwaysAllow:測試用,不應在生產中使用

這些模式可以同時啟用,請求會按順序透過每個授權器,直到其中一個做出決定。

# API Server 授權設定
apiVersion: v1
kind: Pod
metadata:
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - name: kube-apiserver
    image: k8s.gcr.io/kube-apiserver:v1.21.0
    command:
    - kube-apiserver
    - --authorization-mode=Node,RBAC
    # 其他引數...

這個設定啟用了 Node 和 RBAC 授權模式:

  • Node 授權器允許 kubelet 存取與其節點相關的資源
  • RBAC 授權器根據角色和角色繫結決定使用者許可權

在實際佈署中,我強烈建議使用 RBAC 作為主要授權機制。它提供了細粒度的存取控制,並且與 Kubernetes 的宣告式設定模型很好地結合。

使用 RBAC 進行存取控制

RBAC 是 Kubernetes 中最強大的授權機制,它根據角色和許可權定義存取控制。

RBAC 的核心概念包括:

  1. 角色:定義在名稱空間內可執行的操作
  2. 叢集角色:定義在整個叢集範圍內可執行的操作
  3. 角色繫結:將角色與使用者或服務帳戶關聯
  4. 叢集角色繫結:將叢集角色與使用者或服務帳戶關聯
# 建立一個只能讀取 Pod 的角色
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]
---
# 將角色繫結到服務帳戶
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: ServiceAccount
  name: monitoring-sa
  namespace: default
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

這個 RBAC 設定範例:

  • 建立一個名為 pod-reader 的角色,只允許讀取 Pod 資源
  • 建立一個角色繫結,將該角色分配給 monitoring-sa 服務帳戶
  • 服務帳戶現在只能讀取 default 名稱空間中的 Pod,無法修改它們或存取其他資源

RBAC 遵循最小許可權原則,只授予完成任務所需的最小許可權。在設計 RBAC 策略時,我建議從最小許可權開始,然後根據需要逐步增加許可權,而不是一開始就授予過多許可權。

RBAC 最佳實踐

根據我的經驗,以下是實施 RBAC 的一些最佳實踐:

  1. 避免使用 cluster-admin:除非絕對必要,否則不要使用此超級使用者角色
  2. 使用名稱空間隔離:將應用程式分組到名稱空間,並使用名稱空間級別的角色
  3. 定期審查許可權:定期檢查和清理不必要的許可權
  4. 使用組而非個人:將許可權分配給組而不是個人使用者
  5. 實施最小許可權:只授予完成任務所需的最小許可權
# 審查使用者許可權的有用命令
kubectl auth can-i --list --as=system:serviceaccount:default:app-service-account

# 檢查特定操作的授權
kubectl auth can-i get pods --as=system:serviceaccount:default:app-service-account

這些命令可以幫助審查和驗證 RBAC 許可權:

  • 第一個命令列出服務帳戶可以執行的所有操作
  • 第二個命令檢查服務帳戶是否可以執行特定操作(取得 Pod)

我經常使用這些命令來驗證 RBAC 設定是否符合預期,特別是在實施複雜的許可權策略時。這種主動驗證可以幫助發現和修復潛在的許可權問題,避免安全漏洞。

容器映像安全:保護供應鏈

容器安全始於映像安全。不安全的容器映像可能包含漏洞、惡意軟體或後門,危及整個環境。

掃描容器映像

定期掃描容器映像是識別和修復安全漏洞的關鍵步驟。

# 使用 Trivy 掃描容器映像
trivy image nginx:1.19

# 在 CI/CD 流程中整合掃描
trivy image --exit-code 1 --severity HIGH,CRITICAL myapp:latest

這些命令使用 Trivy(一個開放原始碼容器掃描工具)來檢查映像中的漏洞:

  • 第一個命令掃描 nginx:1.19 映像並顯示所有漏洞
  • 第二個命令在發現高危或嚴重漏洞時回傳非零結束碼,適合整合到 CI/CD 流程中

在實際工作中,我發現映像掃描應該在多個階段進行:開發過程中、推播到登入檔時,以及定期掃描已佈署的映像。這種多層掃描策略可以及早發現並修復漏洞。

修補容器映像

發現漏洞後,需要及時修補映像。與傳統系統不同,容器應該透過重建映像而不是在執行時修補來更新。

# 良好的 Dockerfile 實踐
FROM ubuntu:20.04

# 在同一層中更新和安裝,減少層數
RUN apt-get update && \
    apt-get upgrade -y && \
    apt-get install -y --no-install-recommends nginx && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# 使用非特權使用者執行
USER nginx

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

這個 Dockerfile 範例展示了一些安全最佳實踐:

  • 在同一個 RUN 指令中更新和安裝軟體,確保使用最新的安全補丁
  • 安裝後清理 apt 快取,減少映像大小和攻擊面
  • 使用非 root 使用者執行應用程式

在容器世界中,修補策略應該圍繞「不可變基礎設施」原則:不要修改現有容器,而是佈署包含修補程式的新容器。這種方法確保了環境的一致性和可重現性。

CI/CD 最佳實踐

將安全檢查整合到 CI/CD 流程中是確保容器映像安全的關鍵。

# GitLab CI 設定範例
stages:
  - build
  - test
  - scan
  - deploy

build:
  stage: build
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .

vulnerability_scan:
  stage: scan
  script:
    - trivy image --exit-code 1 --severity HIGH,CRITICAL $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
    - docker run --rm -v /var/run/docker.sock:/var/run/docker.sock aquasec/kube-bench:latest image --image $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

deploy:
  stage: deploy
  script:
    - kubectl set image deployment/app container=$CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  only:
    - master

這個 CI/CD 設定範例實施了多層安全檢查:

  • 構建容器映像並標記為提交 SHA
  • 使用 Trivy 掃描映像中的漏洞,如果發現高危或嚴重漏洞則失敗
  • 使用 kube-bench 檢查映像是否符合安全最佳實踐
  • 只有透過所有安全檢查的映像才會佈署到生產環境

在我的實踐中,這種「左移」安全方法(在開發週期早期整合安全檢查)可以顯著減少生產環境中的安全問題。它還培養了開發人員的安全意識,因為他們會立即收到安全問題的反饋。

映像儲存與版本控制

安全地儲存和管理容器映像是容器安全策略的重要組成部分。

# 設定 Pod 使用私有登入檔
apiVersion: v1
kind: Pod
metadata:
  name: private-image-pod
spec:
  containers:
  - name: app
    image: private-registry.example.com/myapp:v1.2.3
  imagePullSecrets:
  - name: registry-credentials
---
# 建立映像提取金鑰
apiVersion: v1
kind: Secret
metadata:
  name: registry-credentials
type: kubernetes.io/dockerconfigjson
data:
  .dockerconfigjson: <base64-encoded-docker-config>

這個設定範例展示瞭如何使用私有登入檔中的映像:

  • 指定完整的映像路徑,包括登入檔網域名稱
  • 使用 imagePullSecrets 提供存取私有登入檔的憑證
  • 建立包含登入檔認證的 Secret

在生產環境中,我強烈建議使用私有容器登入檔,並實施以下最佳實踐:

  • 啟用映像掃描功能
  • 實施映像簽名和驗證
  • 定期清理未使用的映像
  • 強制使用特定的映像標籤,避免使用 “latest”

映像信任與供應鏈安全

確保只佈署受信任的容器映像是防止供應鏈攻擊的關鍵。

# 使用 Notary 簽名映像
docker trust sign myregistry.example.com/myapp:1.0.0

# 在 Kubernetes 中驗證映像簽名
# 需要設定準入控制器,如 Portieris 或 Connaisseur

這個範例展示瞭如何使用 Docker Content Trust (根據 Notary) 簽名和驗證映像:

  • 簽名過程為映像建立數字簽名,證明其來源和完整性
  • 在 Kubernetes 中,可以使用准入控制器確保只佈署經過簽名的映像

映像簽名是建立容器供應鏈信任的重要機制。在我的實踐中,我發現實施映像簽名可以顯著降低佈署惡意或未經授權映像的風險,特別是在多團隊環境中。

最小化映像以減少攻擊面

較小的容器映像不僅佈署更快,而與通常更安全,因為它們包含較少的潛在漏洞。

# 多階段構建範例
FROM golang:1.16 AS builder
WORKDIR /app
COPY . .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:3.14
RUN apk --no-cache add ca-certificates && \
    adduser -D -H -h /app appuser
WORKDIR /app
COPY --from=builder /app/app .
USER appuser
CMD ["./app"]

這個多階段 Dockerfile 展示瞭如何建立最小化的容器映像:

  • 使用 golang 映像作為構建階段,編譯應用程式
  • 使用輕量級的 alpine 映像作為最終映像
  • 只複製編譯好的二進位檔案,不包含原始碼和構建工具
  • 增加必要的 CA 證書以支援 HTTPS
  • 建立並使用非特權使用者執行應用程式

在我的容器化專案中,我經常使用多階段構建和根據 Alpine 或 distroless 的基礎映像。這種方法可以將映像大小減少 90% 以上,同時顯著減少攻擊面。

安全執行容器:執行時防護

即使容器映像是安全的,不安全的執行時設定也可能導致安全漏洞。

拒絕 Root 許可權

在容器中以 root 身份執行應用程式是一種常見但危險的做法。如果容器被攻破,攻擊者可能獲得對主機的存取許可權。

# 設定 Pod 安全上下文
apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  containers:
  - name: app
    image: myapp:1.0.0
    securityContext:
      allowPrivilegeEscalation: false
      readOnlyRootFilesystem: true
      capabilities:
        drop:
        - ALL

這個 Pod 設定實施了多層安全控制:

  • 以 UID 1000 執行容器程式,而不是 root (UID 0)
  • 設定程式的主組為 GID 3000
  • 將掛載的卷的所有權設定為 GID 2000
  • 禁止許可權提升,防止程式獲得更多許可權
  • 將根檔案系統設定為只讀,防止修改系統檔案
  • 刪除所有 Linux 功能,只保留絕對必要的功能

在我的實踐中,這些安全上下文設定是防止容器逃逸和許可權提升攻擊的關鍵。即使應用程式被攻破,這些限制也能顯著減少攻擊者的活動範圍。

准入控制

Kubernetes 准入控制器可以在資源建立或修改時強制執行安全策略。

# 設定 PodSecurityPolicy
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
  name: restricted
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
  readOnlyRootFilesystem: true

這個 PodSecurityPolicy 定義了嚴格的安全要求:

  • 禁止特權容器和許可權提升
  • 刪除所有 Linux 功能
  • 限制可以使用的卷型別
  • 禁止存取主機網路、IPC 和 PID 名稱空間
  • 要求容器以非 root 使用者執行
  • 限制補充組 ID 和 fsGroup ID 的範圍
  • 要求根檔案系統為只讀

PodSecurityPolicy 在 Kubernetes v1.21 中被棄用,並在 v1.25 中移除,取而代之的是 Pod Security Admission。然而,理解這些安全控制仍然很重要,因為它們代表了容器安全的最佳實踐。

安全邊界

瞭解 Kubernetes 中的安全邊界對於設計安全的多租戶環境至關重要。

# 使用名稱空間隔離
apiVersion: v1
kind: Namespace
metadata:
  name: team-a
  labels:
    team: a
---
# 使用網路政策限制通訊
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-from-other-namespaces
  namespace: team-a
spec:
  podSelector: {}
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          team: a

這個設定範例展示瞭如何使用名稱空間和網路政策建立隔離環境:

  • 建立名為 team-a 的名稱空間,並增加標籤
  • 定義網路政策,只允許來自同一團隊名稱空間的流量

在 Kubernetes 中,名稱空間提供了邏輯隔離,但不提供強安全隔離。為了增強隔離,我建議:

  • 實施嚴格的網路政策
  • 使用資源配額限制資源使用
  • 為每個租戶使用單獨的節點池
  • 考慮使用 gVisor 或 Kata Containers 等容器執行時沙箱

安全策略實施

Kubernetes 提供了多種機制來實施安全策略,包括 Pod Security Admission、OPA Gatekeeper 和網路政策。

# Pod Security Standards 設定
apiVersion: v1
kind: Namespace
metadata:
  name: production
  labels:
    pod-security.kubernetes.io/enforce: restricted
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/warn: restricted
---
# OPA Gatekeeper 約束範例
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sRequiredLabels
metadata:
  name: require-team-label
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Namespace"]
  parameters:
    labels: ["team"]

這些設定範例展示瞭如何實施安全策略:

  • 第一個範例使用 Pod Security Admission 在 production 名稱空間中強制執行 “restricted” 安全標準
  • 第二個範例使用 OPA Gatekeeper 要求所有名稱空間都有 “team” 標籤

在我的實踐中,我發現這些策略實施機制最有效的是分層應用:

  • 使用 Pod Security Admission 強制執行基本安全標準
  • 使用 OPA Gatekeeper 實施組織特定的策略
  • 使用網路政策控制 Pod 間通訊
  • 使用資源配額防止資源耗盡攻擊

機密管理:保護敏感資料

安全管理密碼、API 金鑰和其他敏感訊息是 Kubernetes 安全的關鍵方面。

應用最小許可權原則

在機密管理中,最小許可權原則意味著只向需要的 Pod 提供必要的機密。

# 限制 Secret 存取的 RBAC 設定
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: secret-reader
rules:
- apiGroups: [""]
  resources: ["secrets"]
  resourceNames: ["app-secret"]  # 只允許存取特定的 Secret
  verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-app-secret
  namespace: default
subjects:
- kind: ServiceAccount
  name: app-service-account
  namespace: default
roleRef:
  kind: Role
  name: secret-reader
  apiGroup: rbac.authorization.k8s.io

這個 RBAC 設定範例實施了最小許可權原則:

  • 建立一個角色,只允許讀取特定的 Secret (app-secret)
  • 將該角色繫結到應用程式的服務帳戶
  • 服務帳戶只能存取它需要的 Secret,無法存取其他 Secret

在設計機密管理策略時,我建議將機密分解為最小單位,並只向需要的元件提供存取許可權。這種方法可以限制潛在洩露的影響範圍。

Secret 加密

預設情況下,Kubernetes 將 Secret 以 base64 編碼(非加密)的形式儲存在 etcd 中。啟用 Secret 加密是保護敏感資料的關鍵步驟。

# Secret 加密設定
apiVersion: apiserver.config.k8s.io/v1
kind: EncryptionConfiguration
resources:
  - resources:
      - secrets
    providers:
      - aescbc:
          keys:
            - name: key1
              secret: <base64-encoded-key>
      - identity: {}

這個設定啟用了 Secret 的靜態加密:

  • 使用 AES-CBC 加密演算法
  • 指定加金鑰
  • 包含 identity 提供者作為回退

在生產環境中,我強烈建議啟用 Secret 加密,並考慮使用外部金鑰管理系統(如 AWS KMS 或 HashiCorp Vault)來管理加金鑰。這提供了更強的安全性和更好的金鑰管理。

Kubernetes Secret 儲存

Kubernetes Secret 是儲存和管理敏感資料的原生方式。

# 建立 Secret
apiVersion: v1
kind: Secret
metadata:
  name: db-credentials
type: Opaque
data:
  username: YWRtaW4=  # base64 encoded "admin"
  password: cGFzc3dvcmQxMjM=  # base64 encoded "password123"
---
# 在 Pod 中使用 Secret
apiVersion: v1
kind: Pod
metadata:
  name: app-pod
spec:
  containers:
  - name: app
    image: myapp:1.0.0
    env:
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: username
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: db-credentials
          key: password

這個範例展示瞭如何建立和使用 Kubernetes Secret:

  • 建立包含資料函式庫憑證的 Secret
  • 在 Pod 中將 Secret 資料作為環境變數使用

雖然 Kubernetes Secret 提供了基本的機密管理功能,但它有一些限制:

  • 預設情況下未加密(需要額外設定)
  • 缺乏高階功能,如版本控制和自動輪換
  • 所有具有名稱空間存取許可權的管理員都可以讀取 Secret

對於更高階的機密管理需求,我建議考慮外部解決方案,如 HashiCorp Vault 或雲提供商的機密管理服務。

將機密傳遞給容器化程式碼

有多種方法可以將機密傳遞給容器中的應用程式,每種方法都有其優缺點。

# 使用環境變數
apiVersion: v1
kind: Pod
metadata:
  name: env-pod
spec:
  containers:
  - name: app
    image: myapp:1.0.0
    env:
    - name: API_KEY
      valueFrom:
        secretKeyRef:
          name: api-secret
          key: api-key
---
# 使用卷掛載
apiVersion: v1
kind: Pod
metadata:
  name: volume-pod
spec:
  containers:
  - name: app
    image: myapp:1.0.0
    volumeMounts:
    - name: secret-volume
      mountPath: /etc/secrets
      readOnly: true
  volumes:
  - name: secret-volume
    secret:
      secretName: api-secret

這個範例展示了兩種將機密傳遞給容器的方法:

  • 環境變數:直接將機密作為環境變數傳遞
  • 卷掛載:將機密作為檔案掛載到容器檔案系統

在我的實踐中,我發現卷掛載通常比環境變數更安全,原因如下:

  • 環境變數可能出現在日誌或錯誤報告中
  • 環境變數對容器中的所有程式可見
  • 卷掛載的機密可以設定更嚴格的檔案許可權
  • 某些應用程式框架專門支援從檔案讀取機密

機密輪換與復原

定期輪換機密是安全最佳實踐,可以限制潛在洩露的影響。

# 使用 Kubernetes 定時工作 輪換機密
apiVersion: batch/v1beta1
kind: 定時工作
metadata:
  name: secret-rotation
spec:
  schedule: "0 0 * * 0"  # 每週日午夜
  jobTemplate:
    spec:
      template:
        spec:
          serviceAccountName: secret-manager
          containers:
          - name: rotate-secrets
            image: secret-rotation:1.0.0
            command: ["./rotate-secrets.sh"]
          restartPolicy: OnFailure

這個 定時工作 設定範例展示瞭如何自動輪換機密:

  • 建立一個定期執行的作業
  • 使用具有適當許可權的服務帳戶
  • 執行機密輪換指令碼

在實際實施中,機密輪換通常涉及以下步驟:

  1. 生成新的機密值
  2. 更新使用該機密的服務
  3. 更新 Kubernetes Secret
  4. 驗證所有服務是否正常執行
  5. 復原舊的機密值

對於關鍵系統,我建議實施自動機密輪換,並確保應用程式能夠在不中斷服務的情況下處理機密更改。

高階主題:深入 Kubernetes 安全

監控、警示與稽核

全面的安全策略必須包括監控和稽核,以便及時發現和回應安全事件。

# 設定 Kubernetes 稽核
apiVersion: v1
kind: Pod
metadata:
  name: kube-apiserver
  namespace: kube-system
spec:
  containers:
  - name: kube-apiserver
    image: k8s.gcr.io/kube-apiserver:v1.21.0
    command:
    - kube-apiserver
    - --audit-log-path=/var/log/kubernetes/audit.log
    - --audit-log-maxage=30
    - --audit-log-maxbackup=10
    - --audit-log-maxsize=100
    - --audit-policy-file=/etc/kubernetes/audit-policy.yaml
    volumeMounts:
    - name: audit-policy
      mountPath: /etc/kubernetes/audit-policy.yaml
      readOnly: true
    - name: audit-log
      mountPath: /var/log/kubernetes/
  volumes:
  - name: audit-policy
    hostPath:
      path: /etc/kubernetes/audit-policy.yaml
      type: File
  - name: audit-log
    hostPath:
      path: /var/log/kubernetes/
      type: DirectoryOrCreate

這個設定範例啟用了 Kubernetes 稽核日誌:

  • 指定稽核日誌的路徑和保留策略
  • 參照稽核策略檔案,定義要記錄的事件
  • 設定卷掛載以存取策略檔案和寫入日誌

在我的實踐中,我發現以下監控和稽核策略特別有效:

  • 記錄所有認證和授權決策
  • 監控異常的 API 呼叫模式
  • 跟蹤特權操作和設定更改
  • 設定根據異常行為的警示
  • 將稽核日誌傳送到中央日誌管理系統

主機安全

容器安全始於主機安全。不安全的主機可能危及所有執行在其上的容器。

# 加固主機安全的基本步驟
# 1. 最小化作業系統
# 2. 定期更新和修補
# 3. 使用主機防火牆
# 4. 實施強化的 Linux 安全模組 (如 SELinux 或 AppArmor)
# 5. 停用不必要的服務
# 6. 實施強密碼策略和 SSH 金鑰認證
# 7. 使用入侵檢測系統
# 8. 監控系統日誌

這些步驟概述了加固 Kubernetes 節點的基本方法:

  • 使用最小化的作業系統減少攻擊面
  • 保持系統更新以修復已知漏洞
  • 使用防火牆限制網路存取
  • 啟用 SELinux 或 AppArmor 提供額外的安全層
  • 移除不必要的服務和軟體
  • 加強身分驗證機制
  • 實施監控和入侵檢測

在設計 Kubernetes 安全策略時,我始終強調主機安全的重要性。容器提供了隔離,但不是完美的安全邊界。如果主機被攻破,所有容器都可能受到影響。

沙箱化與執行時保護

容器執行時安全是防止容器逃逸和其他執行時攻擊的關鍵。

# 使用 gVisor 執行容器
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
  name: gvisor
handler: runsc
---
apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  runtimeClassName: gvisor
  containers:
  - name: app
    image: myapp:1.0.0

這個設定範例展示瞭如何使用 gVisor 容器執行時沙箱:

  • 定義 RuntimeClass 資源,指定 gVisor (runsc) 處理程式
  • 在 Pod 規範中參照該 RuntimeClass

容器執行時沙箱(如 gVisor 和 Kata Containers)提供了額外的隔離層,可以顯著減少容器逃逸的風險。在處理不受信任的工作負載或多租戶環境時,我強烈建議考慮這些技術。

多租戶環境

在多租戶 Kubernetes 環境中,安全隔離尤為重要。

# 多租戶隔離策略
# 1. 使用名稱空間隔離租戶
apiVersion: v1
kind: Namespace
metadata:
  name: tenant-a
---
# 2. 實施資源配額
apiVersion: v1
kind: ResourceQuota
metadata:
  name: tenant-quota
  namespace: tenant-a
spec:
  hard:
    pods: "10"
    requests.cpu: "4"
    requests.memory: 8Gi
    limits.cpu: "8"
    limits.memory: 16Gi
---
# 3. 使用網路政策隔離
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: tenant-isolation
  namespace: tenant-a
spec:
  podSelector: {}
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          tenant: a

這個設定範例展示了多租戶環境中的隔離策略:

  • 為每個租戶建立專用名稱空間
  • 實施資源配額,防止一個租戶消耗過多資源
  • 使用網路政策限制跨租戶通訊

在設計多租戶 Kubernetes 環境時,我建議考慮以下額外措施:

  • 使用節點選擇器將不同租戶的工作負載排程到不同的節點
  • 為每個租戶實施自定義准入控制器
  • 考慮使用容器執行時沙箱提供更強的隔離
  • 實施詳細的稽核和監控,跟蹤跨租戶活動

網路保護

網路安全是 Kubernetes 安全的重要組成部分,可以防止未授權存取和橫向移動。

# 預設拒絕所有流量的網路政策
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
  - Ingress
  - Egress
---
# 允許特定流量的網路政策
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-specific-traffic
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: web
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - podSelector:
        matchLabels:
          app: frontend
    ports:
    - protocol: TCP
      port: 80
  egress:
  - to:
    - podSelector:
        matchLabels:
          app: database
    ports:
    - protocol: TCP
      port: 5432

這個網路政策設定範例實施了零信任網路模型:

  • 首先,預設拒絕所有入站和出站流量
  • 然後,明確允許特定的必要流量:
    • 允許標記為 “frontend” 的 Pod 存取 Web 服務的 80 連線埠
    • 允許 Web 服務存取資料函式庫服務的 5432 連線埠

在設計 Kubernetes 網路安全策略時,我建議採用"預設拒絕,明確允許"的方法。這種方法雖然初始設定更複雜,但提供了更強的安全保障,防止未授權的網路存取和橫向移動。