天天看點

如何在 Kubernetes 叢集中把 Vault 用起來Vault是什麼?Vault 與 k8s 的內建Vault 與阿裡雲 RAM 的內建如何在 K8s 應用中使用 Vault Secret

原文作者:dahukkk

原文連結:

https://developer.aliyun.com/article/727671

Vault是什麼?

如何在雲上應用中管理和保護使用者的敏感資訊是一個經常令開發者頭疼的問題,使用者的密碼密碼,證書秘鑰等私密資訊時常未經加密被随意的放置在配置檔案,代碼倉庫或是共享存儲裡,而對于普通的開發者來說,設計和實作一套完整的秘鑰管理系統是一個很大的挑戰。且不論令人生畏的加解密算法,很多的雲應用仍然将一些敏感配置資訊僅僅經過 base64 等一些簡單的 hash 運算就放置在某個公共的配置中心上,而很多時候這些敏感資訊會從應用的某行異常日志或是某段監控告警中洩露出去;不僅如此,對于一個集中式的秘鑰管理系統,如何面向使用者進行更細粒度的通路鑒權也是一個難題。

如何在 Kubernetes 叢集中把 Vault 用起來Vault是什麼?Vault 與 k8s 的內建Vault 與阿裡雲 RAM 的內建如何在 K8s 應用中使用 Vault Secret

Vault 的出現給了上述問題一個解決方案,它是 HashiCorp 公司(旗下還有 Vagrant,Terraform,Consul 等知名産品)維護的開源軟體,它的設計思想基于雲原生背景下動态基礎設施的特點,在雲上的不同網絡層以及不同的服務之間已經很難找到傳統的信任邊界,服務之間更加強調以身份(identity)為核心的認證和通路控制,而不是像傳統靜态基礎設施中以 IP、主機位址作為信任憑證。為此 Vault 提供了以下幾個功能點:

  • Secret存儲形式的多樣性,任意的kv形式敏感資訊(如資料庫密碼,證書,ssh登入秘鑰,openapi身份憑證等);
  • 存儲格式的多樣性,支援插件式的存儲引擎擴充,可對接如 AWS,Consul,NoSQL,KV,PKI,SSH 等多種插件引擎;
  • 支援與各類平台的認證對接,可動态生成認證憑據或配置資訊;
  • 支援基于 Shamir 算法的私鑰分割完成 Vault 後端的加封和解封操作,同時支援高可用的部署形态;
  • 支援各類 secret 的動态生成,續租,撤銷和滾動更新;
  • 完備的審計日志;
  • 完備的 CLI 和 RESTful API

Vault 與 k8s 的內建

Vault 松耦合的架構使其支援與多種 secret 引擎和相應的存儲後端對接,同時支援與多種認證伺服器的互動。

本節我們主要介紹 Vault 與 k8s 的內建。

2.1 Vault 在 Kubernetes 中的應用場景

Vault 作為企業級的 secret 管理工具,是一些大客戶在業務上雲過程中的安全強需求,尤其是國外市場。在 Kubernetes 叢集中主要有以下應用場景:

  • 作為部署在 Kubernetes 叢集中的應用對外提供秘鑰管理服務,支援與多家主流雲廠商秘鑰服務以及多種 secrets 形式的對接,支援多種資料庫服務的存儲對接,同時支援多種認證形式的對接。
  • 作為一個公共的加密服務(Encryption as a Service)而不做後端存儲的對接,幫助使用者應用剝離繁瑣的加密加解密邏輯。
  • 面向政府、金融等對資料安全規格有很高要求的客戶,Vault 支援基于 Two-man 原則利用私鑰分割算法對後端服務進行加解封,并結合 k8s 的高可用部署形态為企業提供更加安全可靠的 secret 管理能力。

    當然這裡隻是列舉了一些 Vault 原生提供的能力,作為一個在 Kubernetes 叢集上直接運作的安全應用,任何一個面向 k8s 的應用工具都可以利用其安全能力。

2.2 安裝 Vault

Vault 支援 helm 化安裝,在其官方文檔中我們可以找到關于啟動參數的詳細配置說明,同時在阿裡雲容器服務的應用目錄 apphub 中我們也可以通過控制台在 ACK 叢集中友善的安裝 Vault

如何在 Kubernetes 叢集中把 Vault 用起來Vault是什麼?Vault 與 k8s 的內建Vault 與阿裡雲 RAM 的內建如何在 K8s 應用中使用 Vault Secret

另外 Vault 的預設安裝也內建了其控制台的安裝,通過負載均衡服務或 ingress 路由的方式我們可以在公網通路其 UI,在 vault pod 的日志中我們可以找到登入使用的 root token,在控制台中可以友善的設定與存儲引擎和認證方式的對接,同時還可以進行基于政策的通路控制配置。

如何在 Kubernetes 叢集中把 Vault 用起來Vault是什麼?Vault 與 k8s 的內建Vault 與阿裡雲 RAM 的內建如何在 K8s 應用中使用 Vault Secret

2.3 認證方式的內建

當使用者希望在 k8s pod 的業務邏輯中與 Vault 服務端通訊,擷取需要的 secrets 時,首先 Vault 會對這個 pod 中的請求進行認證,那麼這個 pod 中的 Vault 請求認證憑據應該如何擷取呢?如上所述,Vault 後端支援多種認證方式的對接,對于 Kubernetes,Vault 支援基于 K8s Service Account Token 的認證。

使用上,Vault 管理者首先需要在後端 enable kubernetes 的認證方式,生成一個與 Vault 互動的指定 sa,然後通過 CLI 或 API 将 sa token 和叢集 ca,公網位址等資訊寫入到 Vault 後端中,并配置與 vault 後端的 ACL 政策綁定。詳細步驟請參見官方文檔。

當然在 pod 應用中可以并不局限于一定使用基于 sa 的 kubernetes 認證方式。比如[kubernetes-vault](

https://github.com/Boostport/kubernetes-vault

)。該項目使用 Vault 中的[ AppRole ](

https://www.vaultproject.io/docs/auth/approle.html

)認證方式,在該認證模式中,管理者可以為不同的 pod 建立不同的 Vault 原生 role 模型并綁定到對應的 policy 上,同時可以基于 role 建立 secret_id,secret_id 對應的 token 可作為與 Vault 進行認證的臨時憑證。kubernetes-vault 利用了 AppRole 的認證互動模式,首先已經完成安裝的 kubernetes-vault controller 會去 Watch 叢集中所有 pod 的建立,當發現建立 pod 的部署模闆中有指定 annotation 的 init-container 存在時,controller 會根據模闆中指定的 vault role id 去 Vault 請求擷取其對應的 secret_id 并發送給 init-container 中 kubernetes-vault 的用戶端,在應用容器啟動前 kubernetes-vault 用戶端用 controller 傳回的 secret_id 和 role_id 去 Vault 請求真正的 login token 并最終寫入到與應用 pod 共享的挂載目錄中;同時用戶端會根據 token 過期時間進行定時的輪轉,保證其可用性。下圖為 kubernetes-vault 工作流程圖:

如何在 Kubernetes 叢集中把 Vault 用起來Vault是什麼?Vault 與 k8s 的內建Vault 與阿裡雲 RAM 的內建如何在 K8s 應用中使用 Vault Secret

在社群也存在不少基于 k8s 與 Vault 進行認證對接的其他方案,其設計思路大同小異,基本都采用了通過 init-container 或 sidecar 方式引入一個額外的用戶端去 Vault 請求指定認證模式下的短時憑證并共享給業務容器使用。

在容器服務控制台的應用目錄 apphub 中,我們同樣可以找到 kubernetes-vault,友善開發者使用 helm 直接在叢集指定命名空間一鍵部署。Vault 也計劃在後續自己的官方版本 helm chart 中增加配置項以支援上述登入認證 secret 的動态注入。

Vault 與阿裡雲 RAM 的內建

當我們在應用中需要通路阿裡雲資源時,需要使用 RAM 賬号對應的 AK 或是 STS 臨時 credentials 作為通路相應資源接口的憑證。如果使用賬号 AK,如何使其能夠被應用邏輯擷取的同時保證 AK 的安全性一直是一個頭疼的問題;如果使用臨時 sts token,由于其時效性,我們也需要在考慮安全性的同時思考如何進行臨時通路憑證的輪轉。相比較兩種方式,使用 sts 臨時憑證的方式肯定在安全上是更為推薦的方式,同時對這種動态 secret 的安全管理也正是 Vault 的優勢所在。本節我們來介紹下 Vault 與阿裡雲 RAM 在認證方式和 secret 管理引擎上的內建。

3.1 認證方式的內建

首先在認證方式上,Vault 服務端的 role 模型可以與 RAM role 進行一對一的映射比對,使用者可以使用 Vault提供的 OpenAPI 或是 CLI,通過傳入扮演RAM role 傳回的臨時憑證調用GetCallerIdentity 接口,然後 Vault 服務端會根據請求傳回的角色 arn id 在其後端存儲中查找是否有對應的權限政策配置,如果存在則認證成功并傳回一個可用于調用 Vault 其他後端接口的通路 token。

3.2 Vault Secret 引擎與 RAM 的內建

當我們需要在業務應用邏輯中使用阿裡雲資源時,通常需要通過角色扮演的方式擷取一個 RAM 傳回的臨時憑證,然後通過這個臨時憑證完成與 RAM 的鑒權過程。由于憑證的時效性,我們在保證其安全性的同時還要維護一個對應的秘鑰輪轉機制。Vault 的 secret 引擎實作了與阿裡雲 RAM 的對接插件,幫助我們安全、動态的管理 RAM 憑證,其主要步驟 如下:

1.開啟後端引擎

2.在 RAM 控制台為 Vault 伺服器建立專屬子賬号并綁定定制化權限政策

3.擷取 Vault 子賬号對應的 AK 并通過 Vault CLI/API 寫入到後端指定路徑下

4.在 Vault 後端寫入業務中希望擷取的 RAM 憑證所對應的政策定義或角色,其中政策定義支援 inline 和 remote 政策兩種形式,所謂 inline 模式是指直接在 api 請求中寫入政策模闆,remote 模式指寫入 RAM 中存在的政策類型和名稱,比如:

$ vault write alicloud/role/policy-based \
    remote_policies='name:AliyunOSSReadOnlyAccess,type:System' \
    remote_policies='name:AliyunRDSReadOnlyAccess,type:System'           

角色模式需要使用者指定希望被扮演的角色 arn,另外需要 Vault 子賬号在該角色的受信實體裡,一個示例如下:

$ vault write alibaba/role/role-based \
role_arn='acs:ram::5138828231865461:role/hastrustedactors'           

5.在具體的業務應用中,隻需要通過調用 Vault 的 creds/policy-based 或 role-based 接口即可動态擷取相應的 RAM 通路憑證,下面是一個角色扮演傳回臨時 token 的 CLI 調用示例:

$ vault read alicloud/creds/role-based
Key                Value
---                -----
lease_id           alicloud/creds/role-based/f3e92392-7d9c-09c8-c921-575d62fe80d9
lease_duration     59m59s
lease_renewable    false
access_key         STS.L4aBSCSJVMuKg5U1vFDw
secret_key         wyLTSmsyPGP1ohvvw8xYgB29dlGI8KMiH2pKCNZ9
security_token     CAESrAIIARKAAShQquMnLIlbvEcIxO6wCoqJufs8sWwieUxu45hS9AvKNEte8KRUWiJWJ6Y+YHAPgNwi7yfRecMFydL2uPOgBI7LDio0RkbYLmJfIxHM2nGBPdml7kYEOXmJp2aDhbvvwVYIyt/8iES/R6N208wQh0Pk2bu+/9dvalp6wOHF4gkFGhhTVFMuTDRhQlNDU0pWTXVLZzVVMXZGRHciBTQzMjc0KgVhbGljZTCpnJjwySk6BlJzYU1ENUJuCgExGmkKBUFsbG93Eh8KDEFjdGlvbkVxdWFscxIGQWN0aW9uGgcKBW9zczoqEj8KDlJlc291cmNlRXF1YWxzEghSZXNvdXJjZRojCiFhY3M6b3NzOio6NDMyNzQ6c2FtcGxlYm94L2FsaWNlLyo=
expiration         2018-08-15T21:58:00Z           

如何在 K8s 應用中使用 Vault Secret

在了解了 Vault 的基本概念以及與 Kubernetes 的認證互動流程後,我們進入客戶最為關心的話題。如何在 k8s pod 應用中友善地擷取 Vault 服務端管理的 secret。社群針對此問題也有激烈的讨論和不少相關解決方案,方案主要集中在兩個方向:

[定時同步程序](

https://github.com/hashicorp/vault/issues/7364

):使用一個同步程序定時地從 Vault 服務端擷取指定範圍的秘鑰更新并同步到 K8s 叢集中的 secret 模型,代表的項目有 vaultingkube 和[secrets-manager](

https://github.com/tuenti/secrets-manager

) 。其主要設計思想也不盡相同,以 secrets-manager 為例,首先使用者可以通過 CRD 定義在 Vault 中關注的 secret 資料源,然後 secrets-manager 對應的 controller 會在 Reconcile 函數中定時對比指定管理範圍内的 K8s secret 和 vault secret 的狀态,如果不一緻則進行一次調協。而使用者在 pod 應用中可以直接引用原生 secret 模型中的内容擷取遠端 Vault 伺服器中的秘鑰。

當然社群中也存在一些對這種秘鑰同步方案的質疑,比如認為該方案在秘鑰同步的傳輸過程和使用者 pod 使用原生 secret 的 rest 互動中會增加攻擊面,但是該方案在部署實施上比較友好,也得到了很多使用者的支援。

CSI 插件形式內建:該方案基于 CSI plugin 将 Vault 中的秘鑰通過 volume 的形式挂載到 pod 應用中。

secrets-store-csi-driver 通過實作一套基于 CSI 規範的 driver 機制可以對接不同廠商的後端存儲,而 Vault secret 的 driver(secrets-store.csi.k8s.com)允許 kubelet 将各類企業級秘鑰存儲中的 secret 通過 volume 挂載,一旦 attach 動作完成,秘鑰資料即挂載到了容器對應的檔案系統中。在 CSI driver 的基礎上,不同的秘鑰管理後端可以實作定制化的 provider 去對接 CSI driver 架構中的規定接口。provider 的功能概括如下:

  • 對接後端秘鑰管理系統,提供秘鑰擷取等必須的接口實作
  • 适配目前 CSI driver 的接口定義
  • 通過架構中的回調函數無需調用 Kubernetes API 即可将從後端擷取的秘鑰資料挂載到指定路徑下

    HashiCorp 官方也基于此架構實作了一套對接 Vault 的 Provider。這裡我們以此為例具體來看下在一個 k8s pod 應用中如何通過 CSI plugin 的方式使用 Vault 中管理的 secret 秘鑰。

1 首先我們建立一個開啟了 CSI 存儲插件的 ACK 叢集,然後參考文檔在叢集中部署 Vault 服務端,為了便于驗證這裡我們使用 dev 模式省去 unseal 解封等流程,同時配置 provider 與 Vault 互動的認證模式和相應的通路控制政策

如何在 Kubernetes 叢集中把 Vault 用起來Vault是什麼?Vault 與 k8s 的內建Vault 與阿裡雲 RAM 的內建如何在 K8s 應用中使用 Vault Secret

然後通過 cli 向 Vault 後端寫入測試資料

如何在 Kubernetes 叢集中把 Vault 用起來Vault是什麼?Vault 與 k8s 的內建Vault 與阿裡雲 RAM 的內建如何在 K8s 應用中使用 Vault Secret

2 通過官方提供的 helm 方式安裝 Secret Store CSI Driver,指令如下:

helm install . -n csi-secrets-store --namespace dev --set providers.vault.enabled=true           

安裝成功後如下圖所示:

如何在 Kubernetes 叢集中把 Vault 用起來Vault是什麼?Vault 與 k8s 的內建Vault 與阿裡雲 RAM 的內建如何在 K8s 應用中使用 Vault Secret

3 在叢集中建立 secretproviderclasses 執行個體用于 Secret Store CSI Driver 與 Vault 的參數對接,一個示例如下,注意這裡的 vault 服務端位址可通過 kubectl get service vault 擷取。

如何在 Kubernetes 叢集中把 Vault 用起來Vault是什麼?Vault 與 k8s 的內建Vault 與阿裡雲 RAM 的內建如何在 K8s 應用中使用 Vault Secret

4 最後我們來看下如何在應用 pod 中對接上述provider 執行個體擷取對應的 Vault 秘鑰。這裡 pod 對于上述 vault provider 的使用分為兩種方式:

1)如果 pod 運作的目标叢集版本在 v1.15.0 以上,且叢集 apiserver 和節點 kubelet manifest 配置均開啟了

CSIInlineVolume=true

的 feature-gates,則我們可以在 pod 中的 volume 字段内置聲明需要使用的 csi provider 執行個體。

kind: Pod
apiVersion: v1
metadata:
  name: nginx-secrets-store-inline
spec:
  containers:
  - image: nginx
    name: nginx
    volumeMounts:
    - name: secrets-store-inline
      mountPath: "/mnt/secrets-store"
      readOnly: true
  volumes:
    - name: secrets-store-inline
      csi:
        driver: secrets-store.csi.k8s.com
        readOnly: true
        volumeAttributes:
          secretProviderClass: "vault-foo"
···
2)如果目标叢集不支援 CSI 的 Inline Volume 特性,我們需要首先建立使用 csi 的 pv 和對應的 pvc 執行個體,一個 pv 模闆示例如下:
···
apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv-vault
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadOnlyMany
  persistentVolumeReclaimPolicy: Retain
  csi:
    driver: secrets-store.csi.k8s.com
    readOnly: true
    volumeHandle: kv
    volumeAttributes:
      providerName: "vault"
      roleName: "example-role"
      vaultAddress: http://172.21.12.21:8200
      vaultSkipTLSVerify: "true"
      objects:  |
        array:
          - |
            objectPath: "/foo"
            objectName: "bar"
            objectVersion: ""           

在 pod 執行個體模闆中引用指定 pvc 即可在 pod 中擷取到 vault,這裡我們在 ACK 叢集以 pv/pvc 模式為例建立一個 nginx 應用容器執行個體并在其中挂載上文中我們建立的 secretproviderclasses 執行個體:

如何在 Kubernetes 叢集中把 Vault 用起來Vault是什麼?Vault 與 k8s 的內建Vault 與阿裡雲 RAM 的內建如何在 K8s 應用中使用 Vault Secret

相比于 secrets-manager 等采用 secret 定時同步的方式,使用 CSI 對接指定 Vault secret provider 執行個體的方式雖然在實施步驟上比較複雜,同時在應用中也無法動态擷取 Vault 後端 secret 的變更,但是該方案避免了 secret 在同步鍊路上頻繁傳輸的安全風險,同時也客服了之前 describe po 可能造成的秘鑰洩露,在整體安全性上要高出不少。大家可以根據實際應用場景選擇适合自己的方式。

“阿裡巴巴雲原生微信公衆号(ID:Alicloudnative)關注微服務、Serverless、容器、Service Mesh 等技術領域、聚焦雲原生流行技術趨勢、雲原生大規模的落地實踐,做最懂雲原生開發者的技術公衆号。”

繼續閱讀