亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定

如何在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

  1. 密钥管理库代理(代表我们的应用程序)需要从密钥管理库请求一个密钥值。为此,它需要一个有效的密钥管理库令牌,并且该令牌必须关联有正确的策略(myapp-db-policy)。
  2. 要获取令牌,密钥管理库代理必须使用其从Kubernetes ServiceAccountmyapp-sa)获取的Kubernetes凭证(JWT)来与密钥管理库进行身份验证。这为它获取了具有正确访问权限的密钥管理库访问令牌。
  3. 密钥管理库需要通过其API从Kubernetes请求验证密钥管理库代理的凭证。为此,密钥管理库也需要使用自己的凭证。
  4. 与密钥管理库代理一样,密钥管理库也从其自己的ServiceAccount获取凭证,该服务帐户是安装密钥管理库时创建的。(这几乎自动完成,因为我们正在与密钥管理库代理在同一群集中运行Kubernetes。如果我们正在外部运行或在另一个群集中运行Kubernetes,则需要额外的配置)

Vault Agent现在可以访问Vault并检索数据库密钥,这就开始了新的流程。

使用Vault数据库的凭证

  1. Vault Agent 使用之前获取的与 myapp-db-policy 策略关联的 Vault 访问令牌,从 Vault 请求数据库凭证信息。
  2. Vault 意识到没有所需的凭证(或凭证已过期),请求 Postgres 使用 myapp-db-role 中的 SQL 语句创建新凭证。它使用 myapp-db-cnx 连接连接到数据库。
  3. 创建凭证后,将凭证信息回传给 Vault Agent,Vault Agent 将其存储在共享的内存卷中。
  4. 我们的 Spring Boot 应用程序定时检查凭证文件,并在凭证发生变化时更新其数据库连接信息。
  5. 当应用程序需要访问数据库时,它使用 Vault 最后一次创建的凭证进行访问。

你可能认为存在一个时间竞赛,即凭证过期和新凭证创建之间的时间差。你说得没错,但在这个过程中确实有一个宽限期,在此期间,旧的和新的凭证都可以正常使用。你的应用程序需要频繁地检查凭证文件,以确保在旧凭证过期之前,应用程序能够及时更新连接。

你大概已经看出来了,还有很多活儿要干呢!

我们需要这样设置:

  1. 使用 Vault 管理 Postgres 凭据
  2. 用 Vault 验证 Kubernetes 身份
  3. Spring Boot 应用使用轮换的数据库凭证
  4. 部署我们的应用使其正常运行
配置 Vault

这里有一些关于Vault配置管理的快速说明:有三种方式来配置Vault。

  1. 通过其界面/控制台
  2. 通过其命令行接口(CLI)
  3. 通过其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 凭证的保管库

金库必须配置如下:

  1. 一个管理数据库凭证的数据库引擎 (myapp-db)

  2. 与我们数据库的连接 (myapp-db-cnx)

  3. 一个可以创建和管理凭证轮换的数据库角色 (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 创建用户

虽然 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 认证引擎,但还需要做更多的配置。

Vault 代理人

虽然 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: v1
kind: 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数据库。此前,我已经介绍了standaloneconnectedk8s-debug这些配置文件。接下来我们要关注local-cluster配置文件。在这个配置文件中,我们将利用Vault Agent来注入密钥,正如前面提到的。

简单回顾一下。Vault Agent 担任我们 Spring Boot 应用程序的代理角色,向 Vault 请求数据库凭证。Vault Agent 会自动处理凭证的续期和过期。

使用Vault Agent来管理数据库密钥信息

金库代理程序通过挂载到我们应用(Pod)的内存卷将接收到的凭证注入到我们的应用中。从应用的角度看,它会在文件系统中找到所需的凭证。

根据本文介绍的配置,应用通过一个名为 /vault/secrets/myapp-db 的文件来获取其凭据。

首先,我们在应用程序中添加了一个定期检查文件更新的任务。当它检测到变化时,将新凭据应用到我们的数据源上,在我们的骨架里,这个数据源是默认的Hikari类型的数据源。你可以从这个代码片段中看到我们是怎么设置的(请查看文件链接以查看整个文件内容)。

``动态数据库凭据作业(\https://github.com/MartinHodges/spring-boot-k8s-template/blob/main/src/main/java/com/requillion_solutions/sb_k8s_template/config/DatabaseDynamicCredentialsJob.java``)

@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_USERNAMESTATIC_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 作为代理服务器

我希望你从这篇文章中获得乐趣,并至少学到了一点东西。

如果你对这篇文章感兴趣,请给我点赞,这有助于我了解大家觉得哪些内容有用,以及我未来应该写些什么文章。如果你有任何建议,可以在评论区或回复中提出。

點擊查看更多內容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優質文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學習,寫下你的評論
感謝您的支持,我會繼續努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學

大額優惠券免費領

立即參與 放棄機會
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號

舉報

0/150
提交
取消