Skip to content

chiflix/vault-best-practice

Repository files navigation

在之前的文章「防拖库的最佳实践」中,我提到了使用 Vault 來存储秘钥的方案。本文是我在开发中使用的最佳实践。

准备工作

首先生成一套部署 Vault 所需要的证书。在这里我使用了 cfssl 这个工具,在 Mac 下可以使用 brew install cfssl 来安装。

  1. 创建一个 ca-csr.json 配置文件,如 ca/ca-csr.json。之后运行命令。

cfssl gencert -initca ./ca/ca-csr.json | cfssljson -bare ca

生成 ca.pemca-key.pem

  1. 再创建一个 ca-config.json 配置文件,如 ca/ca-config.json,和 vault-csr.json 配置文件,如 ca/vault-csr.json。之后运行命令:
cfssl gencert \
-ca=ca.pem \
-ca-key=ca-key.pem \
-config=./ca/ca-config.json \
-hostname="vault,localhost,127.0.0.1,vault.of.your.custom.domain.name.com" \
-profile=default \
./vault-csr.json | cfssljson -bare vault
  1. 生成 vault.pem 后再运行 cat vault.pem ca.pem > vault-combined.pem 将 vault 的证书和 ca 证书合并。

这时我们应该有一组 ca-key.pemvault-combined.pem

在 k8s 上部署

假设我们已经有一套 k8s 在线,而且可以使用 kubectl 命令控制。

  1. 先将 ca 证书, vault 的证书和秘钥写入 k8s 的 secret 中。
kubectl create secret generic vault-tls \
--from-file=ca.pem \
--from-file=vault.pem=vault-combined.pem \
--from-file=vault-key.pem
  1. 创建 vault 配置文件 vault.hcl。如果是开发环境,不需要高可用(HA),我会推荐使用 mysql 作为存储服务配置,例如 vault-mysql.hcl。 如果是线上环境,我推荐使用支持 高可用(HA) 的存储层,例如 Google Cloud Storage vault-gcs.hcl
  • 可以在 gcloudconsole 中使用 gsutil mb gs://vault_storage_bucket_name 命令创建 gcs 的 bucket。
  1. 将 vault 的配置文件 vault.hcl 写入 k8s 的 configmap。
kubectl create secret generic vault-config \
--from-file=vault.hcl
  1. 创建一个 k8s 服务配置文件 vault.yml ,例如开发测试环境配置 vault-stage.yml,或者生产环境 vault-prod.yml

  2. 将域名配置写入 configmap。

kubectl create configmap vault \
--from-literal \
api-addr=https://vault.of.your.custom.domain.name.com:8200
  1. 如果使用了 gcs ,将谷歌的 service-account-cert.json,例如 vault-service-account-cert-example.json (这个文件是 gcp 平台上生成的)。写入 secret 。
kubectl create secret generic vault-gcs \
  --from-file=service-account-cert.json
  1. 启动 vault 服务

kubectl apply -f vault.yaml

初始化 vault

vault 在初始化之前是不能正常服务,而且面向 k8s 的健康检查服务也会显示不正常。所以要先登录初始化。

  1. 通过 kubectl get pods 找到 vault 服务的 pod id。如果是按前面开发测试(stage)环境的配置,这个 pod id 应该是 vault-0。如果是生产环境,应该是 vault- 后一串随机的 id。 通过 kubectl 登录进入这个pod的命令行:

kubectl exec -it vault-0 -- /bin/sh

  1. 初始化 vault。

vault operator init -ca-cert=/etc/vault/tls/ca.pem

初始化 vault 的操作只有第一次创建部署 vault 时。会生成5个 root tokens。一定要保存好。初始化成功之后,就不需要也不会再运行 init 的操作。

  1. unseal vault。这个操作使用初始化时生成的 root tokens 中的任意3个,在每次 vault 集群启动后都要运行。

vault operator unseal -ca-cert=/etc/vault/tls/ca.pem

至此完成 vault 服务的部署。

使用 vault

完成 vault 的初始化(init)和 unseal 之后。 k8s 集群应该就会显示 vault 服务健康状态正常,并开始提供服务。这时就可以在任何一个可以访问到这个 vault 服务的实例上,使用 vault 命令行,或者在代码中使用 vault 的库来存取 vault 数据。

  1. 使用命令行,先配置环境变量
export VAULT_ADDR=https://vault.of.your.custom.domain.name.com:8200
export VAULT_CACERT=./ca.pem
export VAULT_TOKEN=your-vault-token
  1. 创建一个 policy 配置,例如 vault-policy-example.hcl。并通过 vault 命令行创建这个 policy。
export VAULT_ADDR=https://vault.of.your.custom.domain.name.com:8200 export VAULT_CACERT=./ca.pem
export VAULT_TOKEN=your-vault-token
  1. 在 secret/example 下写入一个键值。例如写入一个文件或 key。
vault kv put secret/example/foobar \
[email protected] \
gcloud_apikey="your-gcloud-api-key"
  1. 创建一个 policy 配置,例如一个 secret/example/* 下的只读策略, vault-policy-example-readonly.hcl。并通过 vault 命令行创建这个 policy。

policy write example-readonly vault-policy-example-readonly.hcl

  1. 为这个 policy 创建一个 token。这个 token 仅有读取这个 policy 下的键值权限。因此更适合应用中使用。

vault token create -policy=example-readonly

  1. 之后我们就可以在代码中通过这个 readonly 的 token 来读路径下的键值。假设我们使用 golang。
...

import (
	...
	vault "github.com/hashicorp/vault/api"
)

...
// 配置 vault api 的地址
vaultAddr := os.Getenv("VAULT_API_ADDR")
config := &vault.Config{
	Address: vaultAddr,
}

// 配置 vault 的 CA Cert
if err := config.ConfigureTLS(&vault.TLSConfig{
	CACert: os.Getenv("CA_CERT"),
}); err != nil {
	log.Fatalf("failed to configure vault tls: %v", err)
}
vaultClient, err := vault.NewClient(config)
if err != nil {
	log.Fatalf("failed to init vault client %s: %v", vaultAddr, err)
}
// 配置 vault 的 token
vaultClient.SetToken(os.Getenv("VAULT_TOKEN"))

keyName := "secret/example/foobar"
secretValues, err := vaultClient.Logical().Read(keyName)
if err != nil {
	log.Fatalf("failed to read vault secret %s: %v", keyName, err)
}

// 列印配置信息
fmt.Println(secretValues.Data["gcloud_apikey"].(string))
fmt.Println(secretValues.Data["grpc_tls_cert"].(string))

维护

维护上要注意的是, token 和 证书都是有过期时间。到期前要 renew 和重新配置新证书。可以通过命令行 renew:

vault token renew your-vault-token

如果 token 所属的 policy 有 /auth/token/renew-self 相应的权限。那么也可以亦可以代码中自己 renew 自己。那么就能保证活跃的 token 一直有效。而不活跃的 token ,即使被忘记了,一段时间后也会失效。


这些就是我在 k8s 集群上使用 vault 的方式了。

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages