全部產品
Search
文件中心

Container Service for Kubernetes:使用Vault作為KMS服務

更新時間:Aug 29, 2024

Vault是一個基於身份的密鑰管理和資料加密系統,提供對Token、密碼、認證、API Key等常見敏感憑據的安全儲存和控制,可有效解決應用系統中對敏感資訊的寫入程式碼問題。本文介紹如何在ACK叢集中部署和使用Vault。

前提條件

安裝初始化Vault

步驟一:安裝Vault

  1. 任選以下方式擷取Vault的安裝包。本文樣本中Helm的Chart版本為vault-0.24.1,Vault版本為1.3.1。

    • 登入Git倉庫vault-helm擷取。

    • 通過遠程倉庫擷取。執行以下命令,添加並更新倉庫。

      helm repo add hashicorp https://helm.releases.hashicorp.com
      helm repo update
  2. 執行以下命令,配置別名(Alias)簡化操作命令。

    以下指令碼代碼以ACK叢集的KubeConfig資訊放置在$HOME/Downloads/kubeconfig檔案中為例說明,使用時KubeConfig位置資訊請根據實際位置替換。

    # Helm用戶端。
    alias h="helm --kubeconfig $HOME/Downloads/kubeconfig"
    # kubectl用戶端。
    alias k="kubectl --kubeconfig $HOME/Downloads/kubeconfig"
  3. 安裝Vault。

    生產環境建議安裝Raft版本的Vault,請勿使用Standard alone版本。

    1. 執行以下命令,建立名為vault的命名空間。

      k create ns vault

      將Vault安裝在vault的命名空間中,後續和K8s Namespace相關的值都為vault

    2. 執行以下命令,在名為vault的命名空間中安裝Vault。

      以下StorageClass(SC)使用ACK支援的SC,可通過k get sc 查詢。儲存的Size不小於20 GiB。部署完成後,將產生三個隨用隨付的ESSD雲端硬碟,作為Vault的Pod掛載使用的PV。關於雲端硬碟的計費資訊,請參見計費

      h install vault -nvault hashicorp/vault \
          --set='server.ha.enabled=true' \
          --set='server.ha.raft.enabled=true' \
          --set='server.dataStorage.size=20Gi' \
          --set='server.dataStorage.storageClass=alicloud-disk-essd'
    3. 執行以下命令,查看Vault的Pod狀態。

      k get po -n vault

      預期輸出:

      NAME                                    READY   STATUS    RESTARTS   AGE
      vault-0                                 0/1     Running   0          45s
      vault-1                                 0/1     Running   0          45s
      vault-2                                 0/1     Running   0          44s
      vault-agent-injector-59fdd7cdf8-prwv7   1/1     Running   0          45s

步驟二:初始化和解鎖Vault

  1. 執行以下命令,查看Vault第一次啟動後的狀態。

    k exec -nvault vault-0 -- vault status

    預期輸出:

    Key                Value
    ---                -----
    Seal Type          shamir
    Initialized        false
    Sealed             true
    Total Shares       0
    Threshold          0
    Unseal Progress    0/0
    Unseal Nonce       n/a
    Version            1.13.1
    Build Date         2023-03-23T12:51:35Z
    Storage Type       raft
    HA Enabled         true
    command terminated with exit code 2

    InitializedfalseSealedtrue時,表明Vault未進行初始化,且沒有解鎖(Unseal)。您需要進行後續的初始化和解鎖操作。

  2. 執行以下命令,初始化Vault。

    通過容器內的Vault二進位,產生Key。

    k exec vault-0 -nvault -- vault operator init -key-shares=5 -key-threshold=3 -format=json > cluster-keys.json

    初始化過程中,系統產生了5個shares,並指定解鎖次數threshold為3。

  3. 在實際生產環境中,在Pod內通過POST的vault-0.vault.vault.svc:8200/sys/init 進行OpenAPI調用。

    此處使用了StatefulSet(sts)的DNS解析習慣,即{$podName}.{$stsName}.{$Namespace}.svc。關於初始化Vault對應的OpenAPI,請參見Vault Start Initialization

    展開查看產生的檔案cluster-keys.json的內容(OpenAPI返回的data類似)

    {
      "unseal_keys_b64": [
        "Zu6EdLIFn+2****",
        "yvBur6WEphY****",
        "HR4hNkQN++h****",
        "85jAjs8xAj4****",
        "DgYQhjo6l14****"
      ],
      "unseal_keys_hex": [
        "66ee8474b****",
        "caf06eafa****",
        "1d1e21364****",
        "f398c08ec****",
        "0e0610863****"
      ],
      "unseal_shares": 5,
      "unseal_threshold": 3,
      "recovery_keys_b64": [],
      "recovery_keys_hex": [],
      "recovery_keys_shares": 0,
      "recovery_keys_threshold": 0,
      "root_token": "hvs.5aiXKN****"
    }

    將以上產生的檔案cluster-keys.json中unseal_keys_b64的內容匯出,進行下一步解鎖操作。

  4. 執行以下命令,解鎖Vault。關於解鎖Vault對應的OpenAPI,請參見Vault Unseal

    由於以上unseal_threshold設定為3,所以此處需選取3個Unseal key進行解鎖,分別執行1次,共需執行3次。

    k exec -nvault vault-0 -- vault operator unseal Zu6EdLIFn+2****
  5. 執行以下命令,查看vault-0的狀態。

    k exec -it vault-0 -n vault -- vault status
    Key                     Value
    ---                     -----
    Seal Type               shamir
    Initialized             true
    Sealed                  false
    Total Shares            5
    Threshold               3
    Version                 1.13.1
    Build Date              2023-03-23T12:51:35Z
    Storage Type            raft
    Cluster Name            vault-cluster-504959a1
    Cluster ID              d99594a5-75de-53fa-59dd-19ed024b****
    HA Enabled              true
    HA Cluster              https://vault-0.vault-internal:8201
    HA Mode                 active
    Active Since            2023-05-06T10:30:38.237415781Z
    Raft Committed Index    36
    Raft Applied Index      36
    
    k get po -n vault
    NAME                                    READY   STATUS    RESTARTS   AGE
    vault-0                                 1/1     Running   0          46m
    vault-1                                 0/1     Running   0          46m
    vault-2                                 0/1     Running   0          46m
    vault-agent-injector-59fdd7cdf8-prwv7   1/1     Running   0          46m

    預期輸出表明,vault-0已初始化完成。

  6. (可選)如需查看Raft節點,可通過root Token登入節點進行查看。

    1. 執行以下命令,登入vault-0節點。

      此處root_toke值為hvs.5aiXKN****,其值可通過步驟3產生的檔案cluster-keys.json擷取。

      k exec vault-0 -n vault -- vault login hvs.5aiXKN****
    2. 執行以下命令,查看Raft節點。

      k exec -nvault vault-0 -- vault operator raft list-peers

      預期輸出:

      Node                                    Address                        State     Voter
      ----                                    -------                        -----     -----
      10285056-839a-f306-a301-5024934a794f    vault-0.vault-internal:8201    leader    true

步驟三:添加其他Vault節點

  1. 執行以下命令,添加Vault節點。關於添加Vault節點的OpenAPI,請參見Raft

    k exec -nvault vault-1  -- vault operator raft join http://vault-0.vault-internal:8200
    Key       Value
    ---       -----
    Joined    true
    k exec -nvault vault-2  -- vault operator raft join http://vault-0.vault-internal:8200
    Key       Value
    ---       -----
    Joined    true
  2. 分別執行以下命令,解鎖添加的Vault節點。

    每個節點至少要用不同的Unseal key執行3次,共需執行6次。

    k exec -nvault vault-1 -- vault operator unseal Zu6EdLIF****
    k exec -nvault vault-2 -- vault operator unseal Zu6EdLIF****
    ...
    k exec -nvault vault-1 -- vault operator unseal DgYQhjo6****
    k exec -nvault vault-2 -- vault operator unseal DgYQhjo6****
  3. 執行以下命令,查看節點添加結果。

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

    預期輸出:

    Node                                    Address                        State       Voter
    ----                                    -------                        -----       -----
    10285056-839a-f306-a301-5024934a794f    vault-0.vault-internal:8201    leader      true
    71ffd98c-d6d4-a7b3-994c-9ce87f464486    vault-1.vault-internal:8201    follower    true
    1e9f37dc-b55b-fc46-8ca8-595428ad1d81    vault-2.vault-internal:8201    follower    true
    
    k get po -n vault
    NAME                                    READY   STATUS    RESTARTS   AGE
    vault-0                                 1/1     Running   0          66m
    vault-1                                 1/1     Running   0          66m
    vault-2                                 1/1     Running   0          66m
    vault-agent-injector-59fdd7cdf8-prwv7   1/1     Running   0          66m

    預期輸出表明,vault-1vault-2節點已添加成功。

使用樣本

樣本一:通過Vault管理Kubernetes叢集的ServiceAccount Token

您可以通過Valut擷取rolebinding clusterrolebinding對應的Token。啟用此特性後,在Kubernetes叢集上binding時,將不會產生對應的Secret。此方式通過Vault擷取訪問APIServer的Bear Token,可避免攻擊者通過Kubernetes叢集直接擷取SA的訪問憑證。

  1. 使用以下YAML內容,分別建立ClusterRole.yaml和ClusterRoleBinding.yaml檔案。

    展開查看ClusterRole.yaml檔案

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRole
    metadata:
      name: k8s-minimal-secrets-abilities
    rules:
    - apiGroups: [""]
      resources: ["serviceaccounts/token"]
      verbs: ["create"]

    展開查看ClusterRoleBinding.yaml檔案

    apiVersion: rbac.authorization.k8s.io/v1
    kind: ClusterRoleBinding
    metadata:
      name: vault-token-creator-binding
    roleRef:
      apiGroup: rbac.authorization.k8s.io
      kind: ClusterRole
      name: k8s-minimal-secrets-abilities
    subjects:
    - kind: ServiceAccount
      name: vault
      namespace: vault
  2. 執行以下命令,為Vault的SA綁定ClusterRole,使其能建立SA的Token。

    k apply -f ClusterRole.yaml
    k apply -f ClusterRoleBinding.yaml
  3. 執行以下命令,開啟Vault的Kubernetes的Secret特性。

    k exec -nvault vault-0 -- vault secrets enable kubernetes
  4. 驗證使用效果。

    1. 執行以下命令,建立名為test的命名空間。

      k create ns test
    2. 使用以下YAML內容,建立test.yaml檔案。

      展開查看test.yaml檔案

      apiVersion: v1
      kind: ServiceAccount
      metadata:
        name: test-service-account-with-generated-token
        namespace: test
      ---
      apiVersion: rbac.authorization.k8s.io/v1
      kind: Role
      metadata:
        name: test-role-list-pods
        namespace: test
      rules:
      - apiGroups: [""]
        resources: ["pods"]
        verbs: ["list"]
      ---
      apiVersion: rbac.authorization.k8s.io/v1
      kind: RoleBinding
      metadata:
        name: test-role-abilities
        namespace: test
      roleRef:
        apiGroup: rbac.authorization.k8s.io
        kind: Role
        name: test-role-list-pods
      subjects:
      - kind: ServiceAccount
        name: test-service-account-with-generated-token
        namespace: test
    3. 執行以下命令,部署產生測試的SA Role RoleBinding。

      k apply -f test.yaml
    4. 執行以下命令,擷取Token資訊。關於OpenAPI的更多資訊,請參見Kubernetes API

      k exec -nvault vault-0 -- vault write -f kubernetes/config
      k exec -nvault vault-0 -- vault write kubernetes/roles/my-role allowed_kubernetes_namespaces="*" service_account_name="test-service-account-with-generated-token" token_default_ttl="10m"
      k exec -nvault vault-0 -- vault write kubernetes/creds/my-role kubernetes_namespace=test

      最後一個命令輸出的一個JWT Token,可以用於請求訪問APIserver。

      JWT Token即為如下代碼中的service_account_token欄位。

      Key                          Value
      ---                          -----
      lease_id                     kubernetes/creds/my-role/XPDLbuXJ0Bt4fF****
      lease_duration               10m
      lease_renewable              false
      service_account_name         test-service-account-with-generated-token
      service_account_namespace    test
      service_account_token        eyJhbGciOiJSUzI1NiIsImtp****
    5. 執行以下命令,訪問APIServer。

      curl -sk https://XX.XX.XX.XX:6443/api/v1/namespaces/test/pods --header "Authorization: Bearer eyJhbGciOiJSUzI1NiIsImtp****"
        "kind": "PodList",
        "apiVersion": "v1",
        "metadata": {
          "resourceVersion": "2861371"
        },
        "items": []
      }

      以上Token有效期間是10 min,如果Token到期,需要調用Write操作重新擷取Token。

      k exec -nvault vault-0 -- vault write kubernetes/creds/my-role kubernetes_namespace=test

樣本二:如何在應用Pod中動態擷取RAM憑證

您可以通過Vault儲存訪問阿里雲RAM使用者的AK和SK資訊。應用通過和Vault互動動態擷取相關憑證。關於更多OpenAPI資訊,請參見AliCloud Secrets Engine

  1. 執行以下命令,為Vault開啟Alicloud的Secret特性。

    k exec -nvault vault-0 -- vault secrets enable  alicloud
  2. 使用阿里雲帳號登入RAM控制台

  3. 使用以下權限原則內容,建立自訂權限原則ExampleRAMPolicyforVault。具體操作,請參見建立自訂權限原則

    該權限原則允許在RAM使用者使用任何類型的角色建立、刪除憑證或策略,並為使用者指派策略,允許取消使用者的某個策略,建立和刪除使用者、通過角色扮演來訪問資源等。

    展開查看自訂權限原則ExampleRAMPolicyforVault

    {
      "Statement": [
        {
          "Action": [
            "ram:CreateAccessKey",
            "ram:DeleteAccessKey",
            "ram:CreatePolicy",
            "ram:DeletePolicy",
            "ram:AttachPolicyToUser",
            "ram:DetachPolicyFromUser",
            "ram:CreateUser",
            "ram:DeleteUser",
            "sts:AssumeRole"
          ],
          "Effect": "Allow",
          "Resource": "*"
        }
      ],
      "Version": "1"
    }
  4. 建立RAM使用者hashicorp-vault。具體操作,請參見建立RAM使用者

  5. 為RAM使用者hashicorp-vault授予自訂權限原則ExampleRAMPolicyforVault。具體操作,請參見為RAM使用者授權

  6. 為RAM使用者hashicorp-vault建立AccessKey。具體操作,請參見建立AccessKey

    記錄此處的AK和SK資訊。例如,此處的AccessKey為ak1,SecretKey為sk1。

  7. 執行以下命令,將已擷取的AK和SK資訊寫入Vault。

    k exec -nvault vault-0 -- vault write alicloud/config access_key=ak1 secret_key=sk
    Success! Data written to: alicloud/config

    AK和SK會儲存在Vault每個節點的/vault/data/vault.db檔案中,同時此檔案會持久化到PV中,所以節點重啟後資訊不會丟失。

  8. 將Remote和Inline策略定義寫入Vault。

    • 執行以下命令,將Remote策略寫入Vault。

      Remote模式指寫入RAM中已存在的權限原則類型和名稱。此處寫入一個自訂權限原則ExampleRAMPolicyforVault,兩個系統策略AliyunOSSReadOnlyAccess和AliyunRDSReadOnlyAccess。

      k exec -nvault vault-0 -- vault write alicloud/role/policy-based \
          remote_policies='name:ExampleRAMPolicyforVault,type:Custom' \
          remote_policies='name:AliyunOSSReadOnlyAccess,type:System' \
          remote_policies='name:AliyunRDSReadOnlyAccess,type:System'
      Success! Data written to: alicloud/role/policy-based
    • 執行以下命令,將Inline策略寫入Vault。

      Inline模式指直接在API請求中寫入原則範本。此處可將已產生的自訂權限原則ExampleRAMPolicyforVault的配置寫入Vault。

      k exec -nvault vault-0 -- vault write alicloud/role/policy-based \
          inline_policies=-<<EOF
      [
      {
        "Statement": [
          {
            "Action": [
              "ram:CreateAccessKey",
              "ram:DeleteAccessKey",
              "ram:CreatePolicy",
              "ram:DeletePolicy",
              "ram:AttachPolicyToUser",
              "ram:DetachPolicyFromUser",
              "ram:CreateUser",
              "ram:DeleteUser",
              "sts:AssumeRole"
            ],
            "Effect": "Allow",
            "Resource": "*"
          }
        ],
        "Version": "1"
      }
      ]
      EOF
  9. 建立RAM角色vaultTestRole並為該RAM角色授予自訂權限原則ExampleRAMPolicyforVault。具體操作,請參見建立可信實體為阿里雲帳號的RAM角色為RAM角色授權

    為角色授權完成後,會產生一條ARN記錄,格式如下,其中15261****為RAM使用者的ID。

    vaultTestRole@role.15261****.onaliyunservice.com
  10. 執行以下命令,將對應的ARN資訊寫入Vault,即將綁定關係寫入Vault,實現Vault對RAM角色vaultTestRole的扮演。

    k exec -nvault vault-0 -- vault write alicloud/role/role-based \
          role_arn='acs:ram::15261****:role/vaultTestRole'

驗證使用效果

  1. 執行以下命令,擷取基於策略的訪問憑證。

    k exec -nvault vault-0 -- vault read alicloud/creds/policy-based
    Key                Value
    ---                -----
    lease_id           alicloud/creds/policy-based/TG1isE6uga94sRv60NK7****
    lease_duration     768h
    lease_renewable    true
    access_key         ak1
    secret_key         sk1
  2. 執行以下命令,擷取基於角色的訪問憑證(STS Token)。

    k exec -nvault vault-0 -- vault read alicloud/creds/role-based
    Key                Value
    ---                -----
    lease_id           alicloud/creds/role-based/uJxVwNSnqzcni75kkf****
    lease_duration     59m59s
    lease_renewable    false
    access_key         STS.NUM2e1BrC****
    expiration         2023-05-09T04:16:17Z
    secret_key         3VmmRy****
    security_token     CAISiwJ1q****

角色許可權說明

如果一個角色使用不同的權限原則,就需要為角色指派不同的許可權點。

使用的權限原則

對應分配的許可權點

inline_policies

remote_policies

role_arn

樣本三:如何在應用Pod中通過RAM認證訪問Vault API

在K8s的應用Pod中,可通過寫代碼訪問Vault的服務,使用此方式對接阿里雲的身份認證、以及身份對應角色資訊的查詢。此應用代碼攜帶身份認證的資訊,通過訪問Vault的OpenAPI(Auth、Alicloud、Login)擷取該使用者身份對應的角色資訊,以及訪問Vault的Token。

使用此Token可對身份對應的角色進行操作,例如,查看角色詳情、角色列表、建立角色和刪除角色。關於更多操作,請參見AliCloud Auth Method (API)

  1. 執行以下命令,為Vault開啟Alicloud的Auth。更多OpenAPI資訊,請參見Enable Auth Method

    k exec -nvault vault-0 -- vault auth enable alicloud
  2. 執行以下命令,將對應的ARN寫入Vault。

    k exec -nvault vault-0 -- vault write auth/alicloud/role/vaultTestRole arn='acs:ram::15261****:role/vaulttestrole'
  3. 擷取調用GetCallerIdentity介面使用的URL和Header。具體操作,請參見vault-plugin

    應用代碼需先調用GetCallerIdentity介面,然後擷取調用該介面使用的URL和Header。關於GetCallerIdentity介面調用,請參見GetCallerIdentity OpenAPI。進入調用頁面,在左側搜尋方塊輸入GetCallerIdentity,在中間地區選取項目自身業務所在Region,然後在右側選擇SDK樣本,即可看到對應的代碼。

  4. 調用Login。其中IDENTITY_REQUEST_URL_BASE_64為URL的Base64編碼,IDENTITY_REQUEST_HEADERS_BASE_64為Header的Base64編碼。更多資訊,請參見Login OpenAPI

  5. 應用程式通過上一步Login調用返回的client_token欄位,調用Vault的AuthAlicloud等OpenAPI,實現對應角色的訪問。

常見問題

Vault是第三方的維護的開源專案,當您遇到的問題非阿里雲或ACK官方提供支援的產品或組件,請前往Vault Communit開源專案社區諮詢處理。

如何修複Vault叢集異常?

如果Vault Pod重啟,Pod會進入0/1 Running狀態。您可以參考以下步驟對Vault叢集異常問題進行修複。

  1. 執行以下命令,查看Pod的狀態。

    k exec -nvault vault-0 -- vault status

    預期輸出:

    Key                Value
    ---                -----
    Seal Type          shamir
    Initialized        true
    Sealed             true
    Total Shares       5
    Threshold          3
    Unseal Progress    0/3
    Unseal Nonce       n/a
    Version            1.13.1
    Build Date         2023-03-23T12:51:35Z
    Storage Type       raft
    HA Enabled         true
    command terminated with exit code 2

    預期輸出表明,Pod又處於sealed狀態。需要重新進行解鎖操作。

  2. 執行以下命令,解鎖Vault節點。

    此處仍需選擇3個Unseal Key分別執行1次,共需執行3次。

    k exec -nvault vault-0 -- vault operator unseal Zu6EdL****
  3. 執行以下命令,通過root Token登入節點,查看Raft列表。

    k exec -nvault vault-0 -- vault operator raft list-peers

    預期輸出:

    Node                                    Address                        State       Voter
    ----                                    -------                        -----       -----
    10285056-839a-f306-a301-5024934a794f    vault-0.vault-internal:8201    follower    true
    71ffd98c-d6d4-a7b3-994c-9ce87f464486    vault-1.vault-internal:8201    leader      true
    1e9f37dc-b55b-fc46-8ca8-595428ad1d81    vault-2.vault-internal:8201    follower    true

    預期輸出表明,vault-0狀態變為follower正常狀態。

如何在ACK叢集中通過Service調用Vault?

Vault安裝完成後,將產生多個Service。您可以通過以下命令,查看具體Service資訊。

k get svc -n vault

預期輸出:

NAME                       TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)             AGE
vault                      ClusterIP   172.16.193.219   <none>        8200/TCP,8201/TCP   47h
vault-active               ClusterIP   172.16.177.54    <none>        8200/TCP,8201/TCP   47h
vault-internal             ClusterIP   None             <none>        8200/TCP,8201/TCP   47h
vault-standby              ClusterIP   172.16.29.54     <none>        8200/TCP,8201/TCP   47h
  • vaultvault-internal為整個Vault叢集節點的負載平衡,其中,vault-internal為Headless的SVC。

  • vault-active為Raft選出的leader節點。

  • vault-standby為Raft中的follower節點。