如何在Kubernetes上使用Vault和Spring Boot為Postgres設置動態憑證
本文将讲解如何使用Hashicorp Vault密钥管理工具在Spring Boot应用中动态添加Postgres凭据,以实现动态Postgres凭据的添加。
使用 Vault 进行机密管理
最近,我在一个Kind Kubernetes集群上创建了一个简单的Spring Boot应用。我将在该代码基础上展示如何使用Vault动态密钥凭证作为Postgres凭证。你可以在该文章的GitHub仓库中找到该应用程序及其所有配置文件。
管理凭证的重要性你的数据库系统无疑是你的基础设施中最重要的系统。它存储了你、你的用户和你的客户的所有数据信息。这也是未经授权的人想要入侵的目标。
很明显,你应该谨慎且安全地控制对你的数据库的访问,确保这种访问是安全且谨慎的。
一种方法是频繁地轮换应用程序使用的凭证(用户名和密码),这样一旦任何凭证泄露,它们也只能在被无效之前使用很短的时间。
本文将探讨如何自动轮转Kubernetes、Vault、Postgres和一个Spring Boot应用中的凭证。
它的工作原理Vault 有一个数据库密钥管理引擎,用于管理可以访问数据库的用户。
在 Postgres 中,用户被称为用户角色,但在本文中我将称其为用户,以避免与解决方案中的其他角色产生混淆。
当你的应用程序需要连接到数据库时,它会向Vault请求用户名和密码。Vault会检查其密钥缓存,如果没有找到数据库的凭证,它会在数据库中创建一个用户账户,将用户名和密码放入其缓存中,然后将其返回给应用程序。
当应用程序下次请求凭证时,Vault 会从缓存中找到它们。只要这些凭证的生存时间(TTL)还未过期,就会返回这些凭证。如果凭证已经过期,将在数据库中创建新的凭证,并删除旧的凭证。缓存中的凭证会被新的凭证替换。
这看起来真不错。认证信息会自动更新,应用对此完全不知情。
不幸的是,当将Spring Boot应用程序与Vault集成时,Spring Cloud Vault框架并没有像你所期望的那样工作。当凭证过期时,除非重启应用程序,否则不会自动更新。这是有意为之,以确保凭证变更不会破坏连接池或影响任何正在进行的事务。
重启应用程序来轮换凭证是一种较为直接的方法,我并不推荐。
另一种解决方案所以,如果我们不用Spring Cloud Vault,还有另一种选择。
我们可以让Vault Agent代我们的应用去获取凭证信息,然后将这些凭证信息插入到应用里面。
Vault Agent 是主应用程序的辅助容器。这意味着它是一个位于 Pod
内的单独容器。它从 Vault 获取凭证,并在凭证到期时自动续期,如您所料。
一旦它拿到凭证后,就会将这些凭证作为文件插入主应用中,通过一个同时被挂接到其容器和Spring Boot应用容器上的卷。
该应用程序现在可以动态读取凭证并用它们来访问数据库。
我们会这样做。
第一步:设置一下吧这里我们需要配置四个部分,如下图所示。
在 Kubernetes 中使用 Vault Agent
- 密钥管理库代理(代表我们的应用程序)需要从密钥管理库请求一个密钥值。为此,它需要一个有效的密钥管理库令牌,并且该令牌必须关联有正确的策略(
myapp-db-policy
)。 - 要获取令牌,密钥管理库代理必须使用其从Kubernetes
ServiceAccount
(myapp-sa
)获取的Kubernetes凭证(JWT)来与密钥管理库进行身份验证。这为它获取了具有正确访问权限的密钥管理库访问令牌。 - 密钥管理库需要通过其API从Kubernetes请求验证密钥管理库代理的凭证。为此,密钥管理库也需要使用自己的凭证。
- 与密钥管理库代理一样,密钥管理库也从其自己的
ServiceAccount
获取凭证,该服务帐户是安装密钥管理库时创建的。(这几乎自动完成,因为我们正在与密钥管理库代理在同一群集中运行Kubernetes。如果我们正在外部运行或在另一个群集中运行Kubernetes,则需要额外的配置)
Vault Agent现在可以访问Vault并检索数据库密钥,这就开始了新的流程。
使用Vault数据库的凭证
- Vault Agent 使用之前获取的与
myapp-db-policy
策略关联的 Vault 访问令牌,从 Vault 请求数据库凭证信息。 - Vault 意识到没有所需的凭证(或凭证已过期),请求 Postgres 使用
myapp-db-role
中的 SQL 语句创建新凭证。它使用myapp-db-cnx
连接连接到数据库。 - 创建凭证后,将凭证信息回传给 Vault Agent,Vault Agent 将其存储在共享的内存卷中。
- 我们的 Spring Boot 应用程序定时检查凭证文件,并在凭证发生变化时更新其数据库连接信息。
- 当应用程序需要访问数据库时,它使用 Vault 最后一次创建的凭证进行访问。
你可能认为存在一个时间竞赛,即凭证过期和新凭证创建之间的时间差。你说得没错,但在这个过程中确实有一个宽限期,在此期间,旧的和新的凭证都可以正常使用。你的应用程序需要频繁地检查凭证文件,以确保在旧凭证过期之前,应用程序能够及时更新连接。
你大概已经看出来了,还有很多活儿要干呢!
我们需要这样设置:
- 使用 Vault 管理 Postgres 凭据
- 用 Vault 验证 Kubernetes 身份
- Spring Boot 应用使用轮换的数据库凭证
- 部署我们的应用使其正常运行
这里有一些关于Vault配置管理的快速说明:有三种方式来配置Vault。
- 通过其界面/控制台
- 通过其命令行接口(CLI)
- 通过其API(应用程序编程接口)
任何配置流程都应该可以重复并且快速进行。这不适合使用控制台。虽然确保配置按预期工作这一点很有帮助,但它需要人工进行无误的点击操作。
使用命令行界面(CLI)可能导致很长且难以管理的命令行。因为Vault镜像我们正在使用的不允许我们写入配置文件到文件系统,因此这些命令行无法被脚本化。CLI的一个优点体现在添加--output-curl-string
选项可以让你获得与API一起使用的curl命令行。请注意,这个选项应该放在命令行的前面,而不是最后。
这使得 API 变得非常有用,让我能够添加文件到我的 GitHub 仓库,你可以在开发电脑的命令行中使用这些文件。
因此,在这篇文章中,我使用了curl
命令和配置文件(如curl命令)来使用API。为此,我们还需要Vault访问令牌。
请留意,我是在基于 Apple Silicon 的 macOS 上进行开发的,下面的命令是针对这台开发机器编写的。
根据你在安装Vault这个工具的过程中收到的访问令牌,设置环境变量。
# 导出VAULT_TOKEN为<ROOT_TOKEN>
export VAULT_TOKEN=<ROOT_TOKEN>
# 将VAULT_TOKEN环境变量设置为<ROOT_TOKEN>,以便后续操作可以使用它。
现在你可以基本上复制粘贴下面给你的命令了,
1. 用于管理 PostgreSQL 凭证的保管库金库必须配置如下:
-
一个管理数据库凭证的数据库引擎 (
myapp-db
) -
与我们数据库的连接 (
myapp-db-cnx
) - 一个可以创建和管理凭证轮换的数据库角色 (
myapp-db-role
)
为了让Vault帮助我们管理数据库认证信息,我们需要启动一个数据库引擎。
当你在 Vault 中启用一个 secrets 引擎时,你需要在某个 挂载点 上进行操作。在这篇文章里,我选择将挂载点设为 myapp-db
。要这样做,我们首先需要为该引擎设置配置。
[**k8s/vault/enable-db-engine.json**](https://github.com/MartinHodges/spring-boot-k8s-template/blob/main/k8s/vault/enable-db-engine.json) 这是一个文件路径,指向GitHub上的一个配置文件
{
"type": "database",
"description": "我的应用程序的数据库引擎",
"config": {
"options": null,
"default_lease_ttl": "1h",
"max_lease_ttl": "24h",
"force_no_cache": "禁止缓存"
},
"local": false,
"seal_wrap": false,
"external_entropy_access": "外部熵访问",
"options": null
}
这将默认的过期时间(TTL)设为1小时,如果在此时间之前不刷新,凭证将过期。它还设置了默认的最大有效时间为从创建时算起的24小时。超过这个时间段后,凭证将无法再刷新,必须重新生成。这些默认设置是可以被修改的,这一点我们将在后面提到。
现在我们让引擎运转起来,
此命令用于启用数据库引擎,并将其挂载到 Vault 中。
curl -X POST -H "X-Vault-Token: ${VAULT_TOKEN}" http://localhost:31400/v1/sys/mounts/myapp-db -d @k8s/vault/enable-db-engine.json
好的,我们现在已经设置好了数据库
引擎。如果你能访问Vault控制台,你应该能在用户界面(UI)的密钥管理引擎部分看到你的引擎。
Vault 需要连接到我们的数据库来创建和删除用户凭证。它通过我们数据库引擎中配置的连接方式来实现。
再次,我们创建一个配置文件,其中包含连接到数据库的配置。如果你跟着做的话,你应该已经在你的Kubernetes集群中设置了一个Postgres集群,并且数据库名为myapp
。这就是我们想要连接的数据库。
[**k8s/vault/myapp-db-cnx.json**](https://github.com/MartinHodges/spring-boot-k8s-template/blob/main/k8s/vault/myapp-db-cnx.json) 点击这里查看文件。这是myapp-db-cnx.json文件的链接。
{
"插件名称": "postgresql-database-plugin",
"允许的角色": "myapp-db-role",
"连接URL": "postgresql://{{username}}:{{password}}@db-cluster-rw.pg.svc:5432/myapp",
"最大打开连接数": 5,
"最大连接生命周期": "5s",
"用户名": <CREATE_USER_USERNAME>,
"密码": <CREATE_USER_PASSWORD>
}
记住,这个连接仅用来创建用户,所以它的生命周期很短,连接数量也很低。我们还限定它只能为 myapp-db-role
创建用户。
请注意,我们将连接字符串设置为数据库集群读写服务的内部DNS地址(数据库集群读写服务的内部DNS地址为db-cluster-rw.pg.svc)。未定义集群名称,这使这些配置可以在不同的集群中通用。
{{username}}
和 {{password}}
字段是 Vault 使用的占位符,用于插入生成的凭证。我们特别关注 myapp
数据库。
你会发现我们有两个未定义的字段,<CREATE_USER_USERNAME>
和 <CREATE_USER_PASSWORD>
。在开发时,你可以使用 postgres
超级用户,但我更倾向于尽早习惯配置生产环境。这样,当我进入生产环境时,我知道自己在做什么。
鉴于这一点,我们将为Vault创建一个名为create_users
的用户。
找到你的数据库 Pod
,可以使用:
运行命令 `kubectl get pods -n pg` 来查看命名空间 pg 中的 pod 列表。
然后获取数据库的Postgres CLI并将其名称从db-cluster-1
改为你的数据库Pod的名称。Pod是数据库的容器实例。
kubectl exec -it db-cluster-1 -n pg -- psql # 进入名为db-cluster-1的数据库集群,并运行psql命令
在 Postgres 中,用户 是具有登录权限的角色。在这种情况下,我们需要用户能登录,所以我们创建一个 user
。如下(将 < > 字段替换为您的值):
创建用户 create_users,密码设置为 '<my super-secret password>',可以创建角色;
授予 create_users 连接到 myapp 数据库的权限;
我们现在可以使用这些凭据在我们的连接文件中。
记得不要将你的认证信息存入代码库,比如GitHub。
将此应用于我们之前启用的Vault数据库引擎的连接设置。请注意,“此”指的是前面提到的内容。
curl -X POST -H "X-Vault-Token: ${VAULT_TOKEN}" http://localhost:31400/v1/myapp-db/config/myapp-db-cnx -d @k8s/vault/myapp-db-cnx.json
注:此命令用于通过Vault API更新数据库连接配置。
我们将这个连接命名为myapp-db-cnx
,这里的myapp-db-cnx
是连接的名字。如果你的数据库权限或凭证有问题的话,连接将无法建立,并且你会看到数据库返回的错误信息。
这一步几乎完成了。你应该在界面上看到连接。最后一步是确保我们的连接用户已妥善设置。
如果你使用的是
postgres
超级管理员,请 切勿 更换密码,否则你可能就会失去访问数据库的权限。
现在,create_users
的密码很可能可以通过配置来访问。Vault 的一个好处就是,Vault 可以自动帮助你轮转这个用户的密码,甚至你自己都没有办法访问——连 Vault 之外的任何人都不行!
在用户界面找到你的连接,然后点击旋转根凭证
按钮。点击后,连接就设置好了,就可以用了。
虽然 Vault 数据库引擎知道如何在 Postgres 数据库中创建用户,我们还需要告诉它如何为我们设置用户。这是因为它不知道我们希望用户如何被设置,特别是在权限设置上。我们通过提供一个 SQL 命令模版来告诉它应该如何操作。
金库数据库引擎依靠数据库角色来创建具有正确权限的用户。这些角色包含了创建用户的SQL命令模板。
让我们创建一个叫 myapp-db-role
的角色,这个角色要符合我们之前在引擎中配置的允许的角色。
首先,我们需要创建一个设置文件来定义Vault的角色。
[**k8s/vault/myapp-db-role.json**](https://github.com/MartinHodges/spring-boot-k8s-template/blob/main/k8s/vault/myapp-db-role.json)(这是一个与myapp数据库角色相关的Kubernetes Vault配置文件)
{
"数据库名称": "myapp-db-cnx",
"创建语句": "CREATE ROLE \"{{name}}\" WITH LOGIN INHERIT PASSWORD '{{password}}' IN ROLE \"app-user\" VALID UNTIL '{{expiration}}';,ALTER USER \"{{name}}\" SET ROLE = \"app-user\";",
"默认有效期": "10m",
"最大有效期": "1h"
}
你可以看到我们是这么做的:
- 引用数据库连接
myapp-db-cnx
。 - 在数据库中的
myapp
架构上授予用户所有权限,并确保使用此用户创建的所有对象的所有者都设置为_app-user_
,因为我们不希望对象由临时用户拥有。 - 覆盖默认 TTL 和最大 TTL 参数。
你可以调整创建语句以符合你的需求。建议在生产环境中,你可能不希望让用户删除任何数据,但这留待日后深入讨论。
现在用这个文件来设置Vault数据库引擎的配置。
curl -X POST -H "X-Vault-Token: ${VAULT_TOKEN}" http://localhost:31400/v1/myapp-db/roles/myapp-db-role -d @k8s/vault/myapp-db-role.json
你现在应该能够使用控制台找到这个角色,并为它生成新的凭证信息。
我们不需要为引擎配置删除用户的功能,因为它知道如何操作。删除过程中不涉及特定应用的信息。不过,Vault 允许你自定义删除规则,如果有特殊要求的话。
你可以试着通过数据库客户端使用租借的权限进行你的操作,至少能用10分钟吧!
这标志着我们的 Vault 和数据库集成现在已经完成了。
2. 密钥库和Kubernetes集成这个解决方案涉及两种身份验证步骤,它们围绕着以下需求展开。
- Vault Agent 请求 Vault 获取密钥值
- Vault 让 Kubernetes 验证 Vault Agent 的身份验证
作为应用程序代理的情况下,Vault Agent 需要能够向 Vault 进行身份验证,以代表应用程序。
在第二种情况中,Vault 需要能够向 Kubernetes 提供认证,以便 Kubernetes 可以验证 Vault Agent 的认证请求。
Vault的代理访问权限
Kubernetes认证引擎在Vault中我们将首先设置Vault和Kubernetes之间的认证流程。重要的是记住,实际需要访问Kubernetes的是Vault中的kubernetes
认证引擎,因为它代理的是从Vault Agent到Kubernetes的认证请求。
首先,我们开启 Kubernetes 认证引擎。我们需要创建配置文件。
[**k8s/vault/enable-k8s-engine.json**](https://github.com/MartinHodges/spring-boot-k8s-template/blob/main/k8s/vault/enable-k8s-engine.json)(GitHub上的文件路径)
{
"type": "kubernetes",
"description": "用于 pod 验证 ServiceAccount 的认证引擎",
"config": {
"options": null,
"default_lease_ttl": "0s",
"max_lease_ttl": "0s",
"force_no_cache": false
},
"local": false,
"seal_wrap": false,
"external_entropy_access": false,
"options": null
}
我们将把它放在默认位置(auth/kubernetes
)。
curl -X POST -H "X-Vault-Token: ${VAULT_TOKEN}" http://localhost:31400/v1/sys/auth/kubernetes -d @k8s/vault/enable-k8s-engine.json
启用 kubernetes
引擎后,我们需要对其进行配置。在 Kubernetes 中运行 Vault 时,它可以从 Kubelet 创建和维护的文件中提取所需的一些值。不过,你还需要指定 Kubernetes API 的位置。接下来,创建文件:
点击这里查看k8s/vault/vault-k8s-config.json文件
{
"kubernetes主机": "https://kubernetes.default.svc.cluster.local:443"
}
接着将配置信息添加到 kubernetes
引擎配置,
curl -X POST -H "X-Vault-Token: ${VAULT_TOKEN}" http://localhost:31400/v1/auth/kubernetes/config -d @k8s/vault/vault-k8s-config.json
我们已经设置了 kubernetes
认证引擎,但还需要做更多的配置。
虽然 kubernetes
认证代理现在可以将认证请求代理到 Kubernetes,但它需要被设置或配置为允许 Vault Agent 访问数据库密钥(secret)。
我们需要将应用的 ServiceAccount
与 Vault 的 secret
连接起来,为此需要提供一个 policy
来控制访问。
首先,我们先创建政策。我们想要创建的政策如下:
政策我们想要创建的是:
path "myapp-db/creds/myapp-db-role" {
capabilities = ["读"]
}
这允许与该策略相关联的令牌读取挂载在 myapp-db
上的 database
引擎内的数据库凭据。
让我们将其转换成可以与curl
一起使用的JSON负载。这样的JSON负载。创建这个文件:
[**k8s/vault/myapp-db-policy.json文件**](https://github.com/MartinHodges/spring-boot-k8s-template/blob/main/k8s/vault/myapp-db-policy.json)
{
"policy":"路径为「myapp-db/creds/myapp-db-role」 {\n 权限为 [「只读」]\n } "
}```
我们现在来定这个政策:
# 此命令用于通过 Vault API 创建或更新权限策略,具体路径为 http://localhost:31400/v1/sys/policies/acl/myapp-db-policy。请确保 ${VAULT_TOKEN} 变量已正确设置。
curl -X POST -H "X-Vault-Token: ${VAULT_TOKEN}" http://localhost:31400/v1/sys/policies/acl/myapp-db-policy -d @k8s/vault/myapp-db-policy.json
# 注意:此处使用的 localhost 地址仅适用于本地开发环境,生产环境中应替换为实际的 Vault 地址。
这创建了一个策略,让我们能够读取数据库凭据。我们现在需要将它与 `kubernetes` 认证引擎关联起来,这样在认证通过后,返回的令牌就包含这个策略。
接着,我们再来创建一个配置文件。
`[**k8s/vault/myapp-k8s-role.json**](https://github.com/MartinHodges/spring-boot-k8s-template/blob/main/k8s/vault/myapp-k8s-role.json) (Kubernetes和Vault之间的一个角色配置文件)`
{
"bound_service_account_names": "myapp-sa",
"bound_service_account_namespaces": "default",
"policies": "myapp-db-policy",
"ttl": "1h"
}
这允许一个 `Pod`(例如:Vault Agent)通过 Vault 的 `kubernetes` 引擎与 Kubernetes 进行身份验证,具体来说,就是 Vault Agent。这里使用的是 `default` 命名空间内的 `myapp-sa` `ServiceAccount` 对 Vault Agent 进行身份验证。一旦身份验证通过后,Vault Agent 接收的 token 会绑定到 `myapp-db-policy` 访问策略上,这个 token 的有效期为 1 小时。
我们接着将这个角色添加到 Vault 的 `kubernetes` 引擎:
# 使用curl命令向Vault发送POST请求,用于创建或更新kubernetes角色 `myapp-k8s-role`。
# 这个命令需要一个有效的Vault令牌 `${VAULT_TOKEN}` 来验证请求。
# `-d @k8s/vault/myapp-k8s-role.json` 参数指定了包含角色配置的JSON文件路径。
curl -X POST -H "X-Vault-Token: ${VAULT_TOKEN}" http://localhost:31400/v1/auth/kubernetes/role/myapp-k8s-role -d @k8s/vault/myapp-k8s-role.json
在我们完成这一步配置之前,创建我们之前提到的 Kubernetes ServiceAccount 很有帮助。将 `“myapp-sa”` 改为 “myapp-sa”。
我们通过Kubernetes做到这一点,Kubernetes需要一个配置文件来运行。
`[**k8s/myapp-service-account.yml**](https://github.com/MartinHodges/spring-boot-k8s-template/blob/main/k8s/myapp-service-account.yml)`
apiVersion: v1kind: ServiceAccount
metadata:
name: myapp-sa
namespace: default
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: myapp-tokenreview-binding
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:auth-delegator
subjects:
- kind: ServiceAccount
name: myapp-sa
namespace: default
我们现在来创建 ServiceAccount
kubectl apply -f k8s/myapp-service-account.yml
执行上述命令,可以应用 k8s/myapp-service-account.yml
文件中的定义。
这就完成了Vault与Kubernetes集成的步骤。接下来,我们需要对我们自己的Spring Boot应用程序进行一些更改。
3. Spring Boot 应用我们已经到了设置Spring Boot应用程序以使用Vault连接的地步。如果你一直跟着我的一系列文章,你现在应该已经有了一个带有REST API的Spring Boot应用程序的模板,该API基于Postgres数据库。此前,我已经介绍了standalone
、connected
和k8s-debug
这些配置文件。接下来我们要关注local-cluster
配置文件。在这个配置文件中,我们将利用Vault Agent来注入密钥,正如前面提到的。
简单回顾一下。Vault Agent 担任我们 Spring Boot 应用程序的代理角色,向 Vault 请求数据库凭证。Vault Agent 会自动处理凭证的续期和过期。
使用Vault Agent来管理数据库密钥信息
金库代理程序通过挂载到我们应用(Pod)的内存卷将接收到的凭证注入到我们的应用中。从应用的角度看,它会在文件系统中找到所需的凭证。
根据本文介绍的配置,应用通过一个名为 /vault/secrets/myapp-db
的文件来获取其凭据。
首先,我们在应用程序中添加了一个定期检查文件更新的任务。当它检测到变化时,将新凭据应用到我们的数据源上,在我们的骨架里,这个数据源是默认的Hikari类型的数据源。你可以从这个代码片段中看到我们是怎么设置的(请查看文件链接以查看整个文件内容)。
@Service
@Slf4j
@RequiredArgsConstructor
@ConditionalOnProperty(prefix = "应用", name = "dynamic-db-config.enabled", havingValue = "true")
public class DatabaseDynamicCredentialsJob {
@Value("${应用动态数据库配置.启用:false}")
boolean dynamicDbEnabled;
@Value("${应用动态数据库配置.文件名}")
String dynamicDbCredentialsFilename;
private final HikariDataSource hikariDataSource;
@Scheduled(
fixedDelayString = "${应用动态数据库配置.刷新:5}",
timeUnit = TimeUnit.MINUTES
)
public void checkForRefreshedCredentials() throws IOException {
...
}
}
从这个片段中可以看到,我们将其设置为一个名为Service
的服务,只有当application.dynamic-db-config.enabled
属性设置为true
时,才会创建这个服务。这使我们能够将此服务与local-cluster
配置文件关联起来,以避免在其他配置文件中覆盖我们的凭证。
你可以看到任务被安排每5分钟触发一次,这个频率可以通过application.dynamic-db-config.refresh
属性进行调整。也就是说,刷新周期应至少是Vault的database
引擎创建的秘密的TTL的两倍。
还有一个属性定义了 Vault Agent 注入的文件名,该文件名为 application.dynamic-db-config.filename
。这些属性需要在 application-local-cluster.yml
文件中设置,而默认情况下,application.yml
文件已设置并禁用了此刷新机制。
在计划任务中,读取文件并与当前的用户名和密码进行比较。如果有更改,则用修订值替换当前的用户名和密码。然后将新的凭据提供给数据库(或数据源)使用,并使用软驱逐法旋转连接池。以确保在断开连接前事务完成。如下代码片段所示:
**配置数据库动态凭据工作**
(源代码路径)
...
boolean refreshed = !StringUtils.equals(username, hikariDataSource.getUsername()) ||
!StringUtils.equals(password, hikariDataSource.getPassword());
if (refreshed) {
log.info("更新数据库登录信息");
hikariDataSource.setUsername(username);
hikariDataSource.setPassword(password);
hikariDataSource.getHikariPoolMXBean().softEvictConnections();
}
...
我们也需要配置凭证注入代码配置。将以下片段添加到您的本地集群
配置中的Spring Boot设置中:
**k8s/application-local-cluster.yml**
(本地集群应用配置文件)
...
应用:
动态数据库配置功能:
启用状态: true
文件路径: "/vault/secrets/myapp-db"
刷新间隔(秒): 5
...
这标志着第3步完成,我们现在有一个Spring Boot应用,当Vault自动轮换数据库凭证时,它会自动更新连接。
4. 部署我们的软件,现阶段,我们已经有了:
- Vault
database
引擎维护和定期轮转的动态数据库凭证 - 可以与 Kubernetes 进行身份验证的 Vault
kubernetes
引擎,用于验证 Vault Agent 凭证 - 允许适当 Vault 访问令牌访问我们数据库凭证的 Vault 策略
- 与适当
ServiceAccount
关联的 Vault Agent 可以访问数据库凭证 - 可以读取、使用和更新 Vault Agent 注入的凭证的 Spring Boot 应用程序
只剩最后一步了,那就是将我们的 Spring Boot 应用程序部署,让它包含 Vault Agent。
你可能还记得我们创建了一个“服务账户”(ServiceAccount
),以便应用程序可以访问 Vault 中的数据库密钥。我们将其命名为 myapp-sa
,并且需要确保应用程序与该服务账户相关联。
如果你没有 local-cluster-deployment.yml
文件,可以复制 k8s-debug.yml
文件来创建它。可以从之前的文章中复制该文件。
在文件中,你可以删除 env
部分里的设置数据库变量的 STATIC_DB_USERNAME
和 STATIC_DB_PASSWORD
,因为我们不再需要这些变量了。将 SPRING_PROFILES_ACTIVE
更改为 local-cluster
。
然后,在定义我们要部署的模板部分中,我们添加以下 annotations
部分。
注意,Vault 使用了 Kubernetes 的一个特性,使 Vault 能在 Pod 创建时得到通知。如果这些 Pod 正确地进行了标注,Vault 便能自动在创建中的 Pod 中添加额外容器。我们不需要手动定义这些容器,因为 Vault 会在后台自动完成。但是,我们需要正确标注这些 Pod,以使 Vault 能执行相应操作。
[**k8s/local-cluster-deployment.yml**](https://github.com/MartinHodges/spring-boot-k8s-template/blob/main/src/main/resources/application-local-cluster.yml)
...
模板:
元数据:
标签:
app: sb-k8s-template
注释:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp-k8s-role"
vault.hashicorp.com/agent-inject-secret-myapp-db: "myapp-db/creds/myapp-db-role"
vault.hashicorp.com/agent-inject-file-secret-myapp-db: "myapp-db.creds"
vault.hashicorp.com/auth-path: "auth/kubernetes"
vault.hashicorp.com/agent-run-as-user: "1881"
vault.hashicorp.com/agent-pre-populate: "true"
vault.hashicorp.com/agent-pre-populate-only: "false"...
这些注释会告诉你以下几点:
agent-inject
: 告诉 Vault 创建 sidecar 以注入密钥信息role
: Vault 在 Agent 身份验证后提供给访问令牌的角色agent-inject-secret-xxx
: Vault 将要注入的密钥 (xxx
) 及其路径agent-inject-file-secret-xxx
: Vault Agent 将要创建的用于密钥xxx
的密钥文件路径auth-path
: 这是将要使用的 Vault 身份验证引擎的路径(auth-path
)agent-run-as-user
: 确保 Vault Agent 容器不以 root 用户运行agent-pre-populate
: 将此设置为true
可确保我们的数据库凭据在应用程序运行之前就可用 (Vault 使用一个 初始化容器(init container) 来实现这一点)agent-pre-populate-only
: 将此设置为false
可确保我们的凭据不仅在启动时更新,还会持续更新 (Vault 使用一个 sidecar 容器(sidecar container) 来实现这一点)
你可以在这里了解更多详情关于这些注释的信息 here。
这标志着部署清单更新的完成,但是,在我们部署应用之前,我们需要对使用的Docker镜像进行一些调整。
Dockerfile 改动如果你一直跟着教程操作,你应该已经有了一个名为 Docker/Dockerfile.k8s.debug
的文件,用于创建你的 Docker 镜像。该文件创建了一个可以让你的应用程序进行调试的镜像。随着我们向生产版本的准备,我们需要创建一个新的版本,禁用远程调试功能。
[链接**Docker/Dockerfile.local.cluster**](https://github.com/MartinHodges/spring-boot-k8s-template/blob/main/Docker/Dockerfile.local.cluster)
FROM openjdk:17.0.2-slim-buster # 从openjdk:17.0.2-slim-buster镜像开始
RUN addgroup --system spring && useradd --system spring -g spring # 创建系统用户和组spring
USER spring:spring # 设置spring用户为当前用户
ARG JAR_FILE=build/libs/*.jar # 定义构建文件的路径参数
COPY ${JAR_FILE} app.jar # 将构建的jar文件复制到镜像中
ENTRYPOINT ["java","-jar","/app.jar"] # 设置容器启动时运行的主进程
EXPOSE 8080 8081 # 开放端口8080和8081
在创建了包含最新配置文件的 JAR 文件后,接下来需要创建 Docker 镜像。
运行以下命令来构建Docker镜像:
docker build -t sb-k8s-template:01 -f Docker/Dockerfile.local.cluster .
构建一个名为sb-k8s-template:01的Docker镜像,使用Docker/Dockerfile.local.cluster作为Dockerfile。
现在上传它到你的 Kind 集群环境:
加载名为sb-k8s-template:01的Docker镜像到kind中
最后使用我们的新部署清单将它部署到集群中:
kubectl apply -f k8s/local-cluster-deployment.yml
# 运行该命令来应用本地集群部署配置文件。
要试试看
部署完应用后,查看日志以确保应用正常运行。
然后你可以尝试添加鱼和鱼缸(参见此文章链接)[https://medium.com/@martin.hodges/creating-a-spring-boot-application-template-to-use-with-your-kubernetes-cluster-1d601eb1f715]。
如果你想看到数据库连接的切换过程,你可以将数据库连接的TTL减少到几分钟,并记住增加应用程序检查数据库连接变化的频率。
如果有任何问题出现我在仓库里增加了一个 WHEN_THINGS_GO_WRONG.md
文件,提供了一些我在这个项目中发现的提示。
希望你顺利读完了这篇比平常长的文章。遗憾的是,为了在我们的 Spring Boot、Kubernetes、Vault 和 Postgres 解决方案中启用动态凭证,需要完成许多步骤,并且这些步骤都需要在它正常运行之前完成。
简单回顾一下,我们在本文中:
咱们在这篇文章里主要是:
- 将 Vault 配置为管理数据库的动态凭证
- 配置 Vault 以使应用程序能够与 Kubernetes 进行身份验证
- 使用策略保护和锁定我们的 Vault 配置
- 修改程序以便于接收轮转的数据库凭证
- 调整了程序的配置与部署,利用 Vault Agent 作为代理服务器
我希望你从这篇文章中获得乐趣,并至少学到了一点东西。
如果你对这篇文章感兴趣,请给我点赞,这有助于我了解大家觉得哪些内容有用,以及我未来应该写些什么文章。如果你有任何建议,可以在评论区或回复中提出。
共同學習,寫下你的評論
評論加載中...
作者其他優質文章