vault

目录

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

本地测试

Shamir
配置文件(Shamir)
  • docker-compose.yml:

    services:
      vault-1:
        image: hashicorp/vault:latest
        container_name: vault-1
        restart: unless-stopped
        ports:
          - "8200:8200"
          - "8201:8201"
        volumes:
          - vault-1-data:/vault/data
          - ./vault-config-1.hcl:/vault/config/vault.hcl
          - vault-logs-1:/vault/logs
        cap_add:
          - IPC_LOCK
        environment:
          - VAULT_ADDR=http://127.0.0.1:8200
          - VAULT_API_ADDR=http://vault-1:8200
          - VAULT_CLUSTER_ADDR=http://vault-1:8201
        command: vault server -config=/vault/config/vault.hcl
        networks:
          - vault-network
    
      vault-2:
        image: hashicorp/vault:latest
        container_name: vault-2
        restart: unless-stopped
        ports:
          - "8210:8200"
          - "8211:8201"
        volumes:
          - vault-2-data:/vault/file
          - ./vault-config-2.hcl:/vault/config/vault.hcl
          - vault-logs-2:/vault/logs
        cap_add:
          - IPC_LOCK
        environment:
          - VAULT_ADDR=http://127.0.0.1:8200
          - VAULT_API_ADDR=http://vault-2:8200
          - VAULT_CLUSTER_ADDR=http://vault-2:8201
        command: vault server -config=/vault/config/vault.hcl
        networks:
          - vault-network
    
      vault-3:
        image: hashicorp/vault:latest
        container_name: vault-3
        restart: unless-stopped
        ports:
          - "8220:8200"
          - "8221:8201"
        volumes:
          - vault-3-data:/vault/file
          - ./vault-config-3.hcl:/vault/config/vault.hcl
          - vault-logs-3:/vault/logs
        cap_add:
          - IPC_LOCK
        environment:
          - VAULT_ADDR=http://127.0.0.1:8200
          - VAULT_API_ADDR=http://vault-3:8200
          - VAULT_CLUSTER_ADDR=http://vault-3:8201
        command: vault server -config=/vault/config/vault.hcl
        networks:
          - vault-network
    
    volumes:
      vault-1-data:
      vault-2-data:
      vault-3-data:
      vault-logs-1:
      vault-logs-2:
      vault-logs-3:
    
    networks:
      vault-network:
        driver: bridge
    
  • vault-config-1.hcl:

    storage "raft" {
      path = "/vault/file"
      node_id = "vault-1"
    
      retry_join {
        leader_api_addr = "http://vault-1:8200"
      }
      retry_join {
        leader_api_addr = "http://vault-2:8200"
      }
      retry_join {
        leader_api_addr = "http://vault-3:8200"
      }
    }
    
    listener "tcp" {
      address       = "0.0.0.0:8200"
      tls_disable   = true
      cluster_address = "0.0.0.0:8201"
    }
    
    api_addr = "http://vault-1:8200"
    cluster_addr = "http://vault-1:8201"
    ui = true
    disable_mlock = true
    log_level = "info"
    
  • vault-config-2.hcl:

    storage "raft" {
      path = "/vault/file"
      node_id = "vault-2"
    
      retry_join {
        leader_api_addr = "http://vault-1:8200"
      }
      retry_join {
        leader_api_addr = "http://vault-2:8200"
      }
      retry_join {
        leader_api_addr = "http://vault-3:8200"
      }
    }
    
    listener "tcp" {
      address       = "0.0.0.0:8200"
      tls_disable   = true
      cluster_address = "0.0.0.0:8201"
    }
    
    api_addr = "http://vault-2:8200"
    cluster_addr = "http://vault-2:8201"
    ui = true
    disable_mlock = true
    log_level = "info"
    
  • vault-config-3.hcl:

    storage "raft" {
      path = "/vault/file"
      node_id = "vault-3"
    
      retry_join {
        leader_api_addr = "http://vault-1:8200"
      }
      retry_join {
        leader_api_addr = "http://vault-2:8200"
      }
      retry_join {
        leader_api_addr = "http://vault-3:8200"
      }
    }
    
    listener "tcp" {
      address       = "0.0.0.0:8200"
      tls_disable   = true
      cluster_address = "0.0.0.0:8201"
    }
    
    api_addr = "http://vault-3:8200"
    cluster_addr = "http://vault-3:8201"
    ui = true
    disable_mlock = true
    log_level = "info"
    
启动方式
docker-compose up -d
unseal集群
# 设置第一个节点的地址
export VAULT_ADDR='http://127.0.0.1:8200'

# 初始化第一个Vault节点
echo "初始化Vault集群..."
vault operator init -key-shares=5 -key-threshold=3 -format=json > vault-init.json

# 提取根令牌和解封密钥
export VAULT_TOKEN=$(cat vault-init.json | jq -r '.root_token')
UNSEAL_KEY_1=$(cat vault-init.json | jq -r '.unseal_keys_b64[0]')
UNSEAL_KEY_2=$(cat vault-init.json | jq -r '.unseal_keys_b64[1]')
UNSEAL_KEY_3=$(cat vault-init.json | jq -r '.unseal_keys_b64[2]')

echo "根令牌: $VAULT_TOKEN"
echo "解封密钥已保存到 vault-init.json"

# 解封第一个节点
echo "解封 vault-1..."
vault operator unseal $UNSEAL_KEY_1
vault operator unseal $UNSEAL_KEY_2
vault operator unseal $UNSEAL_KEY_3

# 等待一会儿让第一个节点完全启动
sleep 5

# 解封第二个节点
echo "解封 vault-2..."
export VAULT_ADDR='http://127.0.0.1:8210'
vault operator unseal $UNSEAL_KEY_1
vault operator unseal $UNSEAL_KEY_2
vault operator unseal $UNSEAL_KEY_3

# 解封第三个节点
echo "解封 vault-3..."
export VAULT_ADDR='http://127.0.0.1:8220'
vault operator unseal $UNSEAL_KEY_1
vault operator unseal $UNSEAL_KEY_2
vault operator unseal $UNSEAL_KEY_3

# 切换回第一个节点
export VAULT_ADDR='http://127.0.0.1:8200'

# 检查集群状态
echo "检查Raft集群状态..."
vault operator raft list-peers

echo "Vault集群初始化完成!"
echo "Web UI 访问地址:"
echo "  - vault-1: http://localhost:8200"
echo "  - vault-2: http://localhost:8210"
echo "  - vault-3: http://localhost:8220"
echo ""
echo "使用以下命令设置环境变量:"
echo "  export VAULT_ADDR='http://127.0.0.1:8200'"
echo "  export VAULT_TOKEN='$VAULT_TOKEN'"
Tips
  • 上述配置没有启用tls,如果仍然出现未找到vault-ca.pem的错误,则可能是因为本地环境导致的,可以尝试清除本地环境变量:

    unset VAULT_CACERT
    unset VAULT_CA_CERT
    unset VAULT_CLIENT_CERT
    unset VAULT_CLIENT_KEY
    
配置文件(Auto-unseal by transit secrets)

可以测试auto seal和recovery keys。

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=30m
Key                  Value
---                  -----
token                hvs.qp5hXcAcllG6ytHtZXEBxIeB
token_accessor       eYQSFsK1pIuhgrG1zkNxj2Tj
token_duration       30m
token_renewable      true
token_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 engine
path "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 engine
path "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 engine
path "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 engine
path "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 engine
path "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 kubernetes
Success! 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-id
Key     Value
---     -----
role_id 675a50e7-cfe0-be76-e35f-49ec009731ea

生成secretID:

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

查看role信息

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

通过auth/approle/login endpoint进行登陆,输入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.ncEw5bAZJqvGJgl8pBDM0C5h
token_accessor          gIQFfVhUd8fDsZjC7gLBMnQu
token_duration          1h
token_renewable         true
token_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.254602Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            1

====== Data ======
Key         Value
---         -----
db_name     users
password    passw0rd
username    admin

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

$ VAULT_TOKEN=$APP_TOKEN vault kv delete secret/mysql/webapp
Error deleting secret/mysql/webapp: Error making API request.

URL: DELETE http://127.0.0.1:8200/v1/secret/data/mysql/webapp
Code: 403. Errors:

* 1 error occurred:
    * permission denied
封装SecretID

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

$ vault write -wrap-ttl=60s -force auth/approle/role/jenkins/secret-id

Key                              Value
---                              -----
wrapping_token:                  s.yzbznr9NlZNzsgEtz3SI56pX
wrapping_accessor:               Smi4CO0Sdhn8FJvL8XvOT30y
wrapping_token_ttl:              1m
wrapping_token_creation_time:    2021-06-07 20:02:01.019838 -0700 PDT
wrapping_token_creation_path:    auth/approle/role/jenkins/secret-id

解封装的SecretID:

$ VAULT_TOKEN="s.yzbznr9NlZNzsgEtz3SI56pX" vault unwrap

Key                   Value
---                   -----
secret_id             c4086c73-4569-90c9-fd73-72c879e3b7b4
secret_id_accessor    3a2e9483-a7d2-dc19-7480-b1a025daeccc
secret_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有版本以及软删除等概念,因此需要不同的路径来存储这些数据。

Command KV v1 endpoint KV v2 endpoint
vault kv get secret/<key_path> secret/data/<key_path>
vault kv put secret/<key_path> secret/data/<key_path>
vault kv list secret/<key_path> secret/metadata/<key_path>
vault kv delete secret/<key_path> secret/data/<key_path>

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

Command KV v2 endpoint
vault kv patch secret/data/<key_path>
vault kv rollback secret/data/<key_path>
vault kv undelete secret/undelete/<key_path>
vault kv destroy secret/destroy/<key_path>
vault kv metadata secret/metadata/<key_path>
判断kv secrets engine的版本

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

$ vault read sys/mounts/secret
Key                        Value
---                        -----
accessor                   kv_0b42315d
config                     map[default_lease_ttl:0 force_no_cache:false max_lease_ttl:0]
deprecation_status         supported
description                key/value secret storage
external_entropy_access    false
local                      false
options                    map[version:2]
plugin_version             n/a
running_plugin_version     v0.21.0+builtin
running_sha256             n/a
seal_wrap                  false
type                       kv
uuid                       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=s3cr3t
Success! 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/kv2
Key                        Value
---                        -----
accessor                   kv_825ea02f
config                     map[default_lease_ttl:0 force_no_cache:false max_lease_ttl:0]
deprecation_status         supported
description                n/a
external_entropy_access    false
local                      false
options                    map[version:2]
plugin_version             n/a
running_plugin_version     v0.21.0+builtin
running_sha256             n/a
seal_wrap                  false
type                       kv
uuid                       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.673767Z
custom_metadata    <nil>
deletion_time      n/a
destroyed          false
version            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.830407Z
deletion_time    2021-10-31T00:16:25.860618Z
destroyed        false

====== Version 5 ======
Key              Value
---              -----
created_time     2021-10-31T00:15:01.892226Z
deletion_time    2021-10-31T00:16:25.860619Z
destroyed        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: 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"]
- apiGroups: [""]
  resources: ["serviceaccounts", "serviceaccounts/token"]
  verbs: ["create", "update", "delete"]
---
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

Kubernetes 1.24+还需要创建secret:

apiVersion: v1
kind: Secret
metadata:
  name: vault-auth-secret
  namespace: test
  annotations:
    kubernetes.io/service-account.name: test-service-account-with-generated-token
type: kubernetes.io/service-account-token

/config endpoint用于配置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=test

Key                          Value
---                          -----
lease_id                     kubernetes/creds/my-role/TrHUCplToMe5kv8E77IG45Wr
lease_duration               16h40m
lease_renewable              false
service_account_name         test-service-account-with-generated-token
service_account_namespace    test
service_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=20m

Key                        Value
–--                        -----
lease_id                   kubernetes/creds/my-role/31d771a6-...
lease_duration             20m0s
lease_renwable             false
service_account_name       new-service-account-with-generated-token
service_account_namespace  test
service_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/ca
    
    X509v3 CRL Distribution Points:
        Full Name:
          URI:http://vault.example.com:8200/v1/pki/crl
    
    X509v3 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/:type endpoint创建中间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,默认为default issuer。

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

Param Description
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)

创建一个role example-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-com role为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)颁发证书,包括私钥和证书:

      Method Path Issuer
      POST /pki/issue/:name Role selected
      POST /pki/issuer/:issuer_ref/issue/:name Path selected
    • 签发证书

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

      Method Path Issuer
      POST /pki/sign/:name Role selected
      POST /pki/issuer/:issuer_ref/sign/:name Path selected
    • 签发中间CA
      使用配置的CA证书来签发一个中间CA:

      Method Path Issuer
      POST /pki/root/sign-intermediate default
      POST /pki/issuer/:issuer_ref/sign-intermediate Selected

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

      Method Path Private key source (type)
      POST /pki/intermediate/generate/:type specified per request
      POST /pki/issuers/generate/intermediate/:type specified per request
      POST /pki/intermediate/cross-sign existing
  • 查看default issuer:

    $ vault read pki/config/issuers
    Key                              Value
    ---                              -----
    default                          8ceb6b59-7042-25e3-da6f-f06b745773ab
    default_follows_latest_issuer    false
    

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

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

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

    • 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 -<<EOF
pid_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: v1
kind: ServiceAccount
metadata:
  name: vault-auth
  namespace: default
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: role-tokenreview-binding
  namespace: default
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: system:auth-delegator
subjects:
- kind: ServiceAccount
  name: vault-auth
  namespace: default
---
apiVersion: v1
kind: Secret
metadata:
  name: vault-auth-secret
  annotations:
    kubernetes.io/service-account.name: vault-auth
type: kubernetes.io/service-account-token

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

$ vault policy write myapp-kv-ro - <<EOF
path "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命名空间的serviceaccount vault-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: v1
data:
  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: ConfigMap
metadata:
  name: example-vault-agent-config
  namespace: default

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

apiVersion: v1
kind: Pod
metadata:
  name: vault-agent-example
  namespace: default
spec:
  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: true annotation,则会据此变更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/app
vault.hashicorp.com/agent-inject-secret-bar: consul/creds/app
vault.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: v1
kind: ServiceAccount
metadata:
  name: app-example
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app-example-deployment
spec:
  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: v1
kind: ConfigMap
metadata:
  name: my-configmap
data:
  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。

revoke
$ vault token revoke hvs.mY48tW5VVMB8UpxO4tPXuF50
Success! Revoked token (if it existed)
create root token

初始化revoke root token操作,记住生成的OTP

$ vault operator generate-root -init
Nonce         0754414b-f868-a14f-b3b1-91d2a1f3a511
Started       true
Progress      0/3
Complete      false
OTP           gUZhMmgA4gXJSJtHZlx7mcoNEWqL
OTP Length    28

多次执行如下命令生成新的root token(过程需要unseal keys或recovery keys):

$ vault operator generate-root

最终生成加密的root token:

$ vault operator generate-root
Operation nonce: 0754414b-f868-a14f-b3b1-91d2a1f3a511
Unseal Key (will be hidden):
Nonce            0754414b-f868-a14f-b3b1-91d2a1f3a511
Started          true
Progress         3/3
Complete         true
Encoded Token    DyMpRj0PHSduEykBJBoeMA0ZQWc9Gho4cgVJeQ

使用如下方式解密root token:

$ vault operator generate-root -decode=DyMpRj0PHSduEykBJBoeMA0ZQWc9Gho4cgVJeQ -otp=gUZhMmgA4gXJSJtHZlx7mcoNEWqL

hvs.pbzfZtqKwPjxWu9PPyuv7R85
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 aws
Success! 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 devices
path "sys/audit/*"
{
  capabilities = ["create", "read", "update", "delete", "list", "sudo"]
}

# To list enabled audit devices, 'sudo' capability is required
path "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=webapp

Key                  Value
---                  -----
token                s.IcTMGNOug5Cx3wBqpGvI5X4e
token_accessor       s2FhMCQssibpiGeBzVWhxJmn
token_duration       768h
token_renewable      true
token_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

参考:

unseal

vault默认是sealed状态,该状态下,vault可以访问物理存储,但无法解密任何数据。为了解密数据,vault需要获取root key,因此unseal对过程是获取root key的过程。vault使用unseal keys加密了root key

vault默认会使用Shamir's Secret Sharing 算法将root key切为多份。要求一定的份数(-key-threshold)才能重建root key,通过vault operator unseal <key>命令执行unseal过程(需要执行多次(-key-threshold))。

初始化方式如下:

$ vault operator init \
    -key-shares=3 \
    -key-threshold=2 \
    -pgp-keys="keybase:hashicorp,keybase:jefferai,keybase:sethvargo"

默认方式下,vault启动时需要一个节点一个节点去unseal。在vault重启之后会变回sealed状态,需要重新unseal。

auto unseal

Vault 启动后,无需手工unseal,而是自动向一个可信的云服务(如 AWS KMS、Azure Key Vault、GCP CKMS 等)请求解密自身的数据加密密钥。使用Shamir seal时,unseal需要提供unseal keys,使用auto unseal则需要提供recovery keys。

初始化方式如下:

$ vault operator init \
    -recovery-shares=3 \
    -recovery-threshold=2 \
    -recovery-pgp-keys="keybase:hashicorp,keybase:jefferai,keybase:sethvargo"

注意这里生成的是recovery keys:

Recovery Key 1: wcFMA7l3edsU86d4ARAAgOUbfhQmfDN9HS/rolMfYzi5rWRh4sPZMZ47aNGA8A5jjElyIBVCxvnOxxxxxxxxx
Recovery Key 2: wcFMA/WshhkMOj35AQ/9G3Fm2xwJvOCyDAFGYthfP6axpPv1oDnPSH8DIuU0bnGOizj479wrqhzDxxxxxxxxx

以Azure Key Vault为例,初始化过程会生成主密钥 (Master Key),这个密钥用于加密和解密 Vault 中所有的数据。

初始化

1. Vault 初始化
   ├─ vault operator init
   ├─ 生成 Recovery Keys(而非 Unseal Keys)
   ├─ 生成 Root Token
   └─ Master Key

2. Azure Key Vault 交互
   ├─ Vault 连接到 Azure KV
   ├─ 使用指定的密钥加密 Master Key
   └─ 加密后的 Master Key 存储在 Vault 存储后端

自动解封

1. Vault 服务启动
   ├─ 读取配置文件
   ├─ 连接到 Azure Key Vault
   └─ 验证身份认证

2. 自动解封流程
   ├─ 从存储后端读取加密的 Master Key
   ├─ 向 Azure KV 发送解密请求
   ├─ Azure KV 返回解密后的 Master Key
   ├─ Vault 使用 Master Key 解锁数据加密密钥
   └─ Vault 状态变为 "unsealed"

与Shamir unseal不同的是Recovery keys 并不会解密root key,其运行依赖auto unseal 机制的稳定性。可以使用vault operator generate-root命令配合Recovery keys重新生成root key

Recovery keys cannot decrypt the root key and therefore are not sufficient to unseal Vault if the auto unseal mechanism isn't working.
If the seal mechanism or its keys are permanently deleted, then the Vault cluster cannot be recovered, even from backups.

seal

该命令会删除内存中的root key,需要通过unseal才能恢复root key。使用命令vault operator seal可以手动seal vault。

Rekey

Rekey unseal key

使用vault operator rekey可以rekey unseal keys:

  1. 创建gpg Keys

    • 创建gpg keys: gpg --full-generate-key

      • Key Type: RSA and RSA
      • Key Size: 4096
      • Key Validity: 0 (does not expire)
    • 列出存在的gpg keys:gpg --list-secret-keys --keyid-format=long

      gpg: checking the trustdb
      gpg: marginals needed: 3  completes needed: 1  trust model: pgp
      gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
      [keyboxd]
      ---------
      sec   rsa4096/CBBD350A01D3E854 2025-07-22 [SC]
            1794F41C93736FCEABCA2F54CBBD350A01D3E854
      uid                 [ultimate] Eduardo Mota <eduardo.mota@farfetch.com>
      ssb   rsa4096/156C54AB294220D2 2025-07-22 [E]
      
    • 导出public GPG key:

      gpg --armor --export <ID>
      
      # Example
      gpg --armor --export CBBD350A01D3E854 > my.asc
      
  2. 使用如上方式创建2个GPG keys: test1.asctest2.asc

  3. 启动rekey:

    vault operator rekey -init -key-shares=2 -key-threshold=2 -pgp-keys="/vault/docker/test1.asc,/vault/docker/test2.asc"
    
    • init:初始化rekey操作,可以重新设置key-shareskey-threshold

    • key-shares:指定要生成的keys的数目

    • key-threshold:指定需要多少个keys才能重建root key,即需要提供key-threshold个keys才能访问vault。

    • pgp-keys:包含用于加密key-shares的 PGP 公钥。列表中的公钥数量必须与 -key-shares 参数指定的数量完全一致。每个新生成的key会分别使用列表中对应位置的公钥进行加密。

  4. 启动rekey需要输入unseal key,多次执行vault operator rekey并输入正确的unseal keys(需要达到上一次初始化时指定的-key-threshold数目)

    输入足够的unseal keys之后,会生成新的keys,如下生成了2个keys(-key-shares=2)

    Key 1 fingerprint: 436578ae2740acf7f728e430b8b9205505336caa; value: wcFMA7l3edsU86d4AQ//c255uELAcWj8aHDtgD845OIp+DC4oW/2TRjjvD/CzKfQnMfDsYI0zr+cDi2ewvxxxxxx
    
    Key 2 fingerprint: f91a16e501f5ce0b6a66c88a3473c995e110912d; value: wcFMA/WshhkMOj35AQ//Sp5MLWn4H+l/3U6EfjCLavcnqxNVT3K6fZcURtFFtnlM8TOviVOI+Y6flGN/GNxxxxxx
    
  5. 保存被GPG加密的key:

    echo "wcFMA7l3edsU86d4AQ//c255uELAcWj8aHDtgD845OIp+DC4oW/2TRjjvD/CzKfQnMfDsYI0zr+cDi2ewvxxxxxx" | base64 -d > key1.gpg
    echo "wcFMA/WshhkMOj35AQ//Sp5MLWn4H+l/3U6EfjCLavcnqxNVT3K6fZcURtFFtnlM8TOviVOI+Y6flGN/GNxxxxxx" | base64 -d > key2.gpg
    
  6. 解密key,后续可以通过这些keys unseal vault:

    # 解密 Key 1(需要对应的私钥)
    gpg --decrypt key1.gpg
    # 5b873863bedc8bfb99a571d7cc212dd0e6a4e8a25cba4d6171ea5e66c1c244e38f
    
    # 解密 Key 2(需要对应的私钥)
    gpg --decrypt key2.gpg
    #62b11b46a10c36fd7f6713ac930656dd743bd8db0845229efd4e9ae8878d3e2e22
    

Tips

  • 使用-cancel可以取消rekey流程: vault operator rekey -target=recovery -init -key-shares=2 -key-threshold=2 -pgp-keys="/vault/docker/test1.asc,/vault/docker/test2.asc" -cancel
Rekey recovery key

要求auto seal模式。

rekey recovery key需要指定target:vault operator rekey --target=recovery。使用如下命令rekey recovery key:

  1. 按照[Rekey unseal key](#Rekey unseal key)的第1步,生成2个GPG keys: test1.asctest2.asc

  2. 启动rekey流程:

    vault operator rekey -target=recovery -init -key-shares=2 -key-threshold=2 -pgp-keys="/vault/docker/test1.asc,/vault/docker/test2.asc"
    
    • target:默认是barrier,即unseal key。这里为recovery,表示recovery keys。
  3. 多次执行如下命令(需要输入老的recovery keys)来生成新的recovery keys

    vault operator rekey -target=recovery -key-shares=2 -key-threshold=2 -pgp-keys="/vault/docker/test1.asc,/vault/docker/test2.asc"
    

    最终生成2个(-key-shares)recovery keys:

    Key 1 fingerprint: 436578ae2740acf7f728e430b8b9205505336caa; value: wcFMA7l3edsU86d4ARAApOqalZXU+s5JcbiXJIfVjZovmvNVuVF0aVv0L52qEkKmF5Rt9FFE8xxxxxx
    
    Key 2 fingerprint: f91a16e501f5ce0b6a66c88a3473c995e110912d; value: wcFMA/WshhkMOj35ARAAgmGOOJ+5ILralqlzHRKc8f8Nj0S9WPD0AATQ+IJ+E29Lvlq5nKmc9xxxxxx
    
  4. 按照[Rekey unseal key](#Rekey unseal key)的第5,6步可以解密新的recovery keys

升级

参考官方文档,查看:

常用命令

vault secrets list
vault kv list apps/

#查看role和policy
vault auth list
vault list auth/dragon_kubernetes_drg1-prd-asf-quarantine/role/dragon-kubernetes-namespace-vault-role
vault policy list 
vault policy read app_ro_dragon_kubernetes_drg1-prd-asf-quarantine

vault read auth/dragon_kubernetes_drg1-dev-central/role/dragon-logging
vault read auth/dragon_kubernetes_drg1-dev-central/config

修改vault的kv值:

vault kv get -format=json apps/dragon_prime_imageservice/credentials
vault kv put apps/dragon_prime_imageservice/credentials @/tmp/data.json
posted @ 2025-05-14 17:56  charlieroro  阅读(139)  评论(0)    收藏  举报