使用Vault作為KMS服務(wù)
Vault是一個基于身份的密鑰管理和數(shù)據(jù)加密系統(tǒng),提供對Token、密碼、證書、API Key等常見敏感憑據(jù)的安全存儲和控制,可有效解決應(yīng)用系統(tǒng)中對敏感信息的硬編碼問題。本文介紹如何在ACK集群中部署和使用Vault。
前提條件
已創(chuàng)建ACK集群,且集群為1.22及以上版本。具體操作,請參見創(chuàng)建ACK托管集群、手動升級集群。
已安裝Helm,且Helm為v3.6及以上版本。更多信息,請參見Helm Release。
安裝初始化Vault
步驟一:安裝Vault
任選以下方式獲取Vault的安裝包。本文示例中Helm的Chart版本為vault-0.24.1,Vault版本為1.3.1。
登錄Git倉庫vault-helm獲取。
通過遠程倉庫獲取。執(zhí)行以下命令,添加并更新倉庫。
helm repo add hashicorp https://helm.releases.hashicorp.com helm repo update
執(zhí)行以下命令,配置別名(Alias)簡化操作命令。
以下腳本代碼以ACK集群的KubeConfig信息放置在
$HOME/Downloads/kubeconfig
文件中為例說明,使用時KubeConfig位置信息請根據(jù)實際位置替換。# Helm客戶端。 alias h="helm --kubeconfig $HOME/Downloads/kubeconfig" # kubectl客戶端。 alias k="kubectl --kubeconfig $HOME/Downloads/kubeconfig"
安裝Vault。
生產(chǎn)環(huán)境建議安裝Raft版本的Vault,請勿使用Standard alone版本。
執(zhí)行以下命令,創(chuàng)建名為vault的命名空間。
k create ns vault
將Vault安裝在
vault
的命名空間中,后續(xù)和K8s Namespace相關(guān)的值都為vault
。執(zhí)行以下命令,在名為vault的命名空間中安裝Vault。
以下StorageClass(SC)使用ACK支持的SC,可通過
k get sc
查詢。存儲的Size不小于20 GiB。部署完成后,將生成三個按量付費的ESSD云盤,作為Vault的Pod掛載使用的PV。關(guān)于云盤的計費信息,請參見計費。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'
執(zhí)行以下命令,查看Vault的Pod狀態(tài)。
k get po -n vault
預(yù)期輸出:
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
執(zhí)行以下命令,查看Vault第一次啟動后的狀態(tài)。
k exec -nvault vault-0 -- vault status
預(yù)期輸出:
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
當
Initialized
為false
,Sealed
為true
時,表明Vault未進行初始化,且沒有解封(Unseal)。您需要進行后續(xù)的初始化和解封操作。執(zhí)行以下命令,初始化Vault。
通過容器內(nèi)的Vault二進制,生成Key。
k exec vault-0 -nvault -- vault operator init -key-shares=5 -key-threshold=3 -format=json > cluster-keys.json
初始化過程中,系統(tǒng)生成了5個
shares
,并指定解封次數(shù)threshold
為3。在實際生產(chǎn)環(huán)境中,在Pod內(nèi)通過POST的
vault-0.vault.vault.svc:8200/sys/init
進行OpenAPI調(diào)用。此處使用了StatefulSet(sts)的DNS解析習慣,即
{$podName}.{$stsName}.{$Namespace}.svc
。關(guān)于初始化Vault對應(yīng)的OpenAPI,請參見Vault Start Initialization。{ "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
的內(nèi)容導(dǎo)出,進行下一步解封操作。執(zhí)行以下命令,解封Vault。關(guān)于解封Vault對應(yīng)的OpenAPI,請參見Vault Unseal。
由于以上
unseal_threshold
設(shè)置為3,所以此處需選取3個Unseal key進行解封,分別執(zhí)行1次,共需執(zhí)行3次。k exec -nvault vault-0 -- vault operator unseal Zu6EdLIFn+2****
執(zhí)行以下命令,查看vault-0的狀態(tài)。
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
預(yù)期輸出表明,
vault-0
已初始化完成。(可選)如需查看Raft節(jié)點,可通過root Token登錄節(jié)點進行查看。
執(zhí)行以下命令,登錄vault-0節(jié)點。
此處root_toke值為
hvs.5aiXKN****
,其值可通過步驟3生成的文件cluster-keys.json獲取。k exec vault-0 -n vault -- vault login hvs.5aiXKN****
執(zhí)行以下命令,查看Raft節(jié)點。
k exec -nvault vault-0 -- vault operator raft list-peers
預(yù)期輸出:
Node Address State Voter ---- ------- ----- ----- 10285056-839a-f306-a301-5024934a794f vault-0.vault-internal:8201 leader true
步驟三:添加其他Vault節(jié)點
執(zhí)行以下命令,添加Vault節(jié)點。關(guān)于添加Vault節(jié)點的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
分別執(zhí)行以下命令,解封添加的Vault節(jié)點。
每個節(jié)點至少要用不同的Unseal key執(zhí)行3次,共需執(zhí)行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****
執(zhí)行以下命令,查看節(jié)點添加結(jié)果。
k exec -n vault vault-0 -- vault operator raft list-peers
預(yù)期輸出:
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
預(yù)期輸出表明,
vault-1
和vault-2
節(jié)點已添加成功。
使用示例
示例一:通過Vault管理Kubernetes集群的ServiceAccount Token
您可以通過Valut獲取rolebinding clusterrolebinding
對應(yīng)的Token。啟用此特性后,在Kubernetes集群上binding時,將不會生成對應(yīng)的Secret。此方式通過Vault獲取訪問APIServer的Bear Token,可避免攻擊者通過Kubernetes集群直接獲取SA的訪問憑證。
使用以下YAML內(nèi)容,分別創(chuàng)建ClusterRole.yaml和ClusterRoleBinding.yaml文件。
apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: k8s-minimal-secrets-abilities rules: - apiGroups: [""] resources: ["serviceaccounts/token"] verbs: ["create"]
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
執(zhí)行以下命令,為Vault的SA綁定ClusterRole,使其能創(chuàng)建SA的Token。
k apply -f ClusterRole.yaml k apply -f ClusterRoleBinding.yaml
執(zhí)行以下命令,開啟Vault的Kubernetes的Secret特性。
k exec -nvault vault-0 -- vault secrets enable kubernetes
驗證使用效果。
執(zhí)行以下命令,創(chuàng)建名為test的命名空間。
k create ns test
使用以下YAML內(nèi)容,創(chuàng)建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
執(zhí)行以下命令,部署生成測試的SA Role RoleBinding。
k apply -f test.yaml
執(zhí)行以下命令,獲取Token信息。關(guān)于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****
執(zhí)行以下命令,訪問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過期,需要調(diào)用Write操作重新獲取Token。
k exec -nvault vault-0 -- vault write kubernetes/creds/my-role kubernetes_namespace=test
示例二:如何在應(yīng)用Pod中動態(tài)獲取RAM憑證
您可以通過Vault存儲訪問阿里云RAM用戶的AK和SK信息。應(yīng)用通過和Vault交互動態(tài)獲取相關(guān)憑證。關(guān)于更多OpenAPI信息,請參見AliCloud Secrets Engine。
執(zhí)行以下命令,為Vault開啟
Alicloud
的Secret特性。k exec -nvault vault-0 -- vault secrets enable alicloud
使用阿里云賬號登錄RAM控制臺。
使用以下權(quán)限策略內(nèi)容,創(chuàng)建自定義權(quán)限策略ExampleRAMPolicyforVault。具體操作,請參見創(chuàng)建自定義權(quán)限策略。
該權(quán)限策略允許在RAM用戶使用任何類型的角色創(chuàng)建、刪除憑證或策略,并為用戶分配策略,允許取消用戶的某個策略,創(chuàng)建和刪除用戶、通過角色扮演來訪問資源等。
{ "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" }
創(chuàng)建RAM用戶hashicorp-vault。具體操作,請參見創(chuàng)建RAM用戶。
為RAM用戶hashicorp-vault授予自定義權(quán)限策略ExampleRAMPolicyforVault。具體操作,請參見為RAM用戶授權(quán)。
為RAM用戶hashicorp-vault創(chuàng)建AccessKey。具體操作,請參見創(chuàng)建AccessKey。
記錄此處的AK和SK信息。例如,此處的AccessKey為ak1,SecretKey為sk1。
執(zhí)行以下命令,將已獲取的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每個節(jié)點的
/vault/data/vault.db
文件中,同時此文件會持久化到PV中,所以節(jié)點重啟后信息不會丟失。將Remote和Inline策略定義寫入Vault。
執(zhí)行以下命令,將Remote策略寫入Vault。
Remote模式指寫入RAM中已存在的權(quán)限策略類型和名稱。此處寫入一個自定義權(quán)限策略ExampleRAMPolicyforVault,兩個系統(tǒng)策略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
執(zhí)行以下命令,將Inline策略寫入Vault。
Inline模式指直接在API請求中寫入策略模板。此處可將已生成的自定義權(quán)限策略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
創(chuàng)建RAM角色vaultTestRole并為該RAM角色授予自定義權(quán)限策略ExampleRAMPolicyforVault。具體操作,請參見創(chuàng)建可信實體為阿里云賬號的RAM角色和為RAM角色授權(quán)。
為角色授權(quán)完成后,會生成一條ARN記錄,格式如下,其中15261****為RAM用戶的ID。
vaultTestRole@role.15261****.onaliyunservice.com
執(zhí)行以下命令,將對應(yīng)的ARN信息寫入Vault,即將綁定關(guān)系寫入Vault,實現(xiàn)Vault對RAM角色vaultTestRole的扮演。
k exec -nvault vault-0 -- vault write alicloud/role/role-based \ role_arn='acs:ram::15261****:role/vaultTestRole'
驗證使用效果
執(zhí)行以下命令,獲取基于策略的訪問憑證。
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
執(zhí)行以下命令,獲取基于角色的訪問憑證(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****
角色權(quán)限說明
如果一個角色使用不同的權(quán)限策略,就需要為角色分配不同的權(quán)限點。
使用的權(quán)限策略 | 對應(yīng)分配的權(quán)限點 |
inline_policies | |
remote_policies |
|
role_arn |
示例三:如何在應(yīng)用Pod中通過RAM認證訪問Vault API
在K8s的應(yīng)用Pod中,可通過寫代碼訪問Vault的服務(wù),使用此方式對接阿里云的身份認證、以及身份對應(yīng)角色信息的查詢。此應(yīng)用代碼攜帶身份認證的信息,通過訪問Vault的OpenAPI(Auth、Alicloud、Login)獲取該用戶身份對應(yīng)的角色信息,以及訪問Vault的Token。
使用此Token可對身份對應(yīng)的角色進行操作,例如,查看角色詳情、角色列表、創(chuàng)建角色和刪除角色。關(guān)于更多操作,請參見AliCloud Auth Method (API)。
執(zhí)行以下命令,為Vault開啟
Alicloud
的Auth。更多OpenAPI信息,請參見Enable Auth Method。k exec -nvault vault-0 -- vault auth enable alicloud
執(zhí)行以下命令,將對應(yīng)的ARN寫入Vault。
k exec -nvault vault-0 -- vault write auth/alicloud/role/vaultTestRole arn='acs:ram::15261****:role/vaulttestrole'
獲取調(diào)用GetCallerIdentity接口使用的URL和Header。具體操作,請參見vault-plugin。
應(yīng)用代碼需先調(diào)用GetCallerIdentity接口,然后獲取調(diào)用該接口使用的URL和Header。關(guān)于GetCallerIdentity接口調(diào)用,請參見GetCallerIdentity OpenAPI。進入調(diào)用頁面,在左側(cè)搜索框輸入GetCallerIdentity,在中間區(qū)域選擇自身業(yè)務(wù)所在Region,然后在右側(cè)選擇SDK示例,即可看到對應(yīng)的代碼。
調(diào)用Login。其中
IDENTITY_REQUEST_URL_BASE_64
為URL的Base64編碼,IDENTITY_REQUEST_HEADERS_BASE_64為Header的Base64編碼。更多信息,請參見Login OpenAPI。應(yīng)用程序通過上一步Login調(diào)用返回的
client_token
字段,調(diào)用Vault的Auth
、Alicloud
等OpenAPI,實現(xiàn)對應(yīng)角色的訪問。
常見問題
Vault是第三方的維護的開源項目,當您遇到的問題非阿里云或ACK官方提供支持的產(chǎn)品或組件,請前往Vault Communit開源項目社區(qū)咨詢處理。
如何修復(fù)Vault集群異常?
如果Vault Pod重啟,Pod會進入0/1 Running狀態(tài)。您可以參考以下步驟對Vault集群異常問題進行修復(fù)。
執(zhí)行以下命令,查看Pod的狀態(tài)。
k exec -nvault vault-0 -- vault status
預(yù)期輸出:
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
預(yù)期輸出表明,Pod又處于
sealed
狀態(tài)。需要重新進行解封操作。執(zhí)行以下命令,解封Vault節(jié)點。
此處仍需選擇3個Unseal Key分別執(zhí)行1次,共需執(zhí)行3次。
k exec -nvault vault-0 -- vault operator unseal Zu6EdL****
執(zhí)行以下命令,通過root Token登錄節(jié)點,查看Raft列表。
k exec -nvault vault-0 -- vault operator raft list-peers
預(yù)期輸出:
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
預(yù)期輸出表明,
vault-0
狀態(tài)變?yōu)?code data-tag="code" code-type="xCode" class="code">follower正常狀態(tài)。
如何在ACK集群中通過Service調(diào)用Vault?
Vault安裝完成后,將生成多個Service。您可以通過以下命令,查看具體Service信息。
k get svc -n vault
預(yù)期輸出:
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
vault
和vault-internal
為整個Vault集群節(jié)點的負載均衡,其中,vault-internal
為Headless的SVC。vault-active
為Raft選出的leader節(jié)點。vault-standby
為Raft中的follower節(jié)點。