Vault 介绍及安装配置

环境信息

  • Kubernetes 1.24
  • Vault 1.14.0

Vault 简介

Vault 架构及基础概念

Vault 的架构图如下 [1]

从以上架构图可以看到,几乎所有的 Vault 组件都被统称为 Barrier (屏障)

Vault 架构可以大体分为三个部分: [7]

  • Sotrage Backend - 存储后端
  • Barrier - 屏障层
  • HTTPS API - API 接口

常用概念

Storage Backend - Vault 自身不存储数据,因此需要一个存储后端(Storage Backend),存储后端对 Vault 来说是不受信任的,只用来存储加密数据。 [8]

Initialization - Vault 在首次启动时需要初始化(Initialization),这一步会生成一个 Master Key(加密密钥)用于加密数据,只有加密完成的数据才能保存到 Storage Backend

Unseal - Vault 启动后,因为不知道 Master Key (加密密钥)所以无法解密数据(可以访问 Storage Backend 上的数据),这种状态被称为 Sealed(已封印),在能解封(Unseal)数据之前,Vault 无法进行任何操作。Unseal 是获取 Master Key 明文的过程,通过 Master Key 可以解密 Encryption Key 从而可以解密存储的数据 [6]

Master Key - Encryption Key (用来加密存储的数据,加密密钥和加密数据被一同存储) 是被 Master Key(主密钥) 保护(加密),必须提供 Master Key,Vault 才能解密出 Encryption Key,从而完成数据解密操作。Master Key 与其他 Vault 数据被存放在一起,但使用另一种机制进行加密:解封密钥 ,解封密钥默认使用 沙米尔密钥分割算法 生成 Key Shares [9]

Key Shares - 默认情况下,Vault 使用 沙米尔密钥分割算法Master Key 的解封密钥分割成五个 Key Shares(分割密钥),必须要提供其中任意的三个 Key Shares 才能重建 Master Key,以完成 Unseal(解封)操作

Key Shares(分割密钥)的总数,以及重建 Master Key(主密钥)最少需要的分割密钥数量,都是可以调整的。 沙米尔密钥分割算法 也可以关闭,这样主密钥将被直接提供给管理员,管理员可直接使用它进行解封操作。

认证系统及权限系统处理流程

在解密出 Encryption Key 后,Vault 就可以处理客户端请求了。 HTTPS API 请求进入后的整个流程都由 Vault Core 管理,Core 会强制进行 ACL 检查,并确保 Audit logging(审计日志)完成记录。

客户端首次连接 Vault 时,需要首先完成身份认证,Vault 的 Auth Method 模块有很多的身份认证方法可选

  • 用户友好的认证方法,适合管理员使用,包括: user/password云服务商ldap 等,在创建用户的时候,需要为用户绑定 Policy,给予适合的权限
  • 应用友好的方法,适合应用程序使用,包括: public/private keystokenkubernetesjwt

身份验证请求经 Core 转发给 Auth Method 进行认证,Auth Method 判定请求身份是否有效并返回关联的策略(ACL Policies)的列表。

ACL PoliciesPolicy Store 负责管理与存储,Core 负责进行 ACL 检查,ACl 的默认行为是 Deny,意味着除非明确配置 ACL Policy 允许某项操作,否则该操作将被拒绝。

在通过 Auth Method 进行认证,并返回了没有问题的 ACL Policies 后,Token Store 会生成并管理一个新的 Token,这个 凭证 会返回给客户端,用于客户端后续请求的身份信息。Token 都存在一个 lease(租期)。Token 关联了相关的 ACL Policies,这些策略将被用于验证请求的权限。

请求经过验证后,将被路由到 Secret Engine,如果 Secret Engine 返回了一个 secretCore 将其注册到 Expiration Manager,并给它附件一个 Lease IDLease ID 被客户端用于更新(renew)或者吊销(revoke)它得到的 secret。如果客户端允许租约(lease) 到期,Expiration Manager 将自动吊销(revoke) 这个 secret

Secret Engine

Secret Engine 是保存、生成或者加密数据的组件,非常灵活。有的 Secret Engin 只是单纯的存储与读取数据,比如 kv(键值存储)就可以看作一个加密的 Redis。而其他的 Secret Engine 则可能连接到其他的服务并按需生成动态凭证等。

Kubernetes 中安装 Vault

官方建议使用 Helm Chart 在 Kubernetes 安装 Vault [2]

安装 Vault

  1. 添加 Hashicorp 的 Helm 仓库并检查 hashicorp/vault 的 Chart

    $ helm repo add hashicorp https://helm.releases.hashicorp.com
    "hashicorp" has been added to your repositories

    $ helm search repo hashicorp/vault
    NAME CHART VERSION APP VERSION DESCRIPTION
    hashicorp/vault 0.24.0 1.13.1 Official HashiCorp Vault Chart

    $ helm search repo hashicorp/vault -l
    NAME CHART VERSION APP VERSION DESCRIPTION
    hashicorp/vault 0.25.0 1.14.0 Official HashiCorp Vault Chart
    hashicorp/vault 0.24.1 1.13.1 Official HashiCorp Vault Chart
    hashicorp/vault 0.24.0 1.13.1 Official HashiCorp Vault Chart
    hashicorp/vault 0.23.0 1.12.1 Official HashiCorp Vault Chart
    hashicorp/vault 0.22.1 1.12.0 Official HashiCorp Vault Chart
    hashicorp/vault 0.22.0 1.11.3 Official HashiCorp Vault Chart

  2. 安装 Vault

    安装最新版本的 Vault。Vault 安装是有几种模式,分别对应不同的使用场景

    • Dev mode - Helm Chart 将会运行一个 Vault Server ,Storage Backend 会使用 Memeory,仅用于学习环境 [3]
    • Standalone mode - Helm Chart 默认运行的模式,将会安装一个 Vault Server,使用文件存储后端。
    • HA mode - Helm Chart 将会安装 3 个 Vault Servers ,使用已有的 Consul Storage Backend,建议 Consul 也使用 Consul Helm Chart 安装。 [4]

    以下命令安装 Vault,默认安装为最新版本,使用 Standalone 模式。

    helm install vault hashicorp/vault

    安装指定版本的 Vault
    helm install vault hashicorp/vault --version 0.24.0

    以下命令安装 Vault,默认安装为最新版本,使用 Dev 模式。
    helm install vault hashicorp/vault \
    --set "server.dev.enabled=true"

初始化 Vault

Vault Helm Chart 安装了 Standalone 或者 HA 模式后,需要进行初始化操作,初始化操作会产生 Share KeysMaster KeyEncryption Key

$ kubectl exec -it vault-0 -- vault operator init
Unseal Key 1: cs06exiIKAdCUtQb+X6qUqZB+QnsymfXAxgn9ri9sY
Unseal Key 2: CFwLgUIr510fFTWUFhzjoN8Am/8zynqeh6ROXMJIC+
Unseal Key 3: vTAtpYdUmFycNVCfsUwVKCY8svc0rnoEJPywBGqC7F
Unseal Key 4: 7R0VWDtDs5xpPirnPBHNyAcHwrQMkxvcllwSyJcHek
Unseal Key 5: /AIxSkqI/WGicMeawnceOQMXbhlp8EMICa0o6xby/O

Initial Root Token: hvs.hTLJJ1xrsUgcSSj8ET0eAr

Vault initialized with 5 key shares and a key threshold of 3. Please securely
distribute the key shares printed above. When the Vault is re-sealed,
restarted, or stopped, you must supply at least 3 of these keys to unseal it
before it can start servicing requests.

Vault does not store the generated root key. Without at least 3 keys to
reconstruct the root key, Vault will remain permanently sealed!

It is possible to generate new unseal keys, provided you have a quorum of
existing unseal keys shares. See "vault operator rekey" for more information.

初始化命令会产生 Share KeysRoot Token,默认至少使用 3 个 Share Keys 才能恢复 Master Key

使用 3 个 Share Keys(默认的阈值为 3 个 Share Keys 恢复 Master Key) 对 Vault Server 进行解封操作(Unseal)

$ kubectl exec -ti vault-0 -- vault operator unseal 
Unseal Key (will be hidden):
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed true
Total Shares 5
Threshold 3
Unseal Progress 1/3
Unseal Nonce a72a0446-3449-c8a9-a8af-74580f8e6aee
Version 1.14.0
Build Date 2023-06-19T11:40:23Z
Storage Type file
HA Enabled false

$ kubectl exec -ti vault-0 -- vault operator unseal
Unseal Key (will be hidden):
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed true
Total Shares 5
Threshold 3
Unseal Progress 2/3
Unseal Nonce a72a0446-3449-c8a9-a8af-74580f8e6aee
Version 1.14.0
Build Date 2023-06-19T11:40:23Z
Storage Type file
HA Enabled false

$ kubectl exec -ti vault-0 -- vault operator unseal
Unseal Key (will be hidden):
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 5
Threshold 3
Version 1.14.0
Build Date 2023-06-19T11:40:23Z
Storage Type file
Cluster Name vault-cluster-12faa106
Cluster ID 59fe0817-641e-0ce1-4dfd-2e3f9cb265c7
HA Enabled false

Sealed 状态为 false 时,表示解封完成,Vault 即可开始处理请求。

配置 UI 访问

使用 Vault Helm Chart 部署后,会自动创建针对 UI (端口 8200) 的 Service

apiVersion: v1
kind: Service
metadata:
name: vault
annotations:
meta.helm.sh/release-name: vault
meta.helm.sh/release-namespace: default
labels:
app.kubernetes.io/instance: vault
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: vault
helm.sh/chart: vault-0.25.0
namespace: default

spec:
selector:
app.kubernetes.io/instance: vault
app.kubernetes.io/name: vault
component: server
internalTrafficPolicy: Cluster
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- name: http
port: 8200
protocol: TCP
targetPort: 8200
- name: https-internal
port: 8201
protocol: TCP
targetPort: 8201

publishNotReadyAddresses: true
sessionAffinity: None
type: ClusterIP

要 Kubernetes 集群外部访问 UI,只需要配置对应的 Ingress Nginx 即可

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:

name: vault-ui
namespace: default

spec:
ingressClassName: nginx
rules:
- host: vault-ui.test.com
http:
paths:
- backend:
service:
name: vault
port:
number: 8200
path: /
pathType: Prefix

配置之后,访问 UI 域名,即可打开 Vault UI,使用初始化时生产的 Master Key 即可登陆 UI (登陆 Method 选择 Token

Vault 管理

查看 Vault 状态

vault status 命令可以查看 Vault 状态

$ vault status
Key Value
--- -----
Seal Type shamir
Initialized true
Sealed false
Total Shares 5
Threshold 3
Version 1.14.0
Build Date 2023-06-19T11:40:23Z
Storage Type file
Cluster Name vault-cluster-12faa106
Cluster ID 59fe0817-641e-0ce1-4dfd-2e3f9cb265c7
HA Enabled false

vault status 命令输出显示,当前 Vault 已完成初始化,并处于 Unsealed 解封状态 Sealed false,Seal 使用的是 Shamir 密钥分割算法,需要提供 5 条Share Keys 中的 3 条才能解封出 Master Key。Storage Backend 使用 file 类型。 [6]

secrets 管理

启用 secrets

启用 transit 引擎 [11]

$ vault secrets enable transit
Success! Enabled the transit secrets engine at: transit/

Vault 模拟了一个文件系统,各个 Secret Engine 启用后默认被挂载到 Secret Engine 名称路径下,以上示例启用 transit 加密引擎后,默认挂载点是 transit/ [11]

同一个 Secret Engine 可以被挂载到不同的路径,每个路径下的数据都彼此独立。

列出启用的 Secret Engine

以下命令列出系统上已经启用的 Secret Engine 及信息(挂载路径、类型等)

$ vault secrets list
Path Type Accessor Description
---- ---- -------- -----------
cubbyhole/ cubbyhole cubbyhole_e5c17df6 per-token private secret storage
identity/ identity identity_f0404cf8 identity store
sys/ system system_053aea79 system endpoints used for control, policy and debugging
transit/ transit transit_aaaaf63d n/a

创建密钥

$ vault write -f transit/keys/orders
Key Value
--- -----
allow_plaintext_backup false
auto_rotate_period 0s
deletion_allowed false
derived false
exportable false
imported_key false
keys map[1:1689133014]
latest_version 1
min_available_version 0
min_decryption_version 1
min_encryption_version 0
name orders
supports_decryption true
supports_derivation true
supports_encryption true
supports_signing false
type aes256-gcm96


$ vault list transit/keys
Keys
----
orders

Vault 服务配置说明

除非使用 dev 模式运行,否则 Vault 服务使用配置文件进行配置,配置文件使用 HCL 或者 JSON 格式。 [10]

在 Kubernetes 上使用 Standalone 模式运行,Storage Backend 使用 file 的 Vault 服务默认配置文件如下

extraconfig-from-values.hcl
disable_mlock = true
ui = true

listener "tcp" {
tls_disable = 1
address = "[::]:8200"
cluster_address = "[::]:8201"
# Enable unauthenticated metrics access (necessary for Prometheus Operator)
#telemetry {
# unauthenticated_metrics_access = "true"
#}
}
storage "file" {
path = "/vault/data"
}

# Example configuration for using auto-unseal, using Google Cloud KMS. The
# GKMS keys must already exist, and the cluster must have a service account
# that is authorized to access GCP KMS.
#seal "gcpckms" {
# project = "vault-helm-dev"
# region = "global"
# key_ring = "vault-helm-unseal-kr"
# crypto_key = "vault-helm-unseal-key"
#}

# Example configuration for enabling Prometheus metrics in your config.
#telemetry {
# prometheus_retention_time = "30s"
# disable_hostname = true
#}

常见错误

pod has unbound immediate PersistentVolumeClaims

使用 Vault Helm Chart 部署 Vault 后,Pod 状态为 Pending

$ kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
vault-0 0/1 Pending 0 105s <none> <none> <none> <none>
vault-agent-injector-6549d85b8f-8n4ql 1/1 Running 0 106s 10.244.4.204 k8s-worker1 <none>

检查 Pod 的相信描述信息,Pending 的原因为没有绑定到合适的 PV: 0/5 nodes are available: 5 pod has unbound immediate PersistentVolumeClaims

$ $ kubectl describe pod vault-0
Name: vault-0
Namespace: default
Priority: 0
Node: <none>
Labels: app.kubernetes.io/instance=vault
app.kubernetes.io/name=vault
component=server
controller-revision-hash=vault-546b6ddf48
helm.sh/chart=vault-0.25.0
statefulset.kubernetes.io/pod-name=vault-0
Annotations: <none>
Status: Pending
...
Conditions:
Type Status
PodScheduled False
Volumes:
data:
Type: PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
ClaimName: data-vault-0
ReadOnly: false
config:
Type: ConfigMap (a volume populated by a ConfigMa p)
Name: vault-config
Optional: false
home:
Type: EmptyDir (a temporary directory that shares a pod's lifetime)
Medium:
SizeLimit: <unset>
kube-api-access-5hvcw:
Type: Projected (a volume that contains injected data from multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling 2m9s default-scheduler 0/5 nodes are available: 5 pod has unbound immediate PersistentVolumeClaims. preemption: 0/5 nodes are available: 5 Preemption is not helpful for scheduling.

检查 Vault 的 Pod 的 StatefuleSet 配置信息,可以看到其需要一个 PV。根据 PVC 的需求创建 PV 后,Pod 状态变为 Running

$ kubectl edit statefulsets vault
...
volumeClaimTemplates:
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
creationTimestamp: null
name: data
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 10Gi
volumeMode: Filesystem
status:
phase: Pending


参考链接

secrets 管理工具 Vault 的介绍、安装及使用

Vault 中文手册

脚注