在雲端原生應用程式的安全架構中,秘密管理始終是最關鍵且最具挑戰性的環節之一。從資料庫憑證到 API 金鑰,從 TLS 證書到服務令牌,這些敏感資訊的安全儲存、傳輸與存取控制,直接影響著整個系統的安全性。Kubernetes 作為容器編排的事實標準,雖然內建了基礎的 Secret 機制,卻難以滿足企業級應用對於秘密管理的複雜需求。
傳統的 Kubernetes Secret 將敏感資訊以 Base64 編碼方式儲存在 etcd 資料庫中。這種設計雖然提供了基本的隔離機制,卻存在著明顯的安全隱患。Base64 編碼並非加密,任何能夠存取 etcd 或擁有足夠權限的使用者都能輕易解碼這些資訊。更棘手的問題在於,當組織採用 GitOps 工作流程時,如何在版本控制系統中安全地儲存這些敏感組態成為了一個兩難的問題。將 Secret 明文提交到 Git 儲存庫顯然不可接受,但不納入版本控制又會破壞基礎設施即程式碼的完整性。
為了解決這些挑戰,Kubernetes 生態系統發展出多種進階的秘密管理方案。SealedSecrets 透過非對稱加密技術,讓開發者能夠安全地將加密後的秘密提交到版本控制系統。Secret Store CSI Driver 則提供了與企業級外部秘密管理平台的整合能力,讓 Kubernetes 能夠利用 Azure Key Vault、HashiCorp Vault 等專業工具的進階功能。服務網格技術的引入,更進一步強化了服務間通訊的安全性,透過自動化的證書管理與 mTLS 機制,建構起零信任安全架構的基礎。
SealedSecrets 的加密機制與實作原理
SealedSecrets 是 Bitnami 開發的開源專案,它透過引入非對稱加密機制,優雅地解決了 Kubernetes Secret 在版本控制中的安全問題。這個方案的核心思想是將加密與解密的職責進行分離,讓加密過程可以在任何地方執行,而解密操作則僅限於 Kubernetes 叢集內部。
從技術架構來看,SealedSecrets 包含兩個主要元件。第一個是運行在 Kubernetes 叢集中的控制器,它負責監控 SealedSecret 資源的變化,並執行解密操作。第二個是命令列工具 kubeseal,開發者使用這個工具在本地環境中加密原始的 Secret 資料。控制器在啟動時會生成一對 RSA 公私鑰,公鑰用於加密操作,私鑰則被安全地儲存在叢集內部,專門用於解密。
這種設計帶來了顯著的安全優勢。開發者只需要擁有公鑰就能夠加密秘密,而無需接觸私鑰或直接存取 Kubernetes 叢集。加密後的 SealedSecret 可以安全地提交到 Git 儲存庫,因為沒有私鑰就無法解密這些資料。當 SealedSecret 被部署到 Kubernetes 叢集時,控制器會自動使用私鑰解密,並建立對應的標準 Kubernetes Secret 資源。
在實務部署中,安裝 SealedSecrets 控制器是第一步。這個過程通常透過 Helm Chart 或 Kubectl 直接套用 YAML 定義完成。控制器會在 kube-system 命名空間中運行,並自動生成加密金鑰對。
# 使用 Kubectl 安裝 SealedSecrets 控制器
# 這個指令會從官方儲存庫下載最新的部署定義
# 並在 kube-system 命名空間中建立控制器
kubectl apply -f https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/controller.yaml
# 驗證控制器是否正常運行
# 檢查 Pod 的狀態,確保進入 Running 狀態
kubectl get pods -n kube-system -l name=sealed-secrets-controller
# 查看控制器的日誌
# 這有助於確認控制器已正確啟動並生成金鑰對
kubectl logs -n kube-system -l name=sealed-secrets-controller
安裝完控制器後,下一步是安裝 kubeseal 命令列工具。這個工具提供了友善的介面來加密 Secret 資料,支援從檔案讀取或標準輸入接收資料。
# 下載並安裝 kubeseal 工具
# 這個範例使用 macOS 的安裝方式
# Linux 使用者需要下載對應的二進位檔案
wget https://github.com/bitnami-labs/sealed-secrets/releases/download/v0.24.0/kubeseal-0.24.0-darwin-amd64.tar.gz
# 解壓縮並安裝到系統路徑
tar -xvzf kubeseal-0.24.0-darwin-amd64.tar.gz
sudo install -m 755 kubeseal /usr/local/bin/kubeseal
# 驗證安裝是否成功
kubeseal --version
有了這些工具後,我們就可以開始建立加密的秘密。整個流程從建立標準的 Kubernetes Secret 定義開始,然後使用 kubeseal 將其轉換為 SealedSecret。
# 原始的 Secret 定義
# 這個檔案包含敏感資訊,不應該提交到版本控制系統
apiVersion: v1
kind: Secret
metadata:
# Secret 的名稱
name: database-credentials
# Secret 所屬的命名空間
namespace: production
type: Opaque
# stringData 允許直接使用明文定義秘密值
# Kubernetes 會自動將這些值轉換為 Base64 編碼
stringData:
# 資料庫使用者名稱
username: admin
# 資料庫密碼
password: super-secret-password
# 資料庫連線字串
connection-string: postgresql://admin:super-secret-password@db.example.com:5432/appdb
建立了原始 Secret 定義後,使用 kubeseal 進行加密轉換。這個過程會連線到 Kubernetes 叢集,取得控制器的公鑰,然後使用這個公鑰加密秘密資料。
# 使用 kubeseal 加密 Secret
# --format yaml 指定輸出格式為 YAML
# --cert 參數可以指定離線公鑰檔案,避免每次都連線叢集
kubeseal --format yaml < secret.yaml > sealed-secret.yaml
# 如果需要離線加密,可以先匯出公鑰
# 這在 CI/CD 流程中特別有用
kubeseal --fetch-cert > pub-cert.pem
# 使用離線公鑰進行加密
# 這樣就不需要直接存取 Kubernetes 叢集
kubeseal --format yaml --cert pub-cert.pem < secret.yaml > sealed-secret.yaml
# 查看生成的 SealedSecret
cat sealed-secret.yaml
生成的 SealedSecret 資源包含了加密後的資料,這個檔案可以安全地提交到版本控制系統。
# 加密後的 SealedSecret 定義
# 這個檔案可以安全地儲存在 Git 儲存庫中
apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
name: database-credentials
namespace: production
spec:
# 加密後的資料
# 每個欄位都使用控制器的公鑰加密
encryptedData:
# 加密後的使用者名稱
# 這是一個長字串的 Base64 編碼加密資料
username: AgBvD8qN9xK....(省略大量字元)
# 加密後的密碼
password: AgC2Mk5pT7L....(省略大量字元)
# 加密後的連線字串
connection-string: AgA3nQ8sR2M....(省略大量字元)
# 定義範本,指定解密後產生的 Secret 類型與名稱
template:
metadata:
name: database-credentials
namespace: production
type: Opaque
當這個 SealedSecret 被部署到 Kubernetes 叢集時,控制器會自動執行解密操作,並建立對應的標準 Secret 資源。
# 部署 SealedSecret 到 Kubernetes
# 這個操作會觸發控制器執行解密
kubectl apply -f sealed-secret.yaml
# 驗證 Secret 是否被正確建立
# 控制器會自動將 SealedSecret 轉換為 Secret
kubectl get secret database-credentials -n production
# 查看 Secret 的詳細資訊
# 注意:這只會顯示 Base64 編碼的值,不會顯示明文
kubectl describe secret database-credentials -n production
# 在 Pod 中使用這個 Secret
# 建立一個使用此 Secret 的 Pod 定義
應用程式可以透過環境變數或掛載檔案的方式使用這個 Secret,與使用標準 Kubernetes Secret 的方式完全相同。
# 在 Pod 中使用 Secret 的範例
apiVersion: v1
kind: Pod
metadata:
name: database-app
namespace: production
spec:
containers:
- name: app
image: myapp:latest
# 透過環境變數注入 Secret
env:
# 從 Secret 讀取使用者名稱
- name: DB_USERNAME
valueFrom:
secretKeyRef:
name: database-credentials
key: username
# 從 Secret 讀取密碼
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: database-credentials
key: password
# 從 Secret 讀取連線字串
- name: DB_CONNECTION_STRING
valueFrom:
secretKeyRef:
name: database-credentials
key: connection-string
SealedSecrets 的安全性建立在加密金鑰的保護上。控制器的私鑰儲存在 Kubernetes Secret 中,這意味著只有擁有叢集管理權限的人員才能存取。在災難恢復場景中,備份這個私鑰變得至關重要,因為失去私鑰就無法解密既有的 SealedSecret。
# 備份 SealedSecrets 控制器的加密金鑰
# 這個 Secret 包含了私鑰,必須安全儲存
kubectl get secret -n kube-system sealed-secrets-key -o yaml > sealed-secrets-key-backup.yaml
# 在新叢集中恢復金鑰
# 這允許在災難恢復時解密既有的 SealedSecret
kubectl apply -f sealed-secrets-key-backup.yaml
# 重新啟動控制器以載入恢復的金鑰
kubectl delete pod -n kube-system -l name=sealed-secrets-controller
SealedSecrets 提供了不同的加密範圍選項,讓開發者能夠根據需求調整安全性與靈活性的平衡。預設的 strict 模式將加密綁定到特定的命名空間與名稱,提供最高的安全性。namespace-wide 模式允許在同一命名空間內重複使用 SealedSecret。cluster-wide 模式則提供最大的靈活性,但安全性相對較低。
Secret Store CSI Driver 的外部整合架構
Secret Store CSI Driver 代表了 Kubernetes 秘密管理的另一個重要方向,它透過 Container Storage Interface 標準,將外部秘密管理平台整合到 Kubernetes 生態系統中。這種方法讓組織能夠利用既有的企業級秘密管理基礎設施,避免在 Kubernetes 中重複建構相同的功能。
從架構設計來看,Secret Store CSI Driver 採用了 Kubernetes 標準的 CSI 介面。CSI 原本是為了標準化儲存系統的整合而設計,但其抽象層次同樣適用於秘密管理。透過實作 CSI 介面,Secret Store Driver 能夠以標準化的方式將外部秘密掛載到 Pod 中,就像掛載一般的儲存磁碟區一樣。
這個架構的核心優勢在於其模組化設計。CSI Driver 本身只負責與 Kubernetes 的介面整合,而與特定外部平台的互動則由對應的 Provider 負責。這種職責分離讓系統能夠支援多種不同的秘密管理平台,包括 Azure Key Vault、AWS Secrets Manager、Google Cloud Secret Manager 與 HashiCorp Vault 等。
在技術實作層面,Secret Store CSI Driver 以 DaemonSet 的形式運行在每個節點上。這種部署方式確保了任何節點上的 Pod 都能夠存取外部秘密。Driver 在節點上以特權模式運行,能夠直接操作檔案系統,將秘密掛載為 tmpfs 記憶體檔案系統。這種設計避免了將敏感資訊寫入磁碟,大幅提升了安全性。
部署 Secret Store CSI Driver 的第一步是安裝核心元件。這個過程通常透過 Helm Chart 完成,能夠自動處理所有必要的資源建立。
# 添加 Secrets Store CSI Driver 的 Helm 儲存庫
# 這個儲存庫包含了官方維護的 Chart
helm repo add secrets-store-csi-driver \
https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts
# 更新 Helm 儲存庫索引
# 確保能夠取得最新版本的 Chart
helm repo update
# 安裝 CSI Driver
# --namespace kube-system 將元件安裝在系統命名空間
# --set syncSecret.enabled=true 啟用同步 Secret 功能
# --set enableSecretRotation=true 啟用自動輪換功能
helm install csi-secrets-store \
secrets-store-csi-driver/secrets-store-csi-driver \
--namespace kube-system \
--set syncSecret.enabled=true \
--set enableSecretRotation=true
# 驗證安裝是否成功
# 檢查 DaemonSet 是否在所有節點上運行
kubectl get daemonset -n kube-system -l app=secrets-store-csi-driver
# 查看 Driver Pod 的狀態
kubectl get pods -n kube-system -l app=secrets-store-csi-driver
安裝核心 Driver 後,還需要安裝特定的 Provider。以 Azure Key Vault 為例,需要額外安裝 Azure Provider。
# 安裝 Azure Key Vault Provider
# 這個 Provider 負責與 Azure Key Vault 進行互動
kubectl apply -f https://raw.githubusercontent.com/Azure/secrets-store-csi-driver-provider-azure/master/deployment/provider-azure-installer.yaml
# 驗證 Provider 是否正常運行
kubectl get pods -n kube-system -l app=csi-secrets-store-provider-azure
# 查看 Provider 的日誌
kubectl logs -n kube-system -l app=csi-secrets-store-provider-azure
完成 Driver 與 Provider 的安裝後,需要定義 SecretProviderClass 資源。這個資源描述了如何從外部平台取得秘密,包括認證方式、目標資源與存取權限等資訊。
# SecretProviderClass 定義範例
# 這個資源描述如何從 Azure Key Vault 取得秘密
apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
# SecretProviderClass 的名稱
# Pod 會引用這個名稱來指定要使用的組態
name: azure-keyvault-provider
# 部署的命名空間
# SecretProviderClass 是命名空間範圍的資源
namespace: production
spec:
# 指定使用的 Provider
# azure 表示使用 Azure Key Vault Provider
provider: azure
# Provider 特定的參數
parameters:
# Azure Key Vault 的租用戶 ID
# 這個 ID 用於 Azure AD 認證
tenantId: "your-tenant-id"
# Azure Key Vault 的名稱
# Driver 會從這個 Key Vault 取得秘密
keyvaultName: "your-keyvault-name"
# 指定要取得的秘密物件
# 這是一個 JSON 陣列,每個物件定義一個要取得的秘密
objects: |
array:
- |
objectName: database-password
objectType: secret
objectVersion: ""
- |
objectName: api-key
objectType: secret
objectVersion: ""
# 使用 Pod 身份進行認證
# 這需要在 Azure 中設定 Managed Identity
usePodIdentity: "true"
# 定義同步到 Kubernetes Secret 的組態
# 這是選用功能,允許將外部秘密同步為標準 Kubernetes Secret
secretObjects:
- secretName: azure-secrets
type: Opaque
# 定義如何對映外部秘密到 Secret 的欄位
data:
- objectName: database-password
key: db-password
- objectName: api-key
key: api-key
定義了 SecretProviderClass 後,就可以在 Pod 中使用它來掛載外部秘密。這個過程透過定義一個特殊的 CSI 磁碟區來完成。
# 使用 Secret Store CSI Driver 的 Pod 定義
apiVersion: v1
kind: Pod
metadata:
name: application-pod
namespace: production
spec:
containers:
- name: app-container
image: myapp:latest
# 定義磁碟區掛載
volumeMounts:
# 將秘密掛載到容器的檔案系統
- name: secrets-volume
# 掛載路徑,應用程式從這個路徑讀取秘密
mountPath: "/mnt/secrets"
# 唯讀掛載,防止應用程式修改秘密
readOnly: true
# 應用程式可以透過環境變數使用秘密
# 這需要啟用 syncSecret 功能
env:
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: azure-secrets
key: db-password
# 定義磁碟區
volumes:
# CSI 磁碟區定義
- name: secrets-volume
csi:
# 指定使用 Secrets Store CSI Driver
driver: secrets-store.csi.k8s.io
# 唯讀磁碟區
readOnly: true
# 磁碟區屬性
volumeAttributes:
# 引用 SecretProviderClass 的名稱
secretProviderClass: "azure-keyvault-provider"
當 Pod 啟動時,CSI Driver 會根據 SecretProviderClass 的定義,從 Azure Key Vault 取得指定的秘密,並將它們掛載到 Pod 的檔案系統中。應用程式可以像讀取一般檔案一樣讀取這些秘密。
Secret Store CSI Driver 的一個強大功能是自動輪換。當外部秘密管理平台中的秘密值更新時,Driver 可以自動偵測變化並更新掛載的秘密。這個功能透過定期輪詢實現,輪詢間隔可以在安裝時配置。
# 啟用自動輪換的 Helm 安裝參數
# rotation-poll-interval 設定輪詢間隔
helm install csi-secrets-store \
secrets-store-csi-driver/secrets-store-csi-driver \
--namespace kube-system \
--set syncSecret.enabled=true \
--set enableSecretRotation=true \
--set rotationPollInterval=2m
在實際應用中,Secret Store CSI Driver 需要適當的權限配置。對於 Azure Key Vault,這通常透過 Azure AD Pod Identity 或 Managed Identity 實現。
# Azure AD Pod Identity 的組態範例
apiVersion: aadpodidentity.k8s.io/v1
kind: AzureIdentity
metadata:
name: keyvault-identity
namespace: production
spec:
# Azure Managed Identity 的資源 ID
resourceID: "/subscriptions/{subscription-id}/resourcegroups/{resource-group}/providers/Microsoft.ManagedIdentity/userAssignedIdentities/{identity-name}"
# 對應的 Client ID
clientID: "your-client-id"
---
# 將 Identity 綁定到特定的 Pod
apiVersion: aadpodidentity.k8s.io/v1
kind: AzureIdentityBinding
metadata:
name: keyvault-identity-binding
namespace: production
spec:
# 引用上面定義的 AzureIdentity
azureIdentity: keyvault-identity
# 選擇器,用於匹配 Pod
selector: keyvault-pod
使用這個 Identity 的 Pod 需要添加對應的標籤。
# 使用 Pod Identity 的 Pod 定義
apiVersion: v1
kind: Pod
metadata:
name: application-pod
namespace: production
# 添加標籤以匹配 AzureIdentityBinding 的選擇器
labels:
aadpodidbinding: keyvault-pod
spec:
# Pod 定義的其餘部分
containers:
- name: app-container
image: myapp:latest
volumeMounts:
- name: secrets-volume
mountPath: "/mnt/secrets"
readOnly: true
volumes:
- name: secrets-volume
csi:
driver: secrets-store.csi.k8s.io
readOnly: true
volumeAttributes:
secretProviderClass: "azure-keyvault-provider"
Secret Store CSI Driver 提供了強大的功能,但也帶來了額外的複雜性。在採用這個方案時,需要仔細評估組織的需求,確保技術選擇與實際使用場景相符。對於已經大量使用外部秘密管理平台的組織,CSI Driver 提供了seamless 的整合路徑。對於剛開始建構秘密管理策略的團隊,可能需要先評估是否真的需要引入外部平台的複雜性。
@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
title Secret Store CSI Driver 架構與運作流程
package "Kubernetes 叢集" {
component "Pod" as pod {
[應用程式容器]
folder "掛載點" as mount {
file "秘密檔案"
}
}
component "CSI Driver" as driver {
[Driver Pod\n(DaemonSet)]
}
component "Provider" as provider {
[Azure Provider]
[AWS Provider]
[GCP Provider]
[Vault Provider]
}
database "SecretProviderClass" as spc
}
cloud "外部秘密管理平台" {
storage "Azure Key Vault" as akv
storage "AWS Secrets Manager" as asm
storage "HashiCorp Vault" as vault
}
[應用程式容器] --> mount : 讀取秘密
pod --> driver : 請求掛載秘密
driver --> spc : 讀取組態
driver --> provider : 呼叫 Provider
provider --> akv : 取得秘密
provider --> asm : 取得秘密
provider --> vault : 取得秘密
provider --> driver : 回傳秘密資料
driver --> mount : 掛載為 tmpfs
note right of spc
定義秘密來源
認證方式
存取權限
end note
note right of mount
秘密儲存在記憶體
不會寫入磁碟
提升安全性
end note
@enduml這個架構圖清楚地展示了 Secret Store CSI Driver 如何協調 Kubernetes Pod 與外部秘密管理平台之間的互動。整個流程始於 Pod 請求掛載秘密,CSI Driver 讀取 SecretProviderClass 定義,呼叫對應的 Provider 從外部平台取得秘密,然後將秘密掛載為 tmpfs 記憶體檔案系統。這種設計確保了秘密的安全性,同時提供了與多種外部平台整合的靈活性。
服務網格在秘密管理中的角色與價值
服務網格技術為微服務架構帶來了新的安全管理範式。相較於傳統的應用程式層級安全機制,服務網格在基礎設施層面提供了統一的安全策略,特別是在證書管理與服務間通訊加密方面。
從架構角度來看,服務網格透過 Sidecar 模式在每個服務 Pod 中注入代理容器。這些代理容器攔截所有進出服務的網路流量,在流量層面實作安全策略。這種設計讓應用程式程式碼完全不需要關心底層的安全機制,開發者只需要專注於業務邏輯的實作。
在秘密管理的脈絡中,服務網格主要處理兩類敏感資訊。第一類是 TLS 證書,用於建立服務間的加密通訊通道與身份驗證。第二類是服務令牌,用於實作更細緻的存取控制與授權機制。服務網格透過自動化這些秘密的生命週期管理,大幅降低了維運負擔並提升了安全性。
Istio 作為最成熟的服務網格實作之一,提供了完整的證書管理解決方案。其核心元件 istiod 整合了證書授權中心的功能,負責為網格中的每個服務簽發 X.509 證書。這些證書用於實作 mutual TLS,確保服務間通訊的雙向認證與加密。
在實際部署中,安裝 Istio 是建構服務網格安全基礎的第一步。Istio 提供了多種安裝方式,其中 istioctl 工具提供了最直接的部署路徑。
# 下載 Istio 安裝包
# 這個指令會下載最新版本的 Istio
curl -L https://istio.io/downloadIstio | sh -
# 進入 Istio 目錄
cd istio-*
# 將 istioctl 添加到系統路徑
export PATH=$PWD/bin:$PATH
# 使用 demo 設定檔安裝 Istio
# demo 設定檔適合測試與學習用途
# 生產環境應使用 production 設定檔
istioctl install --set profile=demo -y
# 驗證安裝是否成功
# 檢查 istiod 是否正常運行
kubectl get pods -n istio-system
# 查看 Istio 的版本資訊
istioctl version
安裝完 Istio 後,需要為要納入服務網格的命名空間啟用自動 Sidecar 注入。這個功能透過 Webhook 機制實作,當 Pod 建立時自動注入 Envoy 代理容器。
# 為命名空間啟用自動 Sidecar 注入
# 這個標籤會觸發 Istio 的注入 Webhook
kubectl label namespace production istio-injection=enabled
# 驗證標籤是否正確設定
kubectl get namespace production --show-labels
# 在已標記的命名空間中部署應用程式
# 新建立的 Pod 會自動包含 Istio Sidecar
kubectl apply -f application-deployment.yaml -n production
# 檢查 Pod 是否包含 Sidecar 容器
# 注入成功的 Pod 會有兩個容器
kubectl get pods -n production
# 查看 Pod 的詳細資訊
# 應該能看到 istio-proxy 容器
kubectl describe pod <pod-name> -n production
Istio 的 mTLS 功能預設啟用寬鬆模式,允許服務接受加密與未加密的流量。在生產環境中,通常需要強制啟用嚴格的 mTLS 模式,確保所有服務間通訊都經過加密。
# 啟用嚴格 mTLS 的 PeerAuthentication 資源
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
# 資源名稱
name: default
# 部署在 istio-system 命名空間表示全域策略
namespace: istio-system
spec:
# 設定 mTLS 模式為嚴格
# STRICT 模式要求所有通訊都使用 mTLS
# PERMISSIVE 模式允許混合使用加密與未加密通訊
# DISABLE 模式停用 mTLS
mtls:
mode: STRICT
套用這個策略後,所有未使用 mTLS 的連線請求都會被拒絕。這確保了服務網格內的所有通訊都經過加密與雙向認證。
# 套用 mTLS 策略
kubectl apply -f peer-authentication.yaml
# 驗證策略是否生效
# 嘗試從網格外的 Pod 連線到網格內的服務
# 這個連線應該會失敗
kubectl run test-pod --image=curlimages/curl --rm -it -- \
curl http://service-name.production.svc.cluster.local
除了全域策略,Istio 也支援為特定命名空間或服務定義細緻的 mTLS 策略。這種靈活性讓組織能夠逐步遷移到嚴格的 mTLS 模式,而不需要一次性改變所有服務。
# 為特定命名空間定義 mTLS 策略
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: production-mtls
# 部署在特定命名空間,只影響該命名空間的服務
namespace: production
spec:
mtls:
mode: STRICT
---
# 為特定服務定義 mTLS 策略
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
name: database-service-mtls
namespace: production
spec:
# 選擇器,只影響匹配標籤的服務
selector:
matchLabels:
app: database-service
mtls:
mode: STRICT
# 為特定埠定義不同的 mTLS 模式
portLevelMtls:
# 對於 9090 埠使用寬鬆模式
# 這對於監控端點可能是必要的
9090:
mode: PERMISSIVE
Istio 的證書管理機制完全自動化。istiod 為每個服務簽發短期證書,預設有效期為 24 小時。這些證書會在到期前自動輪換,確保即使證書外洩,影響範圍也被限制在很短的時間窗口內。
# 檢視服務的證書資訊
# 這個指令會顯示證書的有效期與簽發者
istioctl proxy-config secret <pod-name> -n production
# 查看證書的詳細內容
# 包括 Subject、Issuer、有效期等資訊
kubectl exec <pod-name> -n production -c istio-proxy -- \
openssl s_client -showcerts -connect localhost:15000 </dev/null
# 驗證 mTLS 連線是否正常
# 這個指令會嘗試建立 mTLS 連線並顯示證書鏈
istioctl experimental proxy-status <pod-name> -n production
除了 mTLS,Istio 也支援基於 JWT 的終端使用者認證。這允許服務驗證來自外部使用者的請求,實作細緻的存取控制。
# 定義 JWT 認證策略
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
name: jwt-authentication
namespace: production
spec:
# 選擇要套用策略的服務
selector:
matchLabels:
app: api-gateway
# 定義 JWT 規則
jwtRules:
# JWT 簽發者
- issuer: "https://auth.example.com"
# JWKS URI,用於驗證 JWT 簽章
jwksUri: "https://auth.example.com/.well-known/jwks.json"
# JWT 必須包含的 audience
audiences:
- "api.example.com"
配合 AuthorizationPolicy,可以實作基於 JWT claims 的細緻授權控制。
# 定義授權策略
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: api-authorization
namespace: production
spec:
# 選擇要套用策略的服務
selector:
matchLabels:
app: api-gateway
# 定義允許的操作
rules:
# 允許帶有 admin 角色的請求存取所有路徑
- from:
- source:
requestPrincipals: ["*"]
when:
# JWT claim 中必須包含 admin 角色
- key: request.auth.claims[role]
values: ["admin"]
# 允許帶有 user 角色的請求存取特定路徑
- from:
- source:
requestPrincipals: ["*"]
to:
- operation:
# 限制只能存取 /api/public 路徑
paths: ["/api/public/*"]
when:
- key: request.auth.claims[role]
values: ["user"]
服務網格在秘密管理中的價值不僅在於技術功能,更在於其提供的安全架構範式。透過將安全策略從應用程式程式碼中剝離,服務網格讓安全團隊能夠統一管理與執行安全政策,而不需要依賴每個開發團隊的安全意識與實作品質。這種職責分離大幅提升了整體系統的安全性與可管理性。
秘密代理機制與零信任架構
在現代雲端原生安全架構中,秘密代理機制扮演著關鍵角色。這種模式透過引入中間層,將應用程式與秘密儲存系統解耦,提供了更高層次的抽象與控制能力。
秘密代理的核心概念是將秘密的存取控制與分發邏輯集中化。應用程式不直接與秘密儲存系統互動,而是透過代理服務發出請求。代理服務負責驗證應用程式的身份,檢查授權策略,從底層儲存系統取得秘密,並安全地傳遞給應用程式。這種設計帶來了多個安全優勢。
首先,集中化的存取控制讓安全團隊能夠在單一位置定義與執行授權策略。相較於在每個秘密儲存系統中分別配置權限,集中化的模式大幅簡化了管理複雜度並降低了配置錯誤的風險。
其次,代理模式提供了統一的審計日誌。所有秘密存取請求都經過代理,讓安全團隊能夠全面追蹤敏感資訊的使用情況。這種可見性對於合規要求與安全事件調查都至關重要。
第三,抽象層讓應用程式與底層儲存系統解耦。當組織需要遷移到不同的秘密管理平台時,只需要更新代理的後端整合,而不需要修改所有應用程式。這種靈活性大幅降低了技術鎖定的風險。
在實作層面,秘密代理可以採用多種架構模式。最簡單的方式是部署一個中心化的代理服務,所有應用程式透過 HTTP API 與其互動。這種模式易於理解與部署,但中心化服務可能成為效能瓶頸與單點故障。
更進階的模式是在每個節點或每個 Pod 中部署代理實例。這種分散式架構避免了中心化瓶頸,但增加了管理複雜度。Sidecar 模式是一個常見的實作選擇,代理容器與應用程式容器運行在同一個 Pod 中,透過本地環路進行通訊。
HashiCorp Vault 提供了完整的秘密代理功能,包括 Vault Agent 與 Vault Sidecar Injector。Vault Agent 可以作為 Sidecar 運行,自動處理 Vault 認證、秘密取得與更新。
# 使用 Vault Agent Sidecar 的 Pod 定義
apiVersion: v1
kind: Pod
metadata:
name: vault-agent-example
# 這個註解觸發 Vault Sidecar Injector
annotations:
# 啟用 Vault Agent 注入
vault.hashicorp.com/agent-inject: "true"
# 指定 Vault 的角色
# 這個角色定義了 Pod 能夠存取哪些秘密
vault.hashicorp.com/role: "myapp"
# 定義要注入的秘密
# 這個註解會創建一個檔案 /vault/secrets/database-config
vault.hashicorp.com/agent-inject-secret-database-config: "secret/data/database/config"
# 定義秘密的範本
# 這允許客製化秘密的輸出格式
vault.hashicorp.com/agent-inject-template-database-config: |
{{- with secret "secret/data/database/config" -}}
export DB_USERNAME="{{ .Data.data.username }}"
export DB_PASSWORD="{{ .Data.data.password }}"
export DB_HOST="{{ .Data.data.host }}"
{{- end }}
spec:
# 設定 Service Account
# Vault 使用這個 SA 的 JWT 進行認證
serviceAccountName: myapp
containers:
- name: app
image: myapp:latest
# 應用程式從檔案讀取秘密
# Vault Agent 會自動更新這個檔案
command:
- sh
- -c
- |
source /vault/secrets/database-config
/app/start.sh
這個配置會自動注入 Vault Agent Sidecar,處理所有與 Vault 的互動。應用程式只需要從指定的檔案路徑讀取秘密,完全不需要整合 Vault SDK 或處理認證邏輯。
Vault Agent 支援自動續期與秘密輪換。當 Vault 中的秘密更新時,Agent 會自動取得新值並更新檔案。應用程式可以監控檔案變化並重新載入組態,實現無需重啟的秘密更新。
# 啟用自動秘密更新的註解
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
vault.hashicorp.com/agent-inject-secret-database-config: "secret/data/database/config"
# 啟用檔案變化通知
# 當秘密更新時,Vault Agent 會發送信號給應用程式容器
vault.hashicorp.com/agent-inject-command-database-config: "pkill -HUP myapp"
除了 Sidecar 模式,Vault 也支援 Init Container 模式。在這種模式下,Vault Agent 作為 Init Container 運行,在應用程式啟動前取得秘密並寫入共享磁碟區。這種模式適合那些只需要在啟動時讀取秘密的應用程式。
# 使用 Vault Agent Init Container 的 Pod 定義
apiVersion: v1
kind: Pod
metadata:
name: vault-init-example
annotations:
# 使用 Init Container 模式而非 Sidecar
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/agent-pre-populate-only: "true"
vault.hashicorp.com/role: "myapp"
vault.hashicorp.com/agent-inject-secret-config: "secret/data/app/config"
spec:
serviceAccountName: myapp
containers:
- name: app
image: myapp:latest
volumeMounts:
# 掛載 Vault Agent 寫入秘密的磁碟區
- name: vault-secrets
mountPath: /vault/secrets
秘密代理機制與零信任架構理念高度契合。在零信任模型中,不應該預設信任任何請求,即使它來自內部網路。每個秘密存取請求都需要經過明確的身份驗證與授權檢查。秘密代理提供了實作這種策略的自然位置,它可以整合多種身份驗證機制,從 Kubernetes Service Account 到外部身份提供者,確保只有經過驗證的實體才能存取敏感資訊。
秘密管理的安全最佳實踐與策略建議
建構安全的 Kubernetes 秘密管理系統需要綜合考慮技術選擇、流程設計與組織文化等多個層面。基於產業實踐與安全原則,以下是一些關鍵的最佳實踐。
最小權限原則是秘密管理的基礎。每個應用程式、服務或使用者都應該只能存取其職責範圍內必需的秘密。過度寬鬆的權限配置是常見的安全隱患,一旦某個元件被攻陷,攻擊者可能取得遠超過必要範圍的敏感資訊。實作最小權限需要仔細分析每個元件的實際需求,並透過 RBAC、命名空間隔離與細緻的授權策略來執行限制。
定期輪換是另一個關鍵實踐。秘密應該被視為有時效性的憑證,而非永久有效的配置。定期輪換能夠限制秘密外洩的影響範圍,即使舊的秘密被洩露,它們也會在短時間內失效。自動化的輪換機制能夠大幅降低這個過程的維運負擔。服務網格在證書輪換方面提供了很好的範例,短期證書配合自動更新,在安全性與可用性之間取得了良好平衡。
加密傳輸與靜態加密都不可或缺。秘密在網路傳輸過程中必須使用 TLS 等加密協定保護,避免中間人攻擊。儲存在磁碟或 etcd 中的秘密也應該加密,防止未授權的直接存取。Kubernetes 提供了 etcd 加密功能,但預設並未啟用,組織應該確保在生產環境中啟用這個功能。
審計日誌是安全可見性的基礎。所有秘密的建立、修改、存取與刪除操作都應該被記錄。這些日誌不僅對於事後調查重要,更能夠透過即時分析發現異常存取模式。審計系統應該獨立於應用系統運作,避免攻擊者能夠刪除或修改審計記錄。
職責分離原則要求不同角色對秘密擁有不同的權限。開發人員可能需要在開發環境中存取秘密,但不應該能夠存取生產環境的敏感資訊。安全團隊可能負責定義秘密策略,但不一定需要讀取秘密的實際值。維運團隊可能需要管理秘密的輪換,但應該透過自動化工具而非手動操作。清晰的職責劃分能夠降低內部威脅的風險。
災難恢復計劃必須涵蓋秘密管理系統。秘密的備份與恢復流程需要特別設計,確保備份本身的安全性。加密金鑰的管理尤其關鍵,失去加密金鑰可能導致所有加密秘密無法解密。組織應該建立安全的金鑰託管機制,在災難場景下能夠恢復系統。
教育培訓是技術措施之外的重要環節。開發者需要理解為什麼不應該在程式碼中硬編碼秘密,如何正確使用秘密管理工具,以及當發現秘密洩露時應該如何應對。定期的安全意識培訓能夠建立安全文化,讓整個團隊都參與到秘密保護中。
在台灣的企業環境中,金融服務業對於秘密管理有著嚴格的合規要求。個人資料保護法與金融監管規範都對敏感資訊的處理有明確要求。組織在建構秘密管理系統時,需要確保符合這些法規要求,包括資料儲存的地理位置限制、審計日誌的保存期限與存取控制的細緻程度。
技術選擇應該基於組織的實際需求與能力。SealedSecrets 適合希望在 GitOps 工作流程中安全管理秘密的團隊,其學習曲線相對平緩,部署與維運成本較低。Secret Store CSI Driver 適合已經大量使用外部秘密管理平台的組織,能夠充分利用既有投資。服務網格適合大規模微服務架構,其價值隨著服務數量的增加而提升。
從演進路徑來看,組織可以採取漸進式的策略。起步階段可以先使用 SealedSecrets 解決版本控制中的秘密安全問題,建立基本的秘密管理流程。隨著應用規模的增長,可以引入外部秘密管理平台,利用其進階功能如動態秘密生成與細緻的審計。當微服務架構成熟後,服務網格能夠進一步強化服務間通訊的安全性。
Kubernetes 秘密管理是一個持續演進的領域,新的工具與實踐不斷湧現。組織需要保持對技術趨勢的關注,定期評估與更新自己的秘密管理策略。同時,安全不應該被視為一次性的專案,而是需要持續投入與改進的過程。透過結合適當的技術工具、完善的流程設計與強大的安全文化,組織能夠在雲端原生時代建構起可靠的秘密管理體系,為應用程式的安全運行提供堅實基礎。