Vault是一个基于身份的密钥管理和数据加密系统,提供对Token、密码、证书、API Key等常见敏感凭据的安全存储和控制,可有效解决应用系统中对敏感信息的硬编码问题。本文介绍如何在ACK集群中部署和使用Vault。
前提条件
已安装Helm,且Helm为v3.6及以上版本。更多信息,请参见Helm Release。
安装初始化Vault
步骤一:安装Vault
任选以下方式获取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
执行以下命令,配置别名(Alias)简化操作命令。
以下脚本代码以ACK集群的KubeConfig信息放置在
$HOME/Downloads/kubeconfig
文件中为例说明,使用时KubeConfig位置信息请根据实际位置替换。# Helm客户端。 alias h="helm --kubeconfig $HOME/Downloads/kubeconfig" # kubectl客户端。 alias k="kubectl --kubeconfig $HOME/Downloads/kubeconfig"
安装Vault。
生产环境建议安装Raft版本的Vault,请勿使用Standard alone版本。
执行以下命令,创建名为vault的命名空间。
k create ns vault
将Vault安装在
vault
的命名空间中,后续和K8s Namespace相关的值都为vault
。执行以下命令,在名为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'
执行以下命令,查看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
执行以下命令,查看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
当
Initialized
为false
,Sealed
为true
时,表明Vault未进行初始化,且没有解封(Unseal)。您需要进行后续的初始化和解封操作。执行以下命令,初始化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。在实际生产环境中,在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中
unseal_keys_b64
的内容导出,进行下一步解封操作。执行以下命令,解封Vault。关于解封Vault对应的OpenAPI,请参见Vault Unseal。
由于以上
unseal_threshold
设置为3,所以此处需选取3个Unseal key进行解封,分别执行1次,共需执行3次。k exec -nvault vault-0 -- vault operator unseal Zu6EdLIFn+2****
执行以下命令,查看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
已初始化完成。(可选)如需查看Raft节点,可通过root Token登录节点进行查看。
执行以下命令,登录vault-0节点。
此处root_toke值为
hvs.5aiXKN****
,其值可通过步骤3生成的文件cluster-keys.json获取。k exec vault-0 -n vault -- vault login hvs.5aiXKN****
执行以下命令,查看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节点
执行以下命令,添加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
分别执行以下命令,解封添加的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****
执行以下命令,查看节点添加结果。
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-1
和vault-2
节点已添加成功。
使用示例
示例一:通过Vault管理Kubernetes集群的ServiceAccount Token
您可以通过Valut获取rolebinding clusterrolebinding
对应的Token。启用此特性后,在Kubernetes集群上binding时,将不会生成对应的Secret。此方式通过Vault获取访问APIServer的Bear Token,可避免攻击者通过Kubernetes集群直接获取SA的访问凭证。
使用以下YAML内容,分别创建ClusterRole.yaml和ClusterRoleBinding.yaml文件。
执行以下命令,为Vault的SA绑定ClusterRole,使其能创建SA的Token。
k apply -f ClusterRole.yaml k apply -f ClusterRoleBinding.yaml
执行以下命令,开启Vault的Kubernetes的Secret特性。
k exec -nvault vault-0 -- vault secrets enable kubernetes
验证使用效果。
执行以下命令,创建名为test的命名空间。
k create ns test
使用以下YAML内容,创建test.yaml文件。
执行以下命令,部署生成测试的SA Role RoleBinding。
k apply -f test.yaml
执行以下命令,获取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****
执行以下命令,访问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。
执行以下命令,为Vault开启
Alicloud
的Secret特性。k exec -nvault vault-0 -- vault secrets enable alicloud
使用阿里云账号登录RAM控制台。
使用以下权限策略内容,创建自定义权限策略ExampleRAMPolicyforVault。具体操作,请参见创建自定义权限策略。
该权限策略允许在RAM用户使用任何类型的角色创建、删除凭证或策略,并为用户分配策略,允许取消用户的某个策略,创建和删除用户、通过角色扮演来访问资源等。
创建RAM用户hashicorp-vault。具体操作,请参见创建RAM用户。
为RAM用户hashicorp-vault授予自定义权限策略ExampleRAMPolicyforVault。具体操作,请参见为RAM用户授权。
为RAM用户hashicorp-vault创建AccessKey。具体操作,请参见创建AccessKey。
记录此处的AK和SK信息。例如,此处的AccessKey为ak1,SecretKey为sk1。
执行以下命令,将已获取的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中,所以节点重启后信息不会丢失。将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
创建RAM角色vaultTestRole并为该RAM角色授予自定义权限策略ExampleRAMPolicyforVault。具体操作,请参见创建可信实体为阿里云账号的RAM角色和为RAM角色授权。
为角色授权完成后,会生成一条ARN记录,格式如下,其中15261****为RAM用户的ID。
vaultTestRole@role.15261****.onaliyunservice.com
执行以下命令,将对应的ARN信息写入Vault,即将绑定关系写入Vault,实现Vault对RAM角色vaultTestRole的扮演。
k exec -nvault vault-0 -- vault write alicloud/role/role-based \ role_arn='acs:ram::15261****:role/vaultTestRole'
验证使用效果
执行以下命令,获取基于策略的访问凭证。
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
执行以下命令,获取基于角色的访问凭证(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)。
执行以下命令,为Vault开启
Alicloud
的Auth。更多OpenAPI信息,请参见Enable Auth Method。k exec -nvault vault-0 -- vault auth enable alicloud
执行以下命令,将对应的ARN写入Vault。
k exec -nvault vault-0 -- vault write auth/alicloud/role/vaultTestRole arn='acs:ram::15261****:role/vaulttestrole'
获取调用GetCallerIdentity接口使用的URL和Header。具体操作,请参见vault-plugin。
应用代码需先调用GetCallerIdentity接口,然后获取调用该接口使用的URL和Header。关于GetCallerIdentity接口调用,请参见GetCallerIdentity OpenAPI。进入调用页面,在左侧搜索框输入GetCallerIdentity,在中间区域选择自身业务所在Region,然后在右侧选择SDK示例,即可看到对应的代码。
调用Login。其中
IDENTITY_REQUEST_URL_BASE_64
为URL的Base64编码,IDENTITY_REQUEST_HEADERS_BASE_64为Header的Base64编码。更多信息,请参见Login OpenAPI。应用程序通过上一步Login调用返回的
client_token
字段,调用Vault的Auth
、Alicloud
等OpenAPI,实现对应角色的访问。
常见问题
Vault是第三方的维护的开源项目,当您遇到的问题非阿里云或ACK官方提供支持的产品或组件,请前往Vault Communit开源项目社区咨询处理。
如何修复Vault集群异常?
如果Vault Pod重启,Pod会进入0/1 Running状态。您可以参考以下步骤对Vault集群异常问题进行修复。
执行以下命令,查看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
状态。需要重新进行解封操作。执行以下命令,解封Vault节点。
此处仍需选择3个Unseal Key分别执行1次,共需执行3次。
k exec -nvault vault-0 -- vault operator unseal Zu6EdL****
执行以下命令,通过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
vault
和vault-internal
为整个Vault集群节点的负载均衡,其中,vault-internal
为Headless的SVC。vault-active
为Raft选出的leader节点。vault-standby
为Raft中的follower节点。