首页/文章列表/文章详情

vault

编程知识592025-05-14评论

Vault使用场景

数据加密

Vault中的所有用户数据在保存和传输时都必须加密。secrets engine用于解决在数据保存、数据传输和为外部系统生成数据时遇到的挑战。如kv secrets engine可以用于数据保存,将数据保存到物理存储上。而transmit secret engine可以用于数据的加密传输,但这种secret engine不会保存任何数据。

image

访问控制

Vault提供了多种访问控制能力,如ACL、control groups和sentinel策略。此外,Vault还可以提供动态凭据管理能力。

  • ACL策略:以声明的方式提供了一种允许或禁止访问以及操作Vault特定路径的方式。
  • Control groups:提供了额外的授权因素,只有通过授权的请求才能被Vault处理
  • Sentinel策略:支持复杂的逻辑,以角色治理策略(RGP,绑定到特定令牌、身份实体或身份群体)和终端治理策略(Endpoint Governing Policies (EGP),绑定到特定路径)的形式增强访问控制
image

有时间限制的访问

Vault通过将TTL和相关凭据关联的方式来实现有时间限制的访问。支持在服务层面和插件层面或角色层面配置TTL。

当一个用户使用预先定义的角色以及用户名和密码进行认证时,会接收到一个Vault token和一个附加的TTL值。

image

灾备恢复

可以主备集群以及Automated data snapshots实现灾备恢复。

image

基于身份(Identity)的安全性

身份可以帮助Vault通过实体(Entity)或别名(Aliases)识别client,实体或别名绑定到启用策略的token。Vault可以通过identity secrets engine提供身份管理方案。

image

人类和机器认证

auth methods属于Vault的一种插件,负责为一个用户分配认证所需的身份和策略。

  • 人类auth methods包括GitHub、LDAP和 userpass。
  • 机器auth methods包括AppRole、AWS、Kubernetes 和 TLS。

大部分情况下,Vault会将身份认证管理和决策委托给相关配置好的外部身份auth methods,如Amazon Web Services、GitHub、Kubernetes等。

image

静态和动态secrets的Secrets engines

Secrets engines适用于存储、生成和加密数据的插件,secrets engines可以管理两种类型的secrets:静态和动态secrets。

静态secrets不会过期,若要修改此类secrets,则需要人工介入。静态secrets包括第三方tokens、API keys、应用keys、PKI证书、PGP keys、加密keys、用户名和密码。

动态secret是指一定时间后需要吊销的secret。动态secrets通常会和第三方平台进行集成,从Vault接收请求并生成凭据。动态secrets包括数据库keys、云提供商的凭据以及短期secrets。

image

Install

$ brew tap hashicorp/tap$ brew install hashicorp/tap/vault$ brew upgrade hashicorp/tap/vault

Setup

使用如下命令启动vault:

$ vault server -dev -dev-root-token-id root -dev-tls

在新窗口中执行上面命令打印的vault地址和证书:

export VAULT_ADDR='https://127.0.0.1:8200'export VAULT_CACERT='/var/folders/hp/nqbj0l8x0jj8jfhh_6_62f6w0000gp/T/vault-tls3057506928/vault-ca.pem'

验证vault状态:

$ vault status

登陆:

$ vault login root

plugin

image

Vault支持3种插件:auth methods, secret engines和 database plugins,这些插件分为内置插件和外部插件两种。可以使用plugin_directory选项加载外部插件。

Vault Integrations给出了支持的插件列表。

Token

在客户端认证成功之后,vault会颁发一个token,用于校验客户端的访问以及客户端可以执行的操作。

使用vault cli执行命令前,需要设置通过 vault login登陆或设置VAULT_TOKEN环境变量。

image

Token元数据

一个token有几个重要属性:

  • Token duration:token的有效时间,默认为32天。只有root token可以不设置有效时间
  • Accessor:token查找、更新和吊销使用的唯一ID,但不能用于登陆vault。token创建之后是无法被查找的,可以使用accessor来间接操作token。
  • Policies:表示通过该token授权的vault操作,可以设置一个或多个策略
$ vault token create -period=30mKey Value--- -----token hvs.qp5hXcAcllG6ytHtZXEBxIeBtoken_accessor eYQSFsK1pIuhgrG1zkNxj2Tjtoken_duration 30mtoken_renewable truetoken_policies ["root"]identity_policies []policies ["root"]

Token类型

主要有两种类型:service tokens 和 batch tokens,大部分场景下使用service tokens就可以了。service tokens的格式为hvs.string,如上面的hvs.qp5hXcAcllG6ytHtZXEBxIeB

orphan tokens

当使用一个token创建新的token时,新创建出来的token会作为该token的子token,当吊销一个父token后,其所有的子tokens也会被吊销。如果一个token没有父token,则这类token被称为orphan token。

Policies

Vault的策略用于允许或禁止访问某些权限,由于Vault本身是基于路径的,因此在编写策略时,只需要控制对特定路径的操作即可。

在用户或服务通过Vault认证后,会给对应的token绑定一个策略。需要注意的是,任何时候都可以修改策略,但对于一个已经颁发的token,修改后的策略并不能直接生效到该token上,必须通过吊销token并重新和Vault认证来接收更新后的策略。

策略优先级

策略越具体,则优先级越高,例如下面第一条策略允许在secret/data/creds路径下执行createupdate操作,但由于第二条策略更具体,其优先级更高,因此,在secret/data/creds/confidential路径下只能执行read操作:

#第一条策略path"secret/data/creds" { capabilities = ["create","update"]}#第二条策略path"secret/data/creds/confidential" { capabilities = ["read"]}
路径相同

若路径相同,则deny优先:

# Vault policy to allow access to the dev-secrets k/v v2 secrets enginepath"dev-secrets/+/root" { capabilities = ["read"]}path"dev-secrets/+/root" { capabilities = ["deny"]}

如果没有deny策略且路径相同,则合并策略,下面允许在路径下执行listreadcreateupdate操作:

# Vault policy to allow access to the dev-secrets k/v v2 secrets enginepath"dev-secrets/+/root" { capabilities = ["list","read"]}path"dev-secrets/+/root" { capabilities = ["create","update"]}

策略通配符

使用通配符,可以允许访问dev-secrets/data/creds-webapp之类的路径,注意*只能用在路径末尾:

# Vault policy to allow access to the dev-secrets k/v v2 secrets enginepath"dev-secrets/data/creds*" { capabilities = ["create","list","read","update"]}

若要匹配中间路径,不能使用dev-secrets/*/creds,应该使用+:

# Vault policy to allow access to the dev-secrets k/v v2 secrets enginepath"dev-secrets/+/creds" { capabilities = ["create","list","read","update"]}

使用显示deny策略

下面策略允许在除dev-secrets/data/root以外的dev-secrets路径上执行create,list,readupdate操作:

# Vault policy to allow access to the dev-secrets k/v v2 secrets enginepath"dev-secrets/+/*" { capabilities = ["create","list","read","update"]}path"dev-secrets/+/root" { #更具体,优先级更高 capabilities = ["deny"]}

Role

Vault的role用于给auth method或secret engine添加更多配置。但并不是所有auth methods和secret engines都支持role,如userpass auth method就不支持role。

可以使用如下方式列出所有的role:

$ vault list auth/{auth_method}/role

下面为kubernetes auth method创建一个role。

首先启用Kubernetes auth method:

$ vault auth enable kubernetesSuccess! Enabled kubernetes auth method at: kubernetes/

提供连接kubernetes所需的配置:

$ vault write auth/kubernetes/config \ token_reviewer_jwt="$K8S_SERVICE_ACCOUNT_TOKEN" \ kubernetes_host=https://192.168.99.100:443 \ kubernetes_ca_cert=@ca.crt

创建一个名为hashicupsApp的role,在除该auth method要求的配置外,还提供了颁发token所需的policiesttlexplicit-max-ttl

$ vault write auth/kubernetes/role/hashicupsApp \ bound_service_account_names=k8sHashicupsAppSA \ bound_service_account_namespaces=k8sDevNamespace \ policies=default,dev-secrets \ ttl=1h \ explicit-max-ttl=2h

Auth 和secret engine的区别

Vault使用场景中可以看到,auth 是 Vault 中用于验证身份的机制。它允许用户、服务、应用程序等 以某种方式登录 Vault,获取一个token 来使用 Vault 的功能。而secrets 是 Vault 中用于 生成、存储和管理敏感数据的模块,称为Secrets Engine。

所有auth的mount地址都以auth/开头。

AppRole

用于给机器或apps提供认证。

image

关键参数

RoleID 和 SecretID类似机器或qpp认证所使用的用户名和密码。

  • RoleID:当使用AppRole方式登陆endpoint时,需要输入RoleID(role_id)
  • SecretID:默认登陆时需要通过secret_id输入secretID,也可以通过AppRole的bind_secret_id参数取消登陆时的SecretID参数。可以为role生成128位的随机UUID(pull模式),或自定义值(push模式)。与token类似,SecretID也有使用限制,TTLs和过期时间。

pull和push SecretID模式

如果用于登陆的SecretID是从AppRole获取到的,则为pull模式,如果由客户端设置AppRole的SecretID,则为push模式。大部分场景下推荐使用pull模式。

使用方式

API

创建role

启用approle

$ vault auth enable approle

创建一个policy:

$ vault policy write jenkins -<<EOF# Read-only permission on secrets stored at 'secret/data/mysql/webapp'path"secret/data/mysql/webapp" { capabilities = ["read" ]}EOF

创建一个role,关联创建出来的策略jenkins(更多参数):

$ vault write auth/approle/role/jenkins token_policies="jenkins" \ token_ttl=1h token_max_ttl=4h
生成RoleID和secretID

获取RoleID:

$ vault read auth/approle/role/jenkins/role-idKey Value--- -----role_id 675a50e7-cfe0-be76-e35f-49ec009731ea

生成secretID:

$ vault write -force auth/approle/role/jenkins/secret-idKey Value--- -----secret_id ed0a642f-2acf-c2da-232f-1b21300d5f29secret_id_accessor a240a31f-270a-4765-64bd-94ba1f65703c

查看role信息

$ vault read auth/approle/role/jenkins
使用RoleID & SecretID进行登陆

通过auth/approle/loginendpoint进行登陆,输入RoleID和SecretID:

$ vault write auth/approle/login role_id="675a50e7-cfe0-be76-e35f-49ec009731ea" \ secret_id="ed0a642f-2acf-c2da-232f-1b21300d5f29" Key Value--- -----token s.ncEw5bAZJqvGJgl8pBDM0C5htoken_accessor gIQFfVhUd8fDsZjC7gLBMnQutoken_duration 1htoken_renewable truetoken_policies ["default""jenkins"]identity_policies []policies ["default""jenkins"]token_meta_role_name jenkins
使用AppRole token读取secrets
$ export APP_TOKEN="s.ncEw5bAZJqvGJgl8pBDM0C5h"
$ VAULT_TOKEN=$APP_TOKEN vault kv get secret/mysql/webapp====== Secret Path ======secret/data/mysql/webapp======= Metadata =======Key Value--- -----created_time 2025-04-10T03:32:45.254602Zcustom_metadata <nil>deletion_time n/adestroyed falseversion 1====== Data ======Key Value--- -----db_name userspassword passw0rdusername admin

受策略限制,如果执行删除命令,则返回403:

$ VAULT_TOKEN=$APP_TOKEN vault kv delete secret/mysql/webappError deleting secret/mysql/webapp: Error making API request.URL: DELETE http://127.0.0.1:8200/v1/secret/data/mysql/webappCode: 403. Errors:* 1 error occurred: * permission denied
封装SecretID

SecretID类似密码,为了防止明文传递,可以对SecretID进行封装:

$ vault write -wrap-ttl=60s -force auth/approle/role/jenkins/secret-idKey Value--- -----wrapping_token: s.yzbznr9NlZNzsgEtz3SI56pXwrapping_accessor: Smi4CO0Sdhn8FJvL8XvOT30ywrapping_token_ttl: 1mwrapping_token_creation_time: 2021-06-07 20:02:01.019838 -0700 PDTwrapping_token_creation_path: auth/approle/role/jenkins/secret-id

解封装的SecretID:

$ VAULT_TOKEN="s.yzbznr9NlZNzsgEtz3SI56pX" vault unwrapKey Value--- -----secret_id c4086c73-4569-90c9-fd73-72c879e3b7b4secret_id_accessor 3a2e9483-a7d2-dc19-7480-b1a025daecccsecret_id_ttl 0s
Tips

查看role:

# 使用vault auth list查看approle的挂载路径$ vault list /auth/<mount_path>/role

secret engine

secret engine是用于存储、生成或加密数据的组件。

静态和动态secrets

KV secrets engine 通常用于存储静态secrets。此外,Vault还能生成动态secrets,如database secret engine, kubernetes secret engine等。

KV secrets engine

KV secrets engine是常用的键值存储,可以存储单一的键值对,也可以为每个键值对存储多个版本。

下面展示了vault kv子命令和对应的API endpoints:

注意这些API endpoints并不是vault kv直接访问的路径,而是执行子命令之后,后台访问的API路径。v1无版本概念,因此直接访问实际的key路径即可,v2有版本以及软删除等概念,因此需要不同的路径来存储这些数据。

CommandKV v1 endpointKV v2 endpoint
vault kv getsecret/<key_path>secret/data/<key_path>
vault kv putsecret/<key_path>secret/data/<key_path>
vault kv listsecret/<key_path>secret/metadata/<key_path>
vault kv deletesecret/<key_path>secret/data/<key_path>

此外,KV v2还有如下子命令:

CommandKV v2 endpoint
vault kv patchsecret/data/<key_path>
vault kv rollbacksecret/data/<key_path>
vault kv undeletesecret/undelete/<key_path>
vault kv destroysecret/destroy/<key_path>
vault kv metadatasecret/metadata/<key_path>
判断kv secrets engine的版本

下面查看了路径为secret/的kv secrets engine的信息,map[version:2]表示版本号为2:

$ vault read sys/mounts/secretKey Value--- -----accessor kv_0b42315dconfig map[default_lease_ttl:0 force_no_cache:false max_lease_ttl:0]deprecation_status supporteddescription key/value secret storageexternal_entropy_access falselocal falseoptions map[version:2]plugin_version n/arunning_plugin_version v0.21.0+builtinrunning_sha256 n/aseal_wrap falsetype kvuuid f702a289-5bcb-6655-fe97-78518ec26429
KV version 1

非版本的kv secrets engine,针对一个key,仅存储最新的value。

enable

启用version 1 kv存储:

$ vault secrets enable -version=1 kv
usage

写入数据:

$ vault kv put kv/my-secret my-value=s3cr3tSuccess! Data written to: kv/my-secret

读取数据:

$ vault kv get kv/my-secret

list keys:

$ vault kv list kv/

delete key:

$ vault kv delete kv/my-secret
TTLs

与其他secrets engines不同,KV secrets engine不会强制TTLs过期,切不会移除数据。此处的ttl仅表示建议:

$ vault kv put kv/my-secret ttl=30m my-value=s3cr3t
KV version 2

确定版本号:

$ vault read sys/mounts/kv2Key Value--- -----accessor kv_825ea02fconfig map[default_lease_ttl:0 force_no_cache:false max_lease_ttl:0]deprecation_status supporteddescription n/aexternal_entropy_access falselocal falseoptions map[version:2]plugin_version n/arunning_plugin_version v0.21.0+builtinrunning_sha256 n/aseal_wrap falsetype kvuuid d92d7308-d526-2f25-050c-e2acbf309432
enable
$ vault secrets enable -path <mount_path> -version=2 kv
write

如在secret/customer/acme路径下创建keys为customer_namecontact_email的数据:

$ vault kv put secret/customer/acme customer_name="ACME Inc." contact_email="john.smith@acme.com"

另一种方式是指定-mount:

$ vault kv put -mount <mount_path> <secret_path> <list_of_kv_values>

如:

$ vault kv put -mount=secret customer/acme customer_name="ACME Inc." contact_email="john.smith@acme.com"
====== Secret Path ======secret/data/customer/acme======= Metadata =======Key Value--- -----created_time 2022-06-13T13:41:45.673767Zcustom_metadata <nil>deletion_time n/adestroyed falseversion 1

创建之后version会从1开始递增,多次put或patch都会增加version。

使用vault kv metadata可以查看secrets拥有的版本:

$ vault kv metadata get secret/customer/acme
限制版本数

kv-v2 secrets engine默认可以保存10个版本。使用下面方式可以将版本数限制为4,如果版本超过4,则会删除最老的版本:

$ vault write secret/config max_versions=4
read

读取指定路径下的所有key/value对,如读取上面创建的secret/customer/acme路径下的所有数据:

$ vault kv get secret/customer/acme

等价于:

$ vault kv get -mount=secret customer/acme
read指定版本
$ vault kv get -version=1 secret/customer/acme
patch

vault kv put会完全替换当前版本的secrets,使用patch可以修改单个key的value。如仅修改secret/customer/acme下的contact_email的值,而保留其他keys不变:

$ vault kv patch secret/customer/acme contact_email="admin@acme.com"
delete

删除特定版本:

$ vault kv delete -versions="4,5" secret/customer/acme

可以看到deletion_timen/a,但destroyedfalse,表示此时为软删除

$ vault kv metadata get secret/customer/acme##...snip...====== Version 4 ======Key Value--- -----created_time 2021-10-31T00:14:59.830407Zdeletion_time 2021-10-31T00:16:25.860618Zdestroyed false====== Version 5 ======Key Value--- -----created_time 2021-10-31T00:15:01.892226Zdeletion_time 2021-10-31T00:16:25.860619Zdestroyed false##...snip...

可以通过vault kv undelete找回delete的数据:

$ vault kv undelete -versions=5 secret/customer/acme
destroy

使用destroy可以永久删除某个版本:

$ vault kv destroy -versions=4 secret/customer/acme

或删除一个路径下的所有版本的secret:

$ vault kv metadata delete secret/customer/acme

Kubernetes secrets engine

API

不推荐使用Vault Kubernetes Auth Method,这种方式会导致在 Vault 中产生许多难以管理的独特身份。

Kubernetes Secrets Engine可以生成kubernetes service account tokens,以及(可选的)service account、role bindings和roles。生成的service account tokens有一个配置的TTL,并在过期后删除所有创建的对象。

启用Kubernetes Secrets Engine:

$ vault secrets enable kubernetes

为vault创建一个service account并创建role和RoleBinding

apiVersion: v1kind: ServiceAccountmetadata: name: test-service-account-with-generated-token namespace: test---apiVersion: rbac.authorization.k8s.io/v1kind: Rolemetadata: name: test-role-list-pods namespace: testrules:- apiGroups: [""] resources: ["pods"] verbs: ["list"]- apiGroups: [""] resources: ["serviceaccounts","serviceaccounts/token"] verbs: ["create","update","delete"]---apiVersion: rbac.authorization.k8s.io/v1kind: RoleBindingmetadata: name: test-role-abilities namespace: testroleRef: apiGroup: rbac.authorization.k8s.io kind: Role name: test-role-list-podssubjects:- kind: ServiceAccount name: test-service-account-with-generated-token namespace: test

Kubernetes 1.24+还需要创建secret:

apiVersion: v1kind: Secretmetadata: name: vault-auth-secret namespace: test annotations: kubernetes.io/service-account.name: test-service-account-with-generated-tokentype: kubernetes.io/service-account-token

/configendpoint用于配置vault连接Kubernetes,后续可以通过vault read auth/kubernetes/config读取连接配置:

$ export SA_SECRET_NAME=$(kubectl get secrets --output=json \ | jq -r '.items[].metadata | select(.name|startswith("vault-auth-")).name')$ export SA_JWT_TOKEN=$(kubectl get secret $SA_SECRET_NAME \ --output 'go-template={{ .data.token }}' | base64 --decode)$ export SA_CA_CRT=$(kubectl -n test get secret $SA_SECRET_NAME -o jsonpath="{.data['ca\.crt']}" | base64 --decode; echo)$ export K8S_HOST=$(kubectl config view --raw --minify --flatten \ --output 'jsonpath={.clusters[].cluster.server}') $ vault write kubernetes/config \ service_account_jwt="$SA_JWT_TOKEN" \ kubernetes_host="$K8S_HOST" \ kubernetes_ca_cert="$SA_CA_CRT"

现在就可以使用 Kubernetes Secrets Engine创建Vault role,绑定到kubernetes的service account test-service-account-with-generated-token:

注意role的token_default_ttl,如果过期,在执行vault write kubernetes/creds/my-role时会返回permission denied.

$ vault write kubernetes/roles/my-role \ allowed_kubernetes_namespaces="*" \ service_account_name="test-service-account-with-generated-token" \ token_default_ttl="1000m"

在授予role足够的权限之后,在creds endpoint写入vault role之后就会生成并返回一个新的service account token,其lease_duration与上面创建的my-role的ttl相同。注意由于此处要生成kubernetes的serviceaccount token,因此要求my-role对应的serviceaccount 具有serviceaccounts/token资源的create权限:

$ vault write kubernetes/creds/my-role \ kubernetes_namespace=testKey Value--- -----lease_id kubernetes/creds/my-role/TrHUCplToMe5kv8E77IG45Wrlease_duration 16h40mlease_renewable falseservice_account_name test-service-account-with-generated-tokenservice_account_namespace testservice_account_token eyJhbGciOiJSUzI1NiIsImtpZCI6IjY2M3ps...

后续可以使用上述的service_account_token访问允许的Kubernetes API :

$ curl -sk $(kubectl config view --minify -o 'jsonpath={.clusters[].cluster.server}')/api/v1/namespaces/test/pods \ --header"Authorization: Bearer eyJHbGci0iJSUzI1Ni..."{"kind":"PodList","apiVersion":"v1","metadata": {"resourceVersion":"1624" },"items": []}

此外还可以在创建或调节(tune) Vault role时设置默认的TTL(token_default_ttl)和最大TTL(token_max_ttl):

$ vault write kubernetes/roles/my-role \ allowed_kubernetes_namespaces="*" \ service_account_name="new-service-account-with-generated-token" \ token_default_ttl="10m" \ token_max_ttl="2h"

还可以在生成token时指定TTL:

$ vault write kubernetes/creds/my-role \ kubernetes_namespace=test \ ttl=20mKey Value–-- -----lease_id kubernetes/creds/my-role/31d771a6-...lease_duration 20m0slease_renwable falseservice_account_name new-service-account-with-generated-tokenservice_account_namespace testservice_account_token eyJHbGci0iJSUzI1NiIsImtpZCI6ImlrUEE...

还可以为已存在的role重新指定kubernetes role:

$ vault write kubernetes/roles/auto-managed-sa-role \ allowed_kubernetes_namespaces="test" \ kubernetes_role_name="test-role-list-pods"

这样就可以通过vault write kubernetes/creds/auto-managed-sa-role kubernetes_namespace=test生成token。

PKI secrets engine

API

使用PKI签发证书时,首先需要创建一个issuer,即CA证书,可以使用vault生成自签CA或使用外部CA。vault pki的几个概念:

  • issuer:表示一个CA,且需要关联一个key,否则无法颁发证书。可以通过下面命令查看一个issuer关联的key:

    $ vault read -field=key_id pki/issuer/:issuer_ref
  • role:表示创建证书的模版,方便颁发证书,它会关联一个issuer,本质还是通过issuer颁发证书。通过如下命令查看关联的issuer:

    $ vault read -field=issuer_ref pki/roles/my-issuer-role
  • urls:一般包含issuing_certificatesocsp_serverscrl_distribution_points。设置好后,会将这些信息嵌入到后续颁发的证书的扩展字段中。这样在客户端验证证书时,可以自动下载 CA 证书链(Issuing CA),可以根据 CRL 或 OCSP URL 检查证书是否已吊销。

    X509v3 Authority Information Access: CA Issuers - URI:http://vault.example.com:8200/v1/pki/caX509v3 CRL Distribution Points: Full Name: URI:http://vault.example.com:8200/v1/pki/crlX509v3 OCSP: URI:http://vault.example.com:8200/v1/pki/ocsp
构建自己的CA
生成CA和私钥

证书可以来自现有的密钥对,也可以生成自签证书。通常建议维护自己的root CA,然后给vault提供一个signed intermediate CA

下面使用根证书生成中间证书,然后使用中间证书来为test.example.com证书:

image
生成root CA

启用pki secret engine:

$ vault secrets enable pki

设置secret engine的TTL ,默认为30d,下面设置为1年,为全局设置,还可以为单独证书设置TTL。

$ vault secrets tune -max-lease-ttl=8760h pki

下面生成example.com root CA,设置issuer name并将证书保存到root_2023_ca.crt文件中:

/pki/root/generate/:type生成根证书,type字段可选:exported,会在响应中返回私钥;internal,不会返回私钥,且无法被检索到;existing,使用key_ref参数查找已有的key来创建CSR。

$ vault write -field=certificate pki/root/generate/internal \ common_name="example.com" \ issuer_name="root-2023" \ ttl=87600h > root_2023_ca.crt

查看证书的issuer:

$ vault list pki/issuers/

通过证书的issuer ID获取证书和其他issuer的元数据(如是否revoked):

$ vault read pki/issuer/$(vault list -format=json pki/issuers/ | jq -r '.[]') \ | tail -n 6

为root CA创建一个role,pki的role可以帮助颁发证书,但前提是要有CA证书。注意此处省去了参数issuer_ref="default"

$ vault write pki/roles/2023-servers allow_any_name=true

配置CA和CRL URLs,下面是全局配置,可以通过AIA字段配置单个issuer的URLs:

$ vault write pki/config/urls \ issuing_certificates="$VAULT_ADDR/v1/pki/ca" \ crl_distribution_points="$VAULT_ADDR/v1/pki/crl"
生成中间CA

使用上面生成的根CA来创建中间CA证书。首先在pki_int路径下启用secrets engine:

$ vault secrets enable -path=pki_int pki

pki_int secrets engine颁发的证书的TTL为43800h:

$ vault secrets tune -max-lease-ttl=43800h pki_int

通过/pki/intermediate/generate/:typeendpoint创建中间CA的CSR,保存为pki_intermediate.csr:

$ vault write -format=json pki_int/intermediate/generate/internal \ common_name="example.com Intermediate Authority" \ issuer_name="example-dot-com-intermediate" \ | jq -r '.data.csr' > pki_intermediate.csr

使用之前生成的root CA 私钥签发证书,endpoint/pki/root/sign-intermediate,保存为intermediate.cert.pem:

$ vault write -format=json pki/root/sign-intermediate \ issuer_ref="root-2023" \ csr=@pki_intermediate.csr \ format=pem_bundle ttl="43800h" \ | jq -r '.data.certificate' > intermediate.cert.pem

一旦签发CSR,root CA会返回一个证书,将该证书加载回vault:

$ vault write pki_int/intermediate/set-signed certificate=@intermediate.cert.pem
创建role

role是一个逻辑名称,映射到一系列生成凭据的策略,帮助颁发证书。一个role会通过issuer_ref关联到一个issuer,默认为defaultissuer。

role的配置参数可以控制证书的common names, alternate names等,下面是值得注意的参数:

ParamDescription
allowed_domains指定role的domains,与allow_bare_domainsallow-subdomains选项结合使用
allow_bare_domains指定clients是否可以请求与实际domain本身的值相匹配的证书
allow_subdomains指定clients是否可以请求带有CNs的证书,且这些CNs是role允许的CNs的子域(注意:此包含通配符子域)。
allow_glob_domains允许在allowed_domains中指定的名称中包含glob模式(如 ftp*.example.com)

创建一个roleexample-dot-com,允许子域(allow_subdomains),并通过issuer_ref指定issuer:

$ vault write pki_int/roles/example-dot-com \ issuer_ref="$(vault read -field=default pki_int/config/issuers)" \ allowed_domains="example.com" \ allow_subdomains=true \ max_ttl="720h"
请求证书

使用example-dot-comrole为test.example.com域创建一组新的证书(私钥和证书),并返回颁发证书的CA和完整的CA chain。后续可以通过该操作来为相同的CN颁发新的证书:

$ vault write pki_int/issue/example-dot-com common_name="test.example.com" ttl="24h"
吊销证书

在吊销一个证书时会重新生成一个CRL,此时vault会移除所有过期的证书。

吊销一个证书时,需要输入证书的序列号:

$ vault write pki_int/revoke serial_number=<serial_number>
移除过期的证书
$ vault write pki_int/tidy tidy_cert_store=true tidy_revoked_certs=true
rotate root CA

用于更换root CA。rotate老的root CA的最大挑战是,可能存在一些长时间离线的设备,但需要在上线之后能够获取到新的root CA。可以使用root bridge CA来关联新老 root CA。

使用外部CA

通过加载外部CA的方式颁发证书。需要提供一对配对的CA和key。可以通过如下方式校验CA和key是否配对:

$ openssl x509 -noout -modulus -in ca.crt | openssl md5$ openssl rsa -noout -modulus -in ca.key | openssl md5

加载外部CA:

$ vault write pki/config/ca pem_bundle=@root-ca.crt 

加载外部key,可以通过vault list pki/keys查看加载的keys:

$ vault write pki/keys/import pem_bundle=@root-ca.key 

查看issuer是否自动关联了key:

$ vault read -field=key_id pki/issuer/:issuer_ref

配置证书分发地址:

$ vault write pki/config/urls \ issuing_certificates="$VAULT_ADDR/v1/pki/ca" \ crl_distribution_points="$VAULT_ADDR/v1/pki/crl"

创建 Role,关联上面加载的issuer:

$ vault write pki/roles/my-issuer-role \ allowed_domains="example.com" \ allow_subdomains=true \ generate_lease=true \ max_ttl="72h" \ issuer_ref="{issuer}"

使用该 Issuer 签发证书:

$ vault write pki/issue/my-issuer-role \ common_name="app.example.com" \ ttl="24h"
Tips
  • Vault生成证书时并不需要指定私钥,而是需要指定与issuer(CA证书)关联的role。颁发(issue)证书和签发(sign)证书的区别:

    • 基于role(:name)颁发证书,包括私钥和证书:

      MethodPathIssuer
      POST/pki/issue/:nameRole selected
      POST/pki/issuer/:issuer_ref/issue/:namePath selected
    • 签发证书

      基于给定的CSR和参数来签发一个受限于role(:name)的新证书,返回签发的证书和完整的CA chain。

      MethodPathIssuer
      POST/pki/sign/:nameRole selected
      POST/pki/issuer/:issuer_ref/sign/:namePath selected
    • 签发中间CA
      使用配置的CA证书来签发一个中间CA:

      MethodPathIssuer
      POST/pki/root/sign-intermediatedefault
      POST/pki/issuer/:issuer_ref/sign-intermediateSelected

      签发中间CA需要用到CSR,可以使用如下接口生成中间CSR:

      MethodPathPrivate key source (type)
      POST/pki/intermediate/generate/:typespecified per request
      POST/pki/issuers/generate/intermediate/:typespecified per request
      POST/pki/intermediate/cross-signexisting
  • 查看default issuer:

    $ vault read pki/config/issuersKey Value--- -----default 8ceb6b59-7042-25e3-da6f-f06b745773abdefault_follows_latest_issuer false

    可以通过vault read pki/cert/ca_chainvault read pki/issuer/default查看默认issuer的证书链。

  • 查看证书内容:
    首先获取证书的序列号(注意:vault list pki/issuers获取到的并不是证书中的序列号,而是issuer的UUID),

    注意,pki/certsendpoint不包含下面序列号:

    • ca:默认issuer的CA证书
    • crl:默认issuer的CRL
    • ca_chain:默认issuer的CA信任链

    但包含vault生成的root证书该证书有可能是默认issuer。不包含外部加载的证书(root和中间CA证书)。

    $ vault list pki/certs

    然后通过序列号查看证书的内容:

    $ vault read -field=certificate pki/cert/:serial|openssl x509 -noout -text
  • 查看吊销证书,返回证书的序列号。vault list pki/certs返回的证书中包含已吊销和未吊销的证书:

    $ vault list pki/certs/revoked

    查看CRL列表:

    $ vault read pki/cert/crl$ vault read pki/issuer/:issuer_ref/crl

    查看crl的内容:

    $ vault read -field=crl pki/issuer/:issuer_ref/crl| openssl crl -text -noout
  • 验证证书签发

    $ vault pki verify-sign pki/issuer/:issuer_ref pki_int/issuer/:issuer_ref
  • 生成key

    $ vault write pki/keys/generate/exported -format=json | jq -r '.data.private_key' > root-key.pem

Storage

通过在vault配置文件的storage字段配置存储后端:

storage [NAME] { [PARAMETERS...]}

如:

storage"file" { path ="/mnt/vault/data"}

Vault支持Integrated Storage(磁盘)和external storage(如Consul、DynamoDB等)

Integrated storage(Raft)后端

使用Integrated Storage时要求提供cluster_addr,用于指定节点间Raft通信的地址和端口:

storage"raft" { path ="/path/to/raft/data" node_id ="raft_node_1" retry_join { leader_api_addr ="http://127.0.0.4:8200" leader_ca_cert_file ="/path/to/ca3" leader_client_cert_file ="/path/to/client/cert3" leader_client_key_file ="/path/to/client/key3" }}cluster_addr ="http://127.0.0.1:8201"

主要参数:

  • path:vault数据存储的文件系统路径
  • node_id:节点在Raft集群的标识符
  • retry_join:指定集群的其他节点,可以配置一个或多个,用于帮助本节点加入集群。

Vault agent

image

Auto-auth

自动认证包含两部分:

当一个工具(vault agent或vault proxy)启用自动认证后,工具会使用配置的认证方式请求一个vault token。如果请求成功,自动认证会将token写入合适的sink中。

Templates

简介

template_config用于配置template引擎的默认行为。template用于配置Vault Agent使用Consul Template语言将secrets渲染到文件。可以配置多个templatetemplate中可以通过contents直接提供需要渲染的内容,或通过source选择引用一个单独的.ctmpl文件进行渲染,如:

template_config { static_secret_render_interval ="10m" exit_on_retry_failure = true max_connections_per_host = 20}template { source ="/tmp/agent/template.ctmpl" destination ="/tmp/agent/render.txt"}template { contents ="{{ with secret \"secret/my-secret\" }}{{ .Data.data.foo }}{{ end }}" destination ="/tmp/agent/render-content.txt"}

template需要通过secret方法pkiCert方法进行渲染。前者适用于所有类型的secrets,后者仅适用于PKI secrets engine证书颁发相关的工作。

下面展示了secret方法,其中Data字段可选,如果存在,则为vault write请求,否则为vault read请求:

{{ secret"<PATH>""<DATA>" }}

下面从KV 存储中读取一个secret:

{{ with secret"secret/my-secret" }}{{ .Data.data.foo }}{{ end }}

如果只需要使用Vault Agent渲染模版,且不需要sink获取到的凭据,则可以忽略auto_authsink

更新secrets

Vault Agent会自动更新secrets/tokens。

  • 当一个secret或token更新之后,Vault Agent会在secret的租期过去2/3之后更新该secret。
  • 如果一个secret或token没有被更新且没有租期,则Vault Agent默认每5分钟拉取一次secret,可以通过static_secret_render_interval进行配置。
  • 如果一个secret或token没有被更新且有租期,则Vault Agent默认会在90%的TTL时拉取该secret,可以通过lease_renewal_threshold进行设置

用法

token_file

创建测试数据

$ tee data.json -<<EOF{"organization":"ACME Inc.","customer_id":"ABXX2398YZPIE7391","region":"US-West","zip_code":"94105","type":"premium","contact_email":"james@acme.com","status":"active"}EOF$ vault kv put secret/customers/acme @data.json

创建一个template:

$ tee customer.json.tmpl -<<EOF{ {{ with secret"secret/data/customers/acme" }}"Organization":"{{ .Data.data.organization }}","ID":"{{ .Data.data.customer_id }}","Contact":"{{ .Data.data.contact_email }}" {{ end }}}EOF

创建Vault Agent配置文件:

tee agent-config.hcl -<<EOFpid_file ="./pidfile"vault { address ="$VAULT_ADDR" tls_skip_verify = true}auto_auth { method { type ="token_file" config = { token_file_path ="$HOME/.vault-token" } } sink"file" { config = { path ="$HOME/vault-token-via-agent" } }}template { source ="$HOME/vault-test/customer.json.tmpl" destination ="$HOME/vault-test/customer.json"}EOF

允许Vault Agent,之后就可以在"$HOME/vault-test/customer.json"中看到渲染之后的内容:

$ vault agent -config=agent-config.hcl

可以通过-config指定多个配置文件,在运行时会组合成单个配置文件。

Kubernetes
Vault Agent auto auth

💁 注意kubernetes vault agent和kubernetes secret engines的不同之处。前者是在vault中创建绑定到kubernetes的serviceaccount的role,并赋予该role访问vault的某些数据的policy,vault agent就可以在auto_auth中使用该role来渲染模版。后者同样会在vault中创建绑定到kubernetes的serviceaccount的role,但会赋予该role操作Kubernetes资源的权限,后续就可以通过该role生成可以操作kubernetes资源的token。

创建Kubernetes的serviceaccount以及相关资源:

apiVersion: v1kind: ServiceAccountmetadata: name: vault-auth namespace: default---apiVersion: rbac.authorization.k8s.io/v1kind: ClusterRoleBindingmetadata: name: role-tokenreview-binding namespace: defaultroleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: system:auth-delegatorsubjects:- kind: ServiceAccount name: vault-auth namespace: default---apiVersion: v1kind: Secretmetadata: name: vault-auth-secret annotations: kubernetes.io/service-account.name: vault-authtype: kubernetes.io/service-account-token

生成demo数据,后续用于Vault Agent渲染模版:

$ vault policy write myapp-kv-ro - <<EOFpath"secret/data/myapp/*" { capabilities = ["read","list"]}EOF$ vault kv put secret/myapp/config \ username='appuser' \ password='suP3rsec(et!' \ ttl='30s'

创建kubernetes auth config。与kubernetes secret engine不同,这里用的是kubernetes auth method

$ export SA_SECRET_NAME=$(kubectl get secrets --output=json \ | jq -r '.items[].metadata | select(.name|startswith("vault-auth-")).name')$ export SA_JWT_TOKEN=$(kubectl get secret $SA_SECRET_NAME \ --output 'go-template={{ .data.token }}' | base64 --decode)$ export SA_CA_CRT=$(kubectl -n default get secret $SA_SECRET_NAME -o jsonpath="{.data['ca\.crt']}" | base64 --decode; echo)$ export K8S_HOST=$(kubectl config view --raw --minify --flatten \ --output 'jsonpath={.clusters[].cluster.server}')
$ vault auth enable kubernetes$ vault write auth/kubernetes/config \ token_reviewer_jwt="$SA_JWT_TOKEN" \ kubernetes_host="$K8S_HOST" \ kubernetes_ca_cert="$SA_CA_CRT" \ issuer="https://kubernetes.default.svc.cluster.local"

创建一个role,对应kubernetes中的default命名空间的serviceaccountvault-auth,并赋予其访问demo数据的权限myapp-kv-ro:

$ vault write auth/kubernetes/role/example \ bound_service_account_names=vault-auth \ bound_service_account_namespaces=default \ token_policies=myapp-kv-ro \ ttl=24h

在使用该serviceaccount的pod中验证该role的权限是否正确:

$ KUBE_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)$ curl --request POST \ --data '{"jwt":"'"$KUBE_TOKEN"'","role":"example"}' \ $VAULT_ADDR/v1/auth/kubernetes/login | python3 -m json.tool

创建Vault Agent配置文件,其中用到了上面创建的role example:

apiVersion: v1data: vault-agent-config.hcl: | # Comment this out if running as sidecar instead of initContainer exit_after_auth = true pid_file ="/home/vault/pidfile" auto_auth { method"kubernetes" { mount_path ="auth/kubernetes" #role的挂载路径 config = { role ="example" #role的名称 } } sink"file" { config = { path ="/home/vault/.vault-token" } } } template { destination ="/etc/secrets/index.html" contents = <<EOT <html> <body> <p>Some secrets:</p> {{- with secret"secret/data/myapp/config" }} <ul> <li><pre>username: {{ .Data.data.username }}</pre></li> <li><pre>password: {{ .Data.data.password }}</pre></li> </ul> {{ end }} </body> </html> EOT }kind: ConfigMapmetadata: name: example-vault-agent-config namespace: default

最后执行渲染,并通过卷共享给其他pod:

apiVersion: v1kind: Podmetadata: name: vault-agent-example namespace: defaultspec: serviceAccountName: vault-auth #注意使用与role绑定的serviceaccount volumes: - configMap: items: - key: vault-agent-config.hcl path: vault-agent-config.hcl name: example-vault-agent-config name: config - emptyDir: {} name: shared-data initContainers: #使用vault agent init container进行渲染 - args: - agent - -config=/etc/vault/vault-agent-config.hcl - -log-level=debug env: - name: VAULT_ADDR value: http://EXTERNAL_VAULT_ADDR:8200 image: vault name: vault-agent volumeMounts: - mountPath: /etc/vault name: config - mountPath: /etc/secrets name: shared-data containers: - image: nginx name: nginx-container ports: - containerPort: 80 volumeMounts: - mountPath: /usr/share/nginx/html name: shared-data
Vault Agent Injector(webhook)

这种方式vault-k8s提供的一种kubernetes mutation webhook方式(上面的configmap是自己指定的initcongtainer),监控pod的CREATEUPDATE事件,如果检测到此类事件,且annotation中包含vault.hashicorp.com/agent-inject: trueannotation,则会据此变更pod规格。

它有两种方式init和sidecar,init容器会将secret生成到共享内存卷(默认为/vault/secrets,可以通过vault.hashicorp.com/secret-volume-path修改),而sidecar则会持续渲染secrets。

使用annotation渲染

与auto auth一样,需要创建一个与kubernetes serviceeaccount对应的role,以及访问策略,后续就可以在annotation vault.hashicorp.com/role中使用该role:

$ vault write auth/kubernetes/role/internal-app \ bound_service_account_names=internal-app \ bound_service_account_namespaces=default \ policies=internal-app \ ttl=24h

annotation格式为:

vault.hashicorp.com/agent-inject-secret-<unique-name>: /path/to/secret

下面例子中,第一个annotation 会被渲染到/vault/secrets/foo,第二个会被渲染到/vault/secrets/bar:

vault.hashicorp.com/agent-inject-secret-foo: database/roles/appvault.hashicorp.com/agent-inject-secret-bar: consul/creds/appvault.hashicorp.com/role: 'app'

secret模版格式如下:

vault.hashicorp.com/agent-inject-template-<unique-name>: | < TEMPLATE HERE >

举例如下:

vault.hashicorp.com/agent-inject-secret-foo: 'database/creds/db-app'vault.hashicorp.com/agent-inject-template-foo: | {{- with secret"database/creds/db-app" -}} postgres://{{ .Data.username }}:{{ .Data.password }}@postgres:5432/mydb?sslmode=disable {{- end }}vault.hashicorp.com/role: 'app'

有用的annotations

  • vault.hashicorp.com/role: role名称,即vault中的auth/<kubernetes>/role/<role_name>role_name

  • vault.hashicorp.com/agent-inject-secret-<unique-name>: /path/to/secret:其中unique-name是渲染的secret的文件名,/path/to/secret指定了vault中用于渲染的secret的数据路径。

  • vault.hashicorp.com/secret-volume-path: /apps/conf:指定渲染文件(即上述的<unique-name>)的挂载路径的方式。使用vault.hashicorp.com/secret-volume-path-SECRET-NAME可以将文件和路径映射起来,如vault.hashicorp.com/secret-volume-path-foo指定了渲染文件foo的所在路径,如果不指定文件路径映射关系,则表示所有渲染文件的默认路径。

  • vault.hashicorp.com/agent-inject-template-<unique-name>:指定secret的渲染模板

  • vault.hashicorp.com/agent-inject-status: update:在injection结束后设置的状态值,

  • vault.hashicorp.com/agent-pre-populate-only:"true":如果为true,则不会注入sidecar容器。推荐CronJobJob使用

使用Vault agent configuration map

该方式需要通过vault.hashicorp.com/agent-configmap指定Vault Agent configuration 文件,配置文件会被挂载到/vault/configs,配置中需要包含如下文件:

  • config-init.hcl:init容器使用,必须将exit_after_auth设置为true.
  • config.hcl:sidecar容器使用,必须将exit_after_auth设置为false.
---apiVersion: v1kind: ServiceAccountmetadata: name: app-example---apiVersion: apps/v1kind: Deploymentmetadata: name: app-example-deploymentspec: replicas: 1 selector: matchLabels: app: app-example template: metadata: labels: app: app-example annotations: vault.hashicorp.com/agent-inject: 'true' vault.hashicorp.com/agent-configmap: 'my-configmap' vault.hashicorp.com/tls-secret: 'vault-tls-client' spec: containers: - name: app image: 'app:1.0.0' serviceAccountName: app-example---apiVersion: v1kind: ConfigMapmetadata: name: my-configmapdata: config.hcl: |"auto_auth" = {"method" = {"config" = {"role" ="db-app" }"type" ="kubernetes" }"sink" = {"config" = {"path" ="/home/vault/.token" }"type" ="file" } }"exit_after_auth" = false"pid_file" ="/home/vault/.pid""template" = {"contents" ="{{- with secret \"database/creds/db-app\" -}}postgres://{{ .Data.username }}:{{ .Data.password }}@postgres:5432/mydb?sslmode=disable{{- end }}""destination" ="/vault/secrets/db-creds" }"vault" = {"address" ="https://vault.demo.svc.cluster.local:8200""ca_cert" ="/vault/tls/ca.crt""client_cert" ="/vault/tls/client.crt""client_key" ="/vault/tls/client.key" } config-init.hcl: |"auto_auth" = {"method" = {"config" = {"role" ="db-app" }"type" ="kubernetes" }"sink" = {"config" = {"path" ="/home/vault/.token" }"type" ="file" } }"exit_after_auth" = true"pid_file" ="/home/vault/.pid""template" = {"contents" ="{{- with secret \"database/creds/db-app\" -}}postgres://{{ .Data.username }}:{{ .Data.password }}@postgres:5432/mydb?sslmode=disable{{- end }}""destination" ="/vault/secrets/db-creds" }"vault" = {"address" ="https://vault.demo.svc.cluster.local:8200""ca_cert" ="/vault/tls/ca.crt""client_cert" ="/vault/tls/client.crt""client_key" ="/vault/tls/client.key" }

官方给出了一些渲染deployment,configmap,将渲染内容注入环境变量等例子。

CLI

Token

capabilities

校验一个token访问某个路径的权限,如下面校验hvs.CAESI...WtiSW5mWUYcubbyhole/foo的访问权限,返回deny

$ vault token capabilities hvs.CAESI...WtiSW5mWUY database/creds/readonly
create

将token绑定到多个策略:

$ vault token create -policy=my-policy -policy=other-policy

创建periodic token,renew的时候使用此period:

$ vault token create -period=30m

还可以通过-ttl指定token的ttl,用于创建非periodic token。

renew
$ vault token renew 96ddf4bc-d217-f3ba-f9bd-017055595017

为token指定续订的时间,如果无-increment参数,则使用默认TTL。periodic tokens忽略该参数

$ vault token renew -increment=30m 96ddf4bc-d217-f3ba-f9bd-017055595017
lookup

查看一个token的详细信息:

$ vault token lookup 96ddf4bc-d217-f3ba-f9bd-017055595017

查看一个token绑定的策略:

$ vault token lookup | grep policies
查看所有token

查看所有token的accessors

$ vault list auth/token/accessors

查看特定的accessor:

$ vault token lookup -format json -accessor <accessor>

过滤出root token:

$ vault list -format json auth/token/accessors | jq -r .[] | xargs -I '{}' vault token lookup -format json -accessor '{}' | jq -r 'select(.data.policies | any(. =="root"))'

policy

list
$ vault policy list
read
$ vault policy read my-policy
write

从本地/tmp/policy.hcl加载策略:

$ vault policy write my-policy /tmp/policy.hcl

标准输入(stdio)加载策略:

$ cat my-policy.hcl | vault policy write my-policy -
delete
$ vault policy delete my-policy

Auth

用于与Vault的auth methods进行交互,如增删改查不同的auth methods。

enable

启用某个auth method

$ vault auth enable userpass
list

查看已启用的auth method

$ vault auth list -detailed

secrets

用于和secrets engines进行交互。

enable

在某个路径下启用一个secrets engine。

$ vault secrets enable awsSuccess! Enabled the aws secrets engine at: aws/
$ vault secrets enable -path=ssh-prod ssh
$ vault secrets enable -max-lease-ttl=30m database
list

查看启用的secrets engines

$ vault secrets list -detailed

kv

参见[KV secrets engine](#KV secrets engine)

metadata

添加metadata

$ vault kv metadata put -custom-metadata=hello="hellotest" 

查询metadata

$ vault kv metadata get secret/hello

read

从特定路径读取数据,可以使用-field指定读取的字段。

write

write命令可以向指定路径写入数据(凭据、secrets、配置或任意数据)。使用key=value格式指定数据,如果value前面包含@,则说明要从文件加载数据,如果一个key的value是-,则说明从标准输入读取数据。可以通过-field指定打印的字段。

$ vault write cubbyhole/git-credentials username="student01" password="p@$$w0rd"

-force允许不带数据的write操作:

$ vault write -force transit/keys/my-key
$ echo $MY_TOKEN | vault write consul/config/access token=-

operator

Vault成员操作
$ vault operator raft list-peers
$ vault operator raft join [options] <leader-api-addr>#如 vault operator raft join"http://127.0.0.2:8200"
$ vault operator raft remove-peer <server_id>
snapshot

备份和恢复

Usage: vault operator raft snapshot <subcommand> [options] [args] This command groups subcommands for operators interacting with the snapshot functionality of the integrated Raft storage backend.Subcommands: restore Installs the provided snapshot, returning the cluster to the state defined in it save Saves a snapshot of the current state of the Raft cluster into a file

查看snapshot的信息

如keys数目、大小等

$ vault operator raft snapshot inspect <snapshot_file>

troubleshoot

审计日志

Vault的audit记录了所有客户端请求和服务端响应的详细信息。审计日志中的auth.policy_results字段给出了请求授权结果:

字段名含义
type日志类型,这里是 request,表示是客户端发起的请求(另一种常见的是 response)。
time请求的时间戳,精确到纳秒(RFC3339 格式)
auth授权信息,表示请求是通过哪个 token、哪些策略授权的。
request请求的详细信息,包括路径、操作类型、数据等。

如果一个client出现"permission denied"之类的错误,可以通过如下方式查看token对应的策略,找出client操作是否在策略要求之内。同时结合审计日志可以更加确定问题根因:

curl \ --silent \ --header"X-Vault-Token: $VAULT_TOKEN" \ $VAULT_ADDR/v1/auth/token/lookup-self \ | jq
启用审计设备

⚠️当启用多审计设备时,Vault会尝试将审计日志发送到所有审计设备,且只要有一个审计设备可用,Vault就可以正常处理请求。因此建议为Vault配置多个审计设备

审计设备要求如下ACL策略:

# 'sudo' capability is required to manage audit devicespath"sys/audit/*"{ capabilities = ["create","read","update","delete","list","sudo"]}# To list enabled audit devices, 'sudo' capability is requiredpath"sys/audit"{ capabilities = ["read","sudo"]}

下面将审计信息写入了file类型的设备中:

$ vault audit enable file file_path=/vault/vault-audit.log

审计日志中的token会被hash,如果要查看原始日志,可以启用log_raw=true:

$ vault audit enable -path=file_raw file \ file_path=/vault/audit-law.log \ log_raw=true

在debug之后记得关闭原始审计功能:

$ vault audit disable file_raw

查看启用的审计设备:

$ vault audit list -detailed
查询审计日志

查看审计日志中的错误信息:

export AUDIT_LOG_FILE="$PWD/learn-vault-monitoring/vault-audit.log"
jq 'select(.error != null) | [.time,.error]' $AUDIT_LOG_FILE

敏感信息出错时会返回HMAC-SHA256 编码后的错误,使用如下方式查看哈希错误以及对应的时间戳:

jq 'select(.response.data.error != null) | [.time,.response.data.error]' \ $AUDIT_LOG_FILE

查看请求路径:

jq -n '[inputs | {Path: .request.path} ] | group_by(.Path) | map({Path: .[0].Path, Count: length}) | sort_by(-.Count) | limit(5;.[])' $AUDIT_LOG_FILE

统计错误次数:

jq -n '[inputs | {Errors: .error} ] | group_by(.Errors) | map({Errors: .[0].Errors, Count: length}) | sort_by(-.Count) | .[]' $AUDIT_LOG_FILE

查看client地址以及访问路径:

jq -s 'group_by(.request.remote_address) | map({"remote_address": .[0].request.remote_address,"access": (group_by(.request.path) | map({"key":.[0].request.path,"value":length}) | from_entries)})' $AUDIT_LOG_FILE

审计日志中的敏感信息是被HMAC过的,如root token的名称、accessor等。

可以使用如下方式计算字符串的HMAC值,然后在审计日志中查找:

$ vault write sys/audit-hash/file input="olAP0Oxb0rvUZAWkRRVcMtYl"Key Value--- -----hash hmac-sha256:d890b3417cef5aa22ee035a3ed685c78ad34f4939a53738c33f87f490a93838b

校验策略权限

有时候会碰到403 permission denied错误,通常是由于策略权限不足导致的:

$ vault token create -policy=webappKey Value--- -----token s.IcTMGNOug5Cx3wBqpGvI5X4etoken_accessor s2FhMCQssibpiGeBzVWhxJmntoken_duration 768htoken_renewable truetoken_policies ["default""webapp"]identity_policies []policies ["default""webapp"]

可以使用vault token capabilities命令测试该token是否有访问某个路径的权限:

$ vault token capabilities s.IcTMGNOug5Cx3wBqpGvI5X4e transit/decrypt/phone-number

vault debug

可以生成perf文件。

遥测指标

使用Prometheus metrics查看异常。

CPU指标
  • cpu.usage_user
  • cpu.usage_iowait

cpu.iowait_cpu大于10%时需要注意。

Network指标
  • net.bytes_recv
  • net.bytes_sent
Memory指标
  • mem.total
  • mem.used_percent
文件描述符指标
  • linux_sysctl_fs.file-nr:主机上使用的文件句柄数
  • linux_sysctl_fs.file-max

file-nr超过80%的file-max时需要注意。

从lost quorum中恢复

vault要求有quorum个正常运行的vault server。从lost quorum中恢复的方法是将集群模式转变为单节点模式。

  1. 首先找到vault-config.hcl中定义的storage

    storage"raft" { path ="/vault/data" server_id ="vault_1"}....
  2. /vault/data/raft目录中创建一个peers.json文件,包含可工作的节点信息:

    • id:server的service_id

    • address:server的地址和端口

    • non_voter:指定该server是否是non-voter角色

    $ cat > /vault/data/raft/peers.json << EOF[ {"id":"vault_1","address":"10.0.101.22:8201","non_voter": false }]EOF
  3. 重启vault

  4. Unseal Vault并查看vault状态

    $ vault operator unseal$ vault status
  5. 校验结果

    $ vault operator raft list-peers

如果后续集群恢复,则将其他节点添加到peers.json文件中即可。

Recovery mode(恢复模式)

主要用于解决由于某些新的bug导致Vault无法启动的问题。

规格

性能测试

使用vault-benchmark

Seal/unseal

该功能需要非dev server。vault的配置和启动参见官方文档。在正式环境中,vault启动时会生成5个unseal key和1个root token。在下面最后一行中可以看到如果要unseal的话,需要3个unseal key。

Unseal Key 1: BvaQM7LygUstmn7N34C7KQ4GWKBi/om5v8vVsCTKek/lUnseal Key 2: Rx3xYfOuLJrRThLTbAauc7gn6E10w3I4BM6WqJ0lKNGOUnseal Key 3: JBYOsGqAbocP75by6MvE4Zyoodv5p33LzFmBMOSsWslcUnseal Key 4: f1CmSy/QzzsJWZAcpa+1bIapffldSJhwFVYwXa+KV0uoUnseal Key 5: L/cXSbU5v4UizDup0GP/EAu8jJ4ZdFfXU15HRssKlHtIInitial Root Token: hvs.eV6KNABVAdV5uSvsficEJpCxVault initialized with 5 key shares and a key threshold of 3...

这种情况下vault的数据是机密的,需要通过unseal过程来获得加密密钥,否则不能操作vault。unseal的过程如下,执行如下命令3次,输入3个不同的unseal key即可:

vault operator unseal

常用命令

vault secrets listvault kv list apps/#查看role和policyvault auth listvault list auth/dragon_kubernetes_drg1-prd-asf-quarantine/role/dragon-kubernetes-namespace-vault-rolevault policy list vault policy read app_ro_dragon_kubernetes_drg1-prd-asf-quarantinevault read auth/dragon_kubernetes_drg1-dev-central/role/dragon-loggingvault read auth/dragon_kubernetes_drg1-dev-central/config 

修改vault的kv值:

vault kv get -format=json apps/dragon_prime_imageservice/credentialsvault kv put apps/dragon_prime_imageservice/credentials @/tmp/data.json
c

charlieroro

这个人很懒...

用户评论 (0)

发表评论

captcha