在雲原生應用程式的開發與部署過程中,秘密管理始終是最關鍵且複雜的安全議題之一。應用程式需要存取各種敏感資訊,包括資料庫連線字串、API 金鑰、加密憑證、第三方服務認證令牌等。傳統的做法是將這些秘密硬編碼在組態檔案中或透過環境變數傳遞,但這些方法都存在嚴重的安全風險。硬編碼的秘密可能隨著程式碼被提交到版本控制系統,環境變數則可能在容器執行時被意外洩露或記錄在日誌中。

Kubernetes 原生提供了 Secret 資源來儲存敏感資訊,但這個機制也有其限制。Kubernetes Secret 預設只進行 Base64 編碼而非真正的加密,雖然在較新版本中可以啟用靜態加密,但金鑰管理、存取控制、稽核追蹤等進階功能仍然有限。此外,Secret 的變更管理、版本控制以及跨叢集同步也是實務上常見的挑戰。

HashiCorp Vault 作為業界領先的秘密管理解決方案,提供了完整的秘密生命週期管理功能。Vault 不僅能夠安全地儲存秘密,還支援動態秘密產生、自動金鑰輪替、細緻的存取控制政策、完整的稽核日誌以及多種認證方法。當 Vault 與 Kubernetes 整合時,能夠為雲原生應用程式提供企業級的秘密管理能力。

本文將深入探討如何在 Kubernetes 環境中整合並使用 HashiCorp Vault。我們會從基礎的認證流程開始,逐步介紹政策控制、多種秘密注入方式,最後探討 Vault 在 Kubernetes 上的高可用性部署架構。透過詳細的技術解析與實務範例,讀者將能夠建立一套完整的 Kubernetes 秘密管理解決方案。

Kubernetes 認證整合機制深度解析

要讓執行在 Kubernetes 中的應用程式能夠安全地從 Vault 取得秘密,首要任務是建立可靠的身份驗證機制。Vault 提供了專門針對 Kubernetes 設計的認證方法,這個方法利用 Kubernetes 的 Service Account 機制,讓 Pod 能夠使用其身份令牌向 Vault 證明自己的身份。

Kubernetes 認證方法運作原理

Kubernetes 認證方法的核心概念是利用 Service Account Token 作為身份憑證。在 Kubernetes 中,每個 Pod 都會被指派一個 Service Account,系統會自動將對應的 JWT 令牌掛載到 Pod 的檔案系統中。這個令牌包含了 Pod 的身份資訊,包括所屬的 Namespace、Service Account 名稱等後設資料,並由 Kubernetes API Server 使用私鑰進行數位簽章。

當 Pod 嘗試向 Vault 請求秘密時,會提供這個 JWT 令牌作為身份證明。Vault 收到請求後,並不會直接信任這個令牌,而是向 Kubernetes API Server 發送 TokenReview 請求,要求 Kubernetes 驗證令牌的真實性與有效性。只有在 Kubernetes 確認令牌有效,且對應的 Service Account 確實存在時,Vault 才會根據預先設定的政策授予秘密存取權限。

這個機制的安全性建立在多個層面。首先,JWT 令牌使用非對稱加密簽章,偽造幾乎不可能。其次,Vault 必須持有 Kubernetes 的 CA 憑證才能與 API Server 安全通訊,這確保了 Vault 與 Kubernetes 之間的信任關係。最後,透過 TokenReview 的即時驗證,即使令牌被竊取,只要對應的 Service Account 被刪除或停用,令牌立即失效,大幅降低了安全風險。

Vault Kubernetes 認證組態實作

在 Vault 中啟用並組態 Kubernetes 認證方法需要提供幾個關鍵參數。首先是 Token Reviewer JWT,這是一個具有執行 TokenReview 權限的 Service Account 令牌,Vault 會使用這個令牌向 Kubernetes API Server 發送驗證請求。其次是 Kubernetes API Server 的位址,Vault 需要知道如何連線到 Kubernetes 叢集。最後是 Kubernetes 的 CA 憑證,用於驗證 API Server 的 TLS 憑證,確保連線的安全性。

KUBE_HOST=$(kubectl config view --raw --minify --flatten \
    --output='jsonpath={.clusters[].cluster.server}')

KUBE_CA_CERT=$(kubectl config view --raw --minify --flatten \
    --output='jsonpath={.clusters[].cluster.certificate-authority-data}' | base64 -d)

TOKEN_REVIEW_JWT=$(kubectl create token vault-auth-reviewer --duration=87600h)

vault auth enable kubernetes

vault write auth/kubernetes/config \
    token_reviewer_jwt="${TOKEN_REVIEW_JWT}" \
    kubernetes_host="${KUBE_HOST}" \
    kubernetes_ca_cert="${KUBE_CA_CERT}" \
    issuer="https://kubernetes.default.svc.cluster.local"

這段組態指令首先從本機的 kubeconfig 檔案中提取必要的資訊。kubectl config view 指令能夠讀取並解析 kubeconfig,我們使用 JSONPath 語法精確地提取 API Server 位址與 CA 憑證。CA 憑證在 kubeconfig 中以 Base64 編碼儲存,因此需要先解碼才能使用。

Token Reviewer JWT 的取得方式在不同的 Kubernetes 版本中有所差異。在較新的版本中,推薦使用 kubectl create token 指令產生有期限的令牌,這比直接從 Secret 中讀取更安全。這裡我們設定令牌有效期為十年,在生產環境中應該根據實際需求調整這個期限,並建立定期輪替的機制。

完成認證方法的基本組態後,我們需要在 Vault 中建立角色,將 Kubernetes 的 Service Account 對應到 Vault 的存取政策。角色定義了哪些 Service Account 可以使用這個角色,以及使用這個角色能夠取得哪些政策授予的權限。

vault write auth/kubernetes/role/webapp-role \
    bound_service_account_names=webapp-sa \
    bound_service_account_namespaces=production \
    policies=webapp-policy \
    ttl=1h

這個角色組態指定只有 production Namespace 中名為 webapp-sa 的 Service Account 能夠使用這個角色。當使用這個角色成功認證後,會獲得 webapp-policy 政策授予的權限,令牌有效期為一小時。這種綁定機制提供了細緻的存取控制,確保只有特定的 Pod 能夠取得對應的秘密。

@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

participant "Pod" as Pod
participant "Vault Agent" as Agent
participant "Vault Server" as Vault
participant "Kubernetes API" as K8s

note over Pod
  Pod 啟動時自動掛載
  Service Account Token
end note

Pod -> Agent: 啟動 Vault Agent Sidecar
activate Agent

Agent -> Agent: 讀取 Service Account Token
note right
  Token 位於
  /var/run/secrets/kubernetes.io/
  serviceaccount/token
end note

Agent -> Vault: 使用 Token 請求認證
activate Vault

note over Vault
  收到認證請求
  準備驗證 Token 有效性
end note

Vault -> K8s: 發送 TokenReview 請求
activate K8s

note over K8s
  驗證 Token 簽章
  檢查 Service Account 狀態
  確認 Token 未過期
end note

K8s --> Vault: 回傳驗證結果
deactivate K8s

Vault -> Vault: 檢查 Service Account 綁定
note right
  驗證 Service Account 名稱
  確認 Namespace 匹配
  檢查政策權限
end note

Vault --> Agent: 發放 Vault Token
deactivate Vault

Agent -> Vault: 使用 Vault Token 請求秘密
activate Vault

Vault -> Vault: 驗證政策權限
note right
  檢查請求的秘密路徑
  確認政策允許存取
end note

Vault --> Agent: 回傳秘密資料
deactivate Vault

Agent -> Pod: 將秘密寫入共享記憶體卷
deactivate Agent

note over Pod
  應用程式容器可以
  從檔案系統讀取秘密
end note

@enduml

網路組態疑難排解

在實務部署中,Kubernetes 認證組態可能遇到網路連線相關的問題。最常見的情況是 kubernetes_host 參數指向 https://127.0.0.1:6443 這類本機位址。當 Vault 部署在 Kubernetes 叢集外部時,這個位址無法從外部存取,導致 TokenReview 請求失敗。

這個問題的根本原因在於 kubeconfig 檔案中記錄的 API Server 位址是從管理者的角度設定的,可能使用了 kubectl proxy 或埠轉發等機制。對於 Vault 而言,需要的是 API Server 的實際網路位址,這可能是叢集節點的 IP 位址、負載平衡器的位址,或者是透過 DNS 解析的服務位址。

解決這個問題有幾種方法。最直接的方式是手動指定正確的 API Server 位址,確保 Vault 能夠透過網路連線到 Kubernetes API。如果 API Server 使用非標準埠,需要在位址中明確指定埠號。另一個選項是在 Vault 所在的環境中設定網路代理,將對本機位址的請求轉發到實際的 API Server。

使用 HAProxy 作為代理是一個實用的解決方案,特別是在測試環境中。HAProxy 可以監聽本機的特定埠,並將流量轉發到遠端的 Kubernetes API Server,同時處理 TLS 憑證驗證等複雜的網路細節。

cat > haproxy.cfg <<EOF
global
    log stdout local0
    maxconn 4096

defaults
    log global
    mode tcp
    option tcplog
    timeout connect 5000ms
    timeout client 50000ms
    timeout server 50000ms

frontend kubernetes-api
    bind *:6443
    default_backend kubernetes-api-backend

backend kubernetes-api-backend
    server kube-api ${ACTUAL_KUBE_HOST}:${ACTUAL_KUBE_PORT} ssl verify required ca-file /etc/ssl/kubernetes-ca.crt
EOF

docker run -d --name vault-haproxy \
    -p 6443:6443 \
    -v $(pwd)/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro \
    -v $(pwd)/kubernetes-ca.crt:/etc/ssl/kubernetes-ca.crt:ro \
    haproxy:latest

這個 HAProxy 組態建立了一個 TCP 代理,監聽本機的 6443 埠,並將所有流量轉發到實際的 Kubernetes API Server。透過這個設定,Vault 可以使用 https://127.0.0.1:6443 作為 API Server 位址,而實際的網路請求會被 HAProxy 正確地路由到遠端的叢集。

在某些環境中,可能還需要處理 TLS 憑證驗證的問題。Kubernetes API Server 的 TLS 憑證通常簽發給特定的主機名稱或 IP 位址,如果透過代理存取,可能會遇到憑證主機名稱不匹配的問題。這種情況下,可以在 HAProxy 中設定 SNI,或者在 Vault 的組態中調整憑證驗證的行為。

Vault 政策控制與權限管理

HashiCorp Vault 的政策系統是其安全架構的核心,提供了細緻且靈活的存取控制機制。政策使用 HCL 語言定義,能夠精確地控制誰可以存取哪些秘密,以及能夠執行哪些操作。良好設計的政策體系是建立零信任安全模型的基礎。

政策語法與權限能力

Vault 政策的基本結構圍繞著路徑與能力的概念。路徑代表 Vault 中資源的位置,可以是具體的秘密路徑,也可以使用萬用字元匹配多個資源。能力則定義了對資源允許的操作類型,包括建立、讀取、更新、刪除、列舉等。

path "secret/data/production/database/*" {
  capabilities = ["read"]
}

path "secret/data/production/api-keys/public" {
  capabilities = ["read", "list"]
}

path "secret/metadata/production/*" {
  capabilities = ["list"]
}

path "secret/data/staging/*" {
  capabilities = ["create", "read", "update", "delete", "list"]
}

這個政策範例展示了不同層級的存取控制。第一個路徑規則允許讀取生產環境資料庫相關的所有秘密,但不允許修改或刪除,確保生產資料的穩定性。第二個規則針對公開的 API 金鑰,除了讀取權限外還允許列舉,方便查看可用的金鑰清單。

第三個規則只授予後設資料的列舉權限,不允許讀取實際的秘密內容。這對於稽核或管理工具很有用,它們需要知道有哪些秘密存在,但不需要存取秘密的實際內容。最後一個規則針對測試環境,授予完整的操作權限,允許開發人員在測試過程中自由地建立、修改與刪除秘密。

政策還支援更進階的功能,例如參數限制、必要屬性以及條件式存取控制。這些功能讓政策能夠實現更複雜的安全需求。

path "secret/data/applications/{{identity.entity.aliases.auth_kubernetes_*.metadata.service_account_namespace}}/*" {
  capabilities = ["read"]
}

path "auth/token/renew-self" {
  capabilities = ["update"]
}

path "auth/token/lookup-self" {
  capabilities = ["read"]
}

這個進階政策範例使用了範本語法,根據認證身份動態決定允許存取的路徑。{{identity.entity.aliases.auth_kubernetes_*.metadata.service_account_namespace}} 會被替換為實際的 Kubernetes Namespace,這意味著每個應用程式只能存取自己 Namespace 下的秘密,實現了自動的多租戶隔離。

後兩個路徑規則允許令牌的自我管理操作,包括續約與查詢。這些是維持長時間執行應用程式所必需的基本權限,允許 Vault Agent 自動續約令牌而無需重新認證。

政策與 Kubernetes 角色綁定

定義好政策後,需要將其與 Kubernetes 的 Service Account 綁定,這透過 Vault 的 Kubernetes 認證角色實現。角色作為政策與 Kubernetes 身份之間的橋樑,定義了哪些 Service Account 能夠使用哪些政策。

vault policy write webapp-readonly - <<EOF
path "secret/data/webapp/*" {
  capabilities = ["read"]
}

path "database/creds/webapp-role" {
  capabilities = ["read"]
}

path "pki/issue/webapp-cert" {
  capabilities = ["create", "update"]
}
EOF

vault write auth/kubernetes/role/webapp-readonly-role \
    bound_service_account_names=webapp-sa \
    bound_service_account_namespaces=production,staging \
    policies=webapp-readonly,default \
    ttl=1h \
    max_ttl=4h

這個組態首先建立了一個名為 webapp-readonly 的政策,授予對 webapp 秘密的讀取權限、資料庫動態憑證的取得權限,以及 PKI 憑證的簽發權限。然後建立 Kubernetes 認證角色,將這個政策綁定到特定的 Service Account。

角色組態中的 bound_service_account_namespaces 參數支援多個值,這裡允許 production 與 staging 兩個 Namespace 中的 webapp-sa Service Account 使用這個角色。這種設計在多環境部署中很常見,同一個應用程式在不同環境中使用相同的 Service Account 名稱,但可能有不同的權限需求。

ttl 參數設定令牌的預設有效期,而 max_ttl 定義了續約的上限。這個設定意味著令牌初始有效期為一小時,可以續約最多到四小時。之後必須重新進行完整的認證流程。這種定期重新認證的機制提供了額外的安全保障,即使令牌洩露,其有效時間也是有限的。

在實務上,政策的設計應該遵循最小權限原則。每個應用程式只應該獲得其正常運作所需的最小權限,避免過度授權。同時,政策應該明確且可稽核,清楚記載每個權限的用途與授予原因。定期審查與更新政策,移除不再需要的權限,是維護安全態勢的重要實踐。

@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

:管理員建立 Vault 政策;

note right
  定義秘密路徑與權限能力
  使用 HCL 語法描述存取規則
end note

:建立 Kubernetes 認證角色;

note right
  綁定 Service Account
  指定適用的 Namespace
  關聯 Vault 政策
end note

:在 Kubernetes 建立 Service Account;

note right
  建立對應的 ServiceAccount 資源
  無需額外的 Secret 或憑證
end note

:部署 Pod 使用 Service Account;

partition "認證與授權流程" {
  :Pod 啟動並載入 Service Account Token;
  
  :Vault Agent 使用 Token 請求認證;
  
  :Vault 驗證 Token 與角色綁定;
  
  if (Service Account 符合角色綁定?) then (是)
    :授予角色關聯的政策權限;
    
    :發放 Vault Token;
    
    :記錄認證事件到稽核日誌;
    
  else (否)
    :拒絕認證請求;
    
    :記錄失敗事件;
    
    stop
  endif
}

:Vault Agent 使用 Token 請求秘密;

partition "權限檢查" {
  :解析請求的秘密路徑;
  
  :檢查 Token 關聯的政策;
  
  :比對政策規則與請求路徑;
  
  if (政策允許此操作?) then (是)
    :回傳秘密資料;
    
    :記錄存取事件;
    
  else (否)
    :回傳權限拒絕錯誤;
    
    :記錄拒絕事件;
    
    stop
  endif
}

:應用程式取得並使用秘密;

stop

@enduml

稽核日誌與合規追蹤

在企業環境中,秘密存取的稽核追蹤是合規性要求的重要組成部分。Vault 提供了完整的稽核日誌功能,能夠記錄所有的認證嘗試、秘密存取、政策變更等操作,為安全事件調查與合規稽核提供可靠的證據。

vault audit enable file file_path=/vault/logs/audit.log

vault audit enable syslog tag="vault" facility="AUTH"

vault audit enable socket address="logstash.example.com:5000" socket_type="tcp"

Vault 支援多種稽核日誌後端,可以同時啟用多個後端實現冗餘。檔案後端將稽核日誌寫入本機檔案,適合簡單的部署環境。Syslog 後端將日誌發送到系統日誌服務,便於與現有的日誌基礎設施整合。Socket 後端則可以將日誌即時串流到遠端的日誌收集系統,例如 Elasticsearch、Splunk 或其他 SIEM 系統。

稽核日誌的內容非常詳細,包含了請求的完整上下文資訊。每個日誌項目記錄了操作的時間戳、請求來源的 IP 位址、使用的認證方法與身份、請求的路徑與操作類型、請求與回應的詳細內容,以及操作的結果與任何錯誤訊息。

kubectl exec -it vault-0 -- tail -f /vault/logs/audit.log | jq .

透過即時監控稽核日誌,安全團隊可以快速發現異常的存取模式。例如,來自非預期 IP 位址的認證嘗試、頻繁的權限拒絕錯誤、大量的秘密讀取操作等,都可能是安全事件的徵兆。結合日誌分析工具與告警機制,可以建立主動的威脅偵測系統。

在設計稽核日誌策略時,需要平衡詳細程度與效能影響。過於詳細的日誌會產生大量的資料,增加儲存成本與分析複雜度。建議根據實際的合規需求與風險評估,決定日誌的詳細程度與保留期限。同時要確保日誌儲存的安全性,防止稽核日誌被竄改或刪除,這通常透過將日誌即時轉發到集中式的日誌管理系統實現。

Vault Agent Injector 秘密注入機制

Vault Agent Injector 是一個 Kubernetes Mutation Webhook,能夠自動修改 Pod 的定義,注入 Vault Agent Sidecar 容器與相關的組態。這個機制讓應用程式能夠透明地從 Vault 取得秘密,無需修改應用程式程式碼或明確處理 Vault 認證。

Mutation Webhook 運作原理

當 Kubernetes 接收到建立 Pod 的請求時,會檢查 Pod 的 Annotations。如果發現包含 Vault 相關的 Annotations,Kubernetes 會呼叫 Vault Agent Injector 的 Webhook。Webhook 會修改 Pod 的定義,加入 Vault Agent Init Container 與 Sidecar Container,同時建立必要的 Volume 與 Volume Mount。

Init Container 會在應用程式容器啟動前執行,負責初始認證與首次秘密提取。它使用 Pod 的 Service Account Token 向 Vault 認證,取得秘密並寫入共享的記憶體卷。Sidecar Container 則在整個 Pod 生命週期中持續執行,負責令牌續約與秘密更新。當秘密在 Vault 中被修改時,Sidecar 會偵測到變更並更新共享卷中的秘密檔案。

這個機制的優勢在於對應用程式完全透明。應用程式只需要從檔案系統讀取秘密,不需要知道 Vault 的存在,也不需要處理認證與令牌管理的複雜邏輯。這大幅降低了整合的複雜度,特別是對於無法修改原始碼的既有應用程式。

Pod Annotation 組態詳解

透過在 Pod 的 Metadata 中加入特定的 Annotations,可以控制 Vault Agent 的行為,包括要取得哪些秘密、秘密的存放路徑、模板格式等。

apiVersion: v1
kind: Pod
metadata:
  name: webapp
  namespace: production
  annotations:
    vault.hashicorp.com/agent-inject: "true"
    vault.hashicorp.com/role: "webapp-readonly-role"
    vault.hashicorp.com/agent-inject-secret-database.txt: "secret/data/webapp/database"
    vault.hashicorp.com/agent-inject-template-database.txt: |
      {{- with secret "secret/data/webapp/database" -}}
      export DB_HOST="{{ .Data.data.host }}"
      export DB_PORT="{{ .Data.data.port }}"
      export DB_USER="{{ .Data.data.username }}"
      export DB_PASS="{{ .Data.data.password }}"
      {{- end }}
    vault.hashicorp.com/agent-inject-secret-api-key.txt: "secret/data/webapp/api-keys"
    vault.hashicorp.com/agent-inject-file-api-key.txt: "api-credentials.json"
spec:
  serviceAccountName: webapp-sa
  containers:
  - name: webapp
    image: webapp:latest
    command: ["/bin/sh", "-c"]
    args:
    - |
      source /vault/secrets/database.txt
      exec /app/start.sh

這個 Pod 定義展示了 Vault Agent Injector 的關鍵 Annotations。vault.hashicorp.com/agent-inject 啟用注入功能,vault.hashicorp.com/role 指定使用的 Vault 角色。這個角色必須在 Vault 中預先定義,並綁定到對應的 Service Account。

vault.hashicorp.com/agent-inject-secret-* Annotations 定義要注入的秘密,星號後的部分成為秘密檔案的名稱。Annotation 的值是 Vault 中秘密的路徑。注入的秘密會被寫入 /vault/secrets/ 目錄下的對應檔案。

vault.hashicorp.com/agent-inject-template-* Annotations 允許使用 Go 模板語法自訂秘密的格式。在這個範例中,資料庫秘密被格式化為環境變數的匯出語句,應用程式容器可以透過 source 指令載入這些環境變數。模板功能非常強大,支援條件判斷、迴圈、函數呼叫等進階功能,能夠將 Vault 的秘密轉換為應用程式需要的任何格式。

vault.hashicorp.com/agent-inject-file-* Annotations 則允許自訂注入檔案的名稱,覆寫預設的檔案命名規則。這在應用程式期望特定檔案名稱時特別有用。

應用程式容器的啟動指令展示了如何使用注入的秘密。透過 source 指令載入資料庫環境變數,然後執行實際的應用程式啟動腳本。這種方式讓秘密作為環境變數對應用程式可見,但避免了在 Pod 定義中明文指定環境變數。

@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

participant "kubectl" as CLI
participant "Kubernetes API" as API
participant "Vault Injector\nWebhook" as Webhook
participant "Vault Agent\nInit Container" as Init
participant "Vault Server" as Vault
participant "Application\nContainer" as App
participant "Vault Agent\nSidecar" as Sidecar

CLI -> API: 提交 Pod 建立請求
activate API

note over API
  檢查 Pod Annotations
  發現 Vault 注入標記
end note

API -> Webhook: 呼叫 Mutation Webhook
activate Webhook

note over Webhook
  分析 Vault Annotations
  決定注入策略
end note

Webhook -> Webhook: 修改 Pod 定義
note right
  加入 Init Container
  加入 Sidecar Container
  建立共享記憶體卷
  掛載 Service Account Token
end note

Webhook --> API: 回傳修改後的 Pod 定義
deactivate Webhook

API -> Init: 啟動 Init Container
activate Init

Init -> Init: 讀取 Service Account Token

Init -> Vault: 使用 Token 進行認證
activate Vault

Vault -> Vault: 驗證 Token 與角色綁定

Vault --> Init: 回傳 Vault Token
deactivate Vault

Init -> Vault: 請求秘密資料
activate Vault

Vault -> Vault: 檢查政策權限

Vault --> Init: 回傳秘密
deactivate Vault

Init -> Init: 套用秘密模板
note right
  使用 Go 模板引擎
  格式化秘密內容
end note

Init -> Init: 寫入秘密到共享卷
note right
  路徑:/vault/secrets/
  應用程式可讀取
end note

deactivate Init

API -> App: 啟動應用程式容器
activate App

App -> App: 從檔案系統讀取秘密
note right
  source /vault/secrets/database.txt
  載入環境變數
end note

API -> Sidecar: 啟動 Sidecar Container
activate Sidecar

loop 持續運作
  Sidecar -> Vault: 續約 Vault Token
  activate Vault
  Vault --> Sidecar: 確認續約成功
  deactivate Vault
  
  Sidecar -> Vault: 檢查秘密更新
  activate Vault
  
  alt 秘密已更新
    Vault --> Sidecar: 回傳新秘密
    deactivate Vault
    Sidecar -> Sidecar: 更新共享卷中的秘密
    note right
      應用程式可以重新讀取
      或透過信號通知重載
    end note
  else 秘密未變更
    Vault --> Sidecar: 回傳未修改狀態
    deactivate Vault
  end
end

@enduml

秘密輪替與應用程式重載

Vault Agent Sidecar 能夠自動偵測秘密的變更並更新共享卷中的檔案,但應用程式如何感知這些變更並重新載入秘密,則需要額外的設計。不同的應用程式有不同的組態重載機制,Vault Agent 提供了幾種方式來觸發應用程式的重載。

最簡單的方式是讓應用程式定期檢查秘密檔案的修改時間,當偵測到檔案更新時重新讀取內容。這種方式不需要 Vault Agent 的額外支援,但會增加應用程式的複雜度,且可能無法即時回應秘密變更。

更優雅的方式是使用 Process Signal。Vault Agent 可以組態為在秘密更新後向應用程式程序發送特定的信號,例如 SIGHUP。許多成熟的應用程式框架支援在收到 SIGHUP 信號時重載組態,無需重啟整個程序。

metadata:
  annotations:
    vault.hashicorp.com/agent-inject-secret-config.txt: "secret/data/webapp/config"
    vault.hashicorp.com/agent-inject-command-config.txt: "pkill -HUP webapp"

這個 Annotation 組態指示 Vault Agent 在更新 config.txt 秘密後,執行 pkill -HUP webapp 指令向應用程式發送 SIGHUP 信號。應用程式需要正確處理這個信號,重新讀取秘密檔案並更新內部狀態。

對於無法支援信號處理的應用程式,可以考慮使用重啟策略。雖然重啟 Pod 會造成短暫的服務中斷,但在配合 Kubernetes 的滾動更新機制下,可以實現無停機的秘密更新。這通常透過外部的控制器實現,監控 Vault 中的秘密變更,並觸發對應 Deployment 的滾動更新。

CSI Driver 秘密儲存整合

Container Storage Interface Secret Store CSI Driver 是 Kubernetes 社群開發的標準化秘密管理方案。相比於 Vault Agent Injector 的 Sidecar 模式,CSI Driver 提供了更輕量級且更符合 Kubernetes 原生設計理念的整合方式。

CSI Driver 架構與優勢

CSI Driver 將秘密作為特殊的儲存卷掛載到 Pod 中。當 Kubelet 需要掛載秘密卷時,會呼叫 CSI Driver 的 API,Driver 負責從 Vault 取得秘密並將其寫入 Pod 的檔案系統。這個過程對 Pod 完全透明,Pod 只需要像存取普通檔案一樣讀取秘密。

CSI Driver 的主要優勢在於不需要額外的 Sidecar 容器。Sidecar 模式雖然靈活,但每個 Pod 都需要執行一個額外的容器,消耗額外的資源。在大規模部署中,這些資源消耗累積起來可能相當可觀。CSI Driver 則是節點級別的服務,所有 Pod 共享同一個 Driver 實例,大幅降低了資源開銷。

另一個優勢是與 Kubernetes 的原生整合。CSI 是 Kubernetes 定義的標準介面,許多雲端服務提供商與儲存解決方案都支援 CSI。使用 CSI Driver 管理秘密讓秘密管理與其他儲存管理遵循一致的模式,簡化了運維人員的學習曲線。

Secret Store CSI Driver 部署組態

部署 Secret Store CSI Driver 需要先安裝 Driver 本身,然後安裝 Vault Provider,最後建立對應的 SecretProviderClass 資源。

helm repo add secrets-store-csi-driver \
    https://kubernetes-sigs.github.io/secrets-store-csi-driver/charts

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=60s

kubectl apply -f https://raw.githubusercontent.com/hashicorp/vault-csi-provider/main/deployment/vault-csi-provider.yaml

這個部署指令安裝了 CSI Driver 的核心元件,並啟用了秘密同步與自動輪替功能。syncSecret.enabled=true 讓 CSI Driver 能夠將秘密同步為 Kubernetes Secret,方便作為環境變數使用。enableSecretRotation=true 啟用自動輪替,Driver 會定期檢查 Vault 中的秘密是否更新。rotationPollInterval 設定檢查的頻率,這裡設為每六十秒檢查一次。

Vault CSI Provider 是專門針對 Vault 的 Driver 外掛,負責與 Vault API 互動,處理認證與秘密提取的具體邏輯。安裝完成後,Provider 會在每個節點上執行一個 DaemonSet Pod,準備處理來自 Kubelet 的秘密掛載請求。

SecretProviderClass 資源定義

SecretProviderClass 是 CSI Driver 的組態資源,定義了如何從 Vault 取得秘密,包括認證方法、秘密路徑、格式轉換等細節。

apiVersion: secrets-store.csi.x-k8s.io/v1
kind: SecretProviderClass
metadata:
  name: webapp-vault-secrets
  namespace: production
spec:
  provider: vault
  parameters:
    vaultAddress: "https://vault.example.com:8200"
    roleName: "webapp-csi-role"
    vaultSkipTLSVerify: "false"
    vaultCACertPath: "/vault/tls/ca.crt"
  objects: |
    - objectName: "db-username"
      secretPath: "secret/data/webapp/database"
      secretKey: "username"
    - objectName: "db-password"
      secretPath: "secret/data/webapp/database"
      secretKey: "password"
    - objectName: "api-key"
      secretPath: "secret/data/webapp/api"
      secretKey: "key"
  secretObjects:
  - secretName: webapp-credentials
    type: Opaque
    data:
    - objectName: "db-username"
      key: "username"
    - objectName: "db-password"
      key: "password"

這個 SecretProviderClass 定義了完整的秘密取得組態。parameters 區塊指定 Vault 伺服器的位址與認證角色。roleName 對應到 Vault 中預先建立的 Kubernetes 認證角色,這個角色必須允許 CSI Driver 使用的 Service Account 進行認證。

objects 區塊定義要從 Vault 取得的秘密清單。每個物件包含 objectName 作為本機識別符、secretPath 指向 Vault 中的秘密路徑,以及 secretKey 指定要提取秘密中的哪個欄位。Vault 的 KV v2 秘密引擎儲存的秘密通常是 JSON 物件,可能包含多個鍵值對,透過 secretKey 可以精確提取需要的部分。

secretObjects 區塊是可選的,定義如何將提取的秘密同步為 Kubernetes Secret。這個功能讓秘密能夠以環境變數的方式注入到容器中,對於需要環境變數的傳統應用程式很有用。同步產生的 Secret 會自動隨著 Vault 中的秘密更新而更新,保持同步。

使用 SecretProviderClass 的 Pod 定義需要宣告對應的 Volume 與 Volume Mount。

apiVersion: v1
kind: Pod
metadata:
  name: webapp
  namespace: production
spec:
  serviceAccountName: webapp-csi-sa
  containers:
  - name: webapp
    image: webapp:latest
    volumeMounts:
    - name: vault-secrets
      mountPath: "/mnt/secrets"
      readOnly: true
    env:
    - name: DB_USERNAME
      valueFrom:
        secretKeyRef:
          name: webapp-credentials
          key: username
    - name: DB_PASSWORD
      valueFrom:
        secretKeyRef:
          name: webapp-credentials
          key: password
  volumes:
  - name: vault-secrets
    csi:
      driver: secrets-store.csi.k8s.io
      readOnly: true
      volumeAttributes:
        secretProviderClass: webapp-vault-secrets

這個 Pod 定義展示了兩種使用秘密的方式。透過 Volume Mount,秘密會被寫入 /mnt/secrets 目錄,應用程式可以直接讀取檔案。同時,透過 secretKeyRef 引用同步產生的 Kubernetes Secret,秘密也能作為環境變數使用。這種雙重方式提供了最大的靈活性,讓應用程式可以選擇最適合的秘密存取方式。

@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

participant "Kubelet" as Kubelet
participant "CSI Driver\nNode Plugin" as CSI
participant "Vault CSI\nProvider" as Provider
participant "Vault Server" as Vault
participant "Pod" as Pod

note over Kubelet
  Pod 被排程到節點
  需要掛載秘密卷
end note

Kubelet -> CSI: 請求掛載 CSI 卷
activate CSI

CSI -> Provider: 轉發掛載請求
activate Provider

note over Provider
  解析 SecretProviderClass
  提取 Vault 組態參數
end note

Provider -> Provider: 讀取 Service Account Token
note right
  從 Pod 的 Volume Mount
  取得 Kubernetes Token
end note

Provider -> Vault: 使用 Token 進行認證
activate Vault

Vault -> Vault: 驗證 Token 與角色

Vault --> Provider: 回傳 Vault Token
deactivate Vault

Provider -> Vault: 請求秘密資料
activate Vault

note over Vault
  根據 SecretProviderClass
  定義的路徑提取秘密
end note

Vault --> Provider: 回傳秘密內容
deactivate Vault

Provider -> Provider: 格式化秘密資料
note right
  根據 objects 定義
  提取特定欄位
  轉換為檔案格式
end note

Provider -> Provider: 寫入秘密到臨時卷
note right
  使用 tmpfs 記憶體檔案系統
  避免秘密寫入磁碟
end note

Provider --> CSI: 回傳掛載點路徑
deactivate Provider

CSI -> CSI: 將臨時卷綁定到 Pod

CSI --> Kubelet: 掛載完成
deactivate CSI

Kubelet -> Pod: 啟動 Pod 容器
activate Pod

Pod -> Pod: 從掛載點讀取秘密
note right
  路徑:/mnt/secrets/
  秘密以檔案形式存在
end note

loop 定期輪替檢查
  Provider -> Vault: 檢查秘密版本
  activate Vault
  
  alt 秘密已更新
    Vault --> Provider: 回傳新秘密
    deactivate Vault
    Provider -> Provider: 更新臨時卷內容
    note right
      Pod 重新讀取即可取得新秘密
      部分應用程式支援熱重載
    end note
  else 秘密未變更
    Vault --> Provider: 回傳未修改狀態
    deactivate Vault
  end
end

@enduml

Vault 高可用性架構部署

在生產環境中,Vault 的可用性直接影響所有依賴它的應用程式。單點故障不僅會導致新應用程式無法啟動,還可能影響執行中應用程式的秘密更新與令牌續約。因此,部署高可用性的 Vault 叢集是企業級應用的必要條件。

Raft 整合式儲存架構

Vault 支援多種高可用性架構,較新的版本推薦使用整合式 Raft 儲存後端。Raft 是一個分散式共識演算法,能夠在多個節點之間同步資料並自動處理領導者選舉。使用 Raft 後端,Vault 不需要外部的儲存系統如 Consul 或 etcd,簡化了架構與運維複雜度。

在 Raft 架構中,Vault 叢集由多個節點組成,其中一個節點被選為領導者,負責處理所有的寫入請求。其他節點作為跟隨者,接收並複製領導者的資料變更。當客戶端向跟隨者發送讀取請求時,跟隨者可以直接回應,提供讀取的水平擴展能力。如果領導者失效,跟隨者會自動選舉新的領導者,確保服務的連續性。

global:
  enabled: true
  tlsDisable: false

server:
  image:
    repository: "hashicorp/vault"
    tag: "1.15.0"
  
  updateStrategyType: "RollingUpdate"
  
  resources:
    requests:
      memory: "256Mi"
      cpu: "250m"
    limits:
      memory: "512Mi"
      cpu: "500m"
  
  readinessProbe:
    enabled: true
    path: "/v1/sys/health?standbyok=true&sealedcode=204&uninitcode=204"
  livenessProbe:
    enabled: true
    path: "/v1/sys/health?standbyok=true"
    initialDelaySeconds: 60
  
  extraEnvironmentVars:
    VAULT_CACERT: /vault/userconfig/vault-tls/ca.crt
  
  extraVolumes:
    - type: secret
      name: vault-tls
      path: /vault/userconfig/vault-tls
  
  standalone:
    enabled: false
  
  ha:
    enabled: true
    replicas: 3
    
    raft:
      enabled: true
      setNodeId: true
      
      config: |
        ui = true
        
        listener "tcp" {
          tls_disable = false
          address = "[::]:8200"
          cluster_address = "[::]:8201"
          tls_cert_file = "/vault/userconfig/vault-tls/tls.crt"
          tls_key_file  = "/vault/userconfig/vault-tls/tls.key"
          tls_client_ca_file = "/vault/userconfig/vault-tls/ca.crt"
        }
        
        storage "raft" {
          path = "/vault/data"
          
          retry_join {
            leader_api_addr = "https://vault-0.vault-internal:8200"
            leader_ca_cert_file = "/vault/userconfig/vault-tls/ca.crt"
          }
          retry_join {
            leader_api_addr = "https://vault-1.vault-internal:8200"
            leader_ca_cert_file = "/vault/userconfig/vault-tls/ca.crt"
          }
          retry_join {
            leader_api_addr = "https://vault-2.vault-internal:8200"
            leader_ca_cert_file = "/vault/userconfig/vault-tls/ca.crt"
          }
        }
        
        service_registration "kubernetes" {}

ui:
  enabled: true
  serviceType: "LoadBalancer"

這個 Helm Values 檔案定義了一個三節點的 Vault 高可用性叢集。ha.enabled 啟用高可用性模式,ha.replicas 設定節點數量。在生產環境中,建議使用奇數個節點,通常是三個或五個,以確保 Raft 共識演算法能夠正常運作。

raft.config 區塊包含詳細的 Vault 組態。listener 定義了 API 介面的監聽位址與 TLS 設定,在生產環境中必須啟用 TLS 保護通訊安全。storage 區塊組態 Raft 後端,retry_join 定義其他節點的位址,新節點啟動時會自動嘗試加入現有叢集。

service_registration 組態讓 Vault 將其服務資訊註冊到 Kubernetes,這使得其他 Pod 能夠透過 Kubernetes DNS 發現 Vault 服務,無需硬編碼 Vault 的位址。

叢集初始化與解封流程

部署 Vault 叢集後,需要執行初始化與解封操作才能開始使用。初始化只需要執行一次,會產生根令牌與解封金鑰。解封則是每次 Vault 重啟後都需要執行的操作,用於解密主金鑰。

helm repo add hashicorp https://helm.releases.hashicorp.com

helm install vault hashicorp/vault \
    --namespace vault \
    --create-namespace \
    --values vault-ha-values.yaml

kubectl wait --for=condition=ready pod/vault-0 -n vault --timeout=300s

kubectl exec -n vault vault-0 -- vault operator init \
    -key-shares=5 \
    -key-threshold=3 \
    -format=json > vault-init-keys.json

VAULT_UNSEAL_KEY_1=$(jq -r '.unseal_keys_b64[0]' vault-init-keys.json)
VAULT_UNSEAL_KEY_2=$(jq -r '.unseal_keys_b64[1]' vault-init-keys.json)
VAULT_UNSEAL_KEY_3=$(jq -r '.unseal_keys_b64[2]' vault-init-keys.json)
ROOT_TOKEN=$(jq -r '.root_token' vault-init-keys.json)

kubectl exec -n vault vault-0 -- vault operator unseal $VAULT_UNSEAL_KEY_1
kubectl exec -n vault vault-0 -- vault operator unseal $VAULT_UNSEAL_KEY_2
kubectl exec -n vault vault-0 -- vault operator unseal $VAULT_UNSEAL_KEY_3

初始化指令使用 Shamir 秘密分享演算法產生五個解封金鑰片段,需要其中任意三個才能解封 Vault。這個機制提供了安全性與可用性的平衡,即使部分金鑰遺失或持有者不可用,仍然能夠解封 Vault。在企業環境中,這些金鑰應該分配給不同的管理員保管,避免單點風險。

初始化完成後,第一個節點會成為領導者並處於解封狀態。其他節點需要加入叢集並同樣進行解封。

kubectl exec -n vault vault-1 -- vault operator raft join \
    -leader-ca-cert="$(cat vault-tls/ca.crt)" \
    https://vault-0.vault-internal:8200

kubectl exec -n vault vault-2 -- vault operator raft join \
    -leader-ca-cert="$(cat vault-tls/ca.crt)" \
    https://vault-0.vault-internal:8200

for i in {1..2}; do
    kubectl exec -n vault vault-$i -- vault operator unseal $VAULT_UNSEAL_KEY_1
    kubectl exec -n vault vault-$i -- vault operator unseal $VAULT_UNSEAL_KEY_2
    kubectl exec -n vault vault-$i -- vault operator unseal $VAULT_UNSEAL_KEY_3
done

每個節點加入叢集後,也需要使用相同的解封金鑰進行解封。解封是一個暫態操作,Vault 重啟後會回到密封狀態,需要重新執行解封。在生產環境中,可以使用自動解封機制,透過雲端 KMS 服務或硬體安全模組自動解封,避免手動操作的麻煩與安全風險。

驗證叢集狀態可以透過查看 Raft 成員資訊與領導者狀態。

kubectl exec -n vault vault-0 -- vault login $ROOT_TOKEN

kubectl exec -n vault vault-0 -- vault operator raft list-peers

正常運作的叢集應該顯示所有節點都處於連線狀態,且有一個明確的領導者。當領導者失效時,其他節點會自動選舉新領導者,整個過程通常在數秒內完成,對客戶端的影響最小。

資料持久化與備份策略

Vault 的資料包含所有的秘密、政策、認證組態等關鍵資訊,必須確保資料的持久化與定期備份。在 Kubernetes 環境中,Vault 使用 PersistentVolumeClaim 儲存 Raft 資料。

server:
  dataStorage:
    enabled: true
    size: 10Gi
    storageClass: "fast-ssd"
    accessMode: ReadWriteOnce
  
  auditStorage:
    enabled: true
    size: 5Gi
    storageClass: "standard"
    accessMode: ReadWriteOnce

這個組態為每個 Vault 節點建立獨立的持久化儲存卷。dataStorage 用於儲存 Raft 資料,應該使用高效能的儲存類別如 SSD,因為 Raft 對於儲存延遲較為敏感。auditStorage 用於儲存稽核日誌,可以使用較便宜的標準儲存。

即使使用了持久化儲存,定期備份仍然是必要的。Vault 提供了快照功能,能夠產生一致性的資料快照。

kubectl exec -n vault vault-0 -- vault operator raft snapshot save /tmp/vault-snapshot.snap

kubectl cp vault/vault-0:/tmp/vault-snapshot.snap ./vault-backup-$(date +%Y%m%d).snap

aws s3 cp ./vault-backup-$(date +%Y%m%d).snap \
    s3://vault-backups/$(date +%Y%m%d)/

這個備份流程從領導者節點產生快照,複製到本機,然後上傳到雲端儲存。備份應該定期自動執行,例如每日一次,並保留適當的歷史版本。在災難發生時,可以使用快照恢復 Vault 叢集。

kubectl exec -n vault vault-0 -- vault operator raft snapshot restore \
    -force /tmp/vault-snapshot.snap

恢復操作會覆寫現有的資料,因此應該在確認必要時才執行。完整的災難恢復計畫還應該包括定期演練,確保備份可用且恢復流程能夠在緊急情況下順利執行。

生產環境最佳實踐與安全加固

部署 Vault 到生產環境需要考慮許多安全性與可靠性的面向。除了基本的高可用性架構外,還需要實施多層次的安全控制,確保 Vault 本身以及其管理的秘密得到妥善保護。

網路安全與存取控制

Vault 應該部署在受保護的網路區段,限制能夠存取 Vault API 的來源。在 Kubernetes 環境中,可以使用 NetworkPolicy 實施網路隔離。

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: vault-network-policy
  namespace: vault
spec:
  podSelector:
    matchLabels:
      app.kubernetes.io/name: vault
  policyTypes:
  - Ingress
  - Egress
  ingress:
  - from:
    - namespaceSelector:
        matchLabels:
          vault-access: "true"
    ports:
    - protocol: TCP
      port: 8200
  - from:
    - podSelector:
        matchLabels:
          app.kubernetes.io/name: vault
    ports:
    - protocol: TCP
      port: 8200
    - protocol: TCP
      port: 8201
  egress:
  - to:
    - podSelector:
        matchLabels:
          app.kubernetes.io/name: vault
  - to:
    - namespaceSelector: {}
      podSelector:
        matchLabels:
          k8s-app: kube-dns
    ports:
    - protocol: UDP
      port: 53

這個 NetworkPolicy 限制只有標記為允許存取 Vault 的 Namespace 中的 Pod 能夠連線到 Vault API。Vault 節點之間的叢集通訊也被明確允許。同時允許 Vault 存取 DNS 服務,這對於 Kubernetes 服務發現是必要的。

在叢集層級,應該使用 RBAC 控制對 Vault 資源的存取。只有授權的管理員應該能夠修改 Vault 的部署組態、查看 Vault 的日誌或執行管理指令。

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: vault-admin
  namespace: vault
rules:
- apiGroups: [""]
  resources: ["pods", "pods/log", "pods/exec"]
  verbs: ["get", "list", "create"]
- apiGroups: ["apps"]
  resources: ["statefulsets"]
  verbs: ["get", "list", "patch", "update"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: vault-admin-binding
  namespace: vault
subjects:
- kind: User
  name: vault-ops-team
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: vault-admin
  apiGroup: rbac.authorization.k8s.io

金鑰管理與自動解封

手動解封 Vault 在生產環境中是一個挑戰,特別是在自動擴展或災難恢復的場景中。Vault 支援自動解封機制,可以使用雲端 KMS 服務或其他受信任的金鑰管理系統自動解封。

seal "awskms" {
  region     = "us-west-2"
  kms_key_id = "arn:aws:kms:us-west-2:123456789012:key/12345678-1234-1234-1234-123456789012"
  endpoint   = "https://kms.us-west-2.amazonaws.com"
}

使用 AWS KMS 自動解封時,Vault 會在啟動時自動向 KMS 請求解密主金鑰。這需要 Vault 執行的環境具有適當的 IAM 權限。自動解封大幅簡化了 Vault 的運維,特別是在節點故障自動恢復的場景中。

對於無法使用雲端 KMS 的環境,可以考慮使用 Vault 的 Transit 秘密引擎實現類似的功能。設置一個專門的 Vault 叢集作為自動解封的金鑰來源,被保護的 Vault 叢集在啟動時向這個專門叢集請求解封。

稽核與監控整合

完整的稽核日誌對於安全合規與事件調查至關重要。Vault 的稽核日誌應該即時轉發到集中式的日誌管理系統,避免日誌在節點故障時遺失。

vault audit enable -path="audit-to-elk" socket \
    address="logstash.logging.svc:5000" \
    socket_type="tcp" \
    format="json"

除了稽核日誌,Vault 還提供了豐富的監控指標,可以透過 Prometheus 整合到現有的監控系統。

server:
  extraEnvironmentVars:
    VAULT_TELEMETRY_PROMETHEUS_RETENTION_TIME: "30s"
  
  serviceMonitor:
    enabled: true
    interval: "30s"

關鍵的監控指標包括解封狀態、領導者選舉事件、令牌過期與續約統計、秘密引擎效能、稽核日誌寫入延遲等。建立適當的告警規則,在 Vault 出現異常時及時通知運維團隊。

定期的安全審查也是必要的實踐。審查 Vault 的政策定義,確保遵循最小權限原則。檢查認證方法的組態,驗證令牌的有效期設定合理。分析稽核日誌,識別異常的存取模式。這些主動的安全措施能夠在問題變成實際的安全事件之前發現並修正。

結論

在 Kubernetes 環境中整合 HashiCorp Vault 實現企業級的秘密管理,需要深入理解認證機制、政策控制、秘密注入方式以及高可用性架構等多個面向。透過本文詳細的技術解析與實務範例,我們探討了從基礎的 Kubernetes 認證整合,到進階的 CSI Driver 應用,再到生產環境的高可用性部署與安全加固的完整方案。

Vault Agent Injector 與 CSI Driver 提供了兩種主流的秘密注入方式,各有其適用場景。Injector 的 Sidecar 模式提供了更大的靈活性與功能豐富性,特別是在需要複雜的秘密模板化或應用程式需要主動秘密更新通知時。CSI Driver 則更輕量級且更符合 Kubernetes 的原生設計理念,在資源效率與管理簡化方面具有優勢。實務上,可以根據具體的應用程式需求與運維考量選擇適合的方式,甚至在同一個叢集中混合使用兩種方法。

高可用性架構是生產環境部署的基礎要求。Raft 整合式儲存後端簡化了 Vault 的部署架構,消除了對外部儲存系統的依賴,同時提供了自動的領導者選舉與資料複製機制。配合適當的資源規劃、持久化儲存組態以及災難恢復計畫,能夠建立一個可靠且可擴展的秘密管理基礎設施。

安全性必須貫穿整個秘密管理生命週期。從網路層的存取控制、認證層的身份驗證、授權層的政策管理,到稽核層的完整日誌記錄,每個環節都需要細緻的安全設計。自動解封、金鑰輪替、秘密版本控制等進階功能進一步提升了安全性與可管理性。

展望未來,隨著雲原生技術的持續演進,秘密管理也將朝向更自動化與智慧化的方向發展。機器學習驅動的異常偵測能夠識別可疑的秘密存取模式,自動化的金鑰輪替與秘密更新機制減少人工介入的需求,而與服務網格的深度整合將實現更細緻的應用程式間通訊安全控制。同時,零信任架構的普及推動了對動態秘密與短期憑證的需求,Vault 的動態秘密產生功能將發揮更重要的作用。

對於採用 Kubernetes 與 Vault 建構秘密管理解決方案的組織而言,持續學習與實踐是關鍵。技術文件與社群資源提供了豐富的知識來源,而實際的部署經驗與故障排除過程則能深化對系統行為的理解。建立標準化的部署範本、自動化的測試流程以及完整的災難恢復演練,能夠確保秘密管理系統在關鍵時刻可靠運作,為雲原生應用程式提供堅實的安全基礎。