跳转到主要内容

Spring Cloud Vault Config允许您的Spring应用程序从Hashicorp Vault检索配置数据。此外,您可以让Vault管理应用程序的数据库帐户。

这是通过名为“数据库”的秘密后端实现的,该后端允许Vault为每个请求访问的实例动态创建数据库帐户。在本文中,我们将讨论为什么这可能是您想要的,以及如何在SpringCloudVault配置中使用它。

为什么你应该关心

凭据是敏感信息,无论其管理方式如何。因此,减少凭证受损的影响并增加攻击者的负担是解决凭证相关问题的几种方法之一。

对于机器使用的帐户,很难实现真正适用于人类帐户、交互式确认或多因素身份验证的好方法。

减少凭证丢失影响的一种方法是限制其使用期限:使用期限已过的被盗凭证毫无价值(希望看看你,密码轮换要求和“password-7”、“password-8”…)。

除了限制凭证可以使用的时间外,还可以限制可接受的使用量。如果一次性代币已经用完,那么事后再偷是没有意义的。

另一个声明的选项增加了攻击者获取凭据的难度,可以通过多种方式实现。将凭据保存在文件中可能比仅保存在内存中风险更大。这并不是因为攻击者不可能在运行的应用程序内存中四处窥探,而是因为发送一个充满凭据的文件更容易。或者,上帝禁止,将其推到GitHub或Pastebin。

所以,有一种方法为每个应用程序实例自动创建一个单独的帐户是不是很好?

在下一节中,我们将了解Vault如何尝试解决此问题,然后我们将通过一个示例应用程序来了解如何防止上榜。

Vault如何解决此问题

Vault具有机密引擎的概念,这意味着它可以使用这些组件来管理机密。这些包括一个集成的CA、一个键值存储和(我们将使用的)数据库机密引擎。

当一个经过身份验证的实体(例如后端应用程序的实例)请求数据库访问并获得授权时,该机密引擎将创建一个具有相应生存期和访问权限的数据库用户和密码。该请求和创建过程如下图所示。

如果已配置,则凭据的有效期有限,如果不续订,则会很快过期。对于用于与vault通信的令牌以及数据库的凭据,存在此续订过程。这些刷新机制如下所示。

这种初始身份验证通常是通过提供某种令牌来实现的,我们将在稍后讨论。这里的重要部分是,此令牌用于访问系统中的其他组件,从而允许对允许的组件进行严格控制。

另一个值得一提的概念是令牌层次结构。如果某个令牌被认为已损坏,则可以将其撤销。这也会撤销它产生的所有其他令牌。

想象一下,部署后端应用程序的一个CI/CD系统被破坏了。使用令牌层次结构,您可以撤销由该系统创建的所有令牌,例如用于实例最初使用Vault进行身份验证的令牌。现在,这些权限也会被撤销,导致受影响实例的所有访问权限(包括数据库访问权限)在几秒钟内被撤销。

当然,这需要您能够处理部分生产环境失去对所有内容的访问所带来的后果。

让我们构建这个

我们需要访问Vault安装和数据库。我们的演示应用程序和Vault需要能够访问数据库。

由于在开发模式下运行vault很简单,所以我们将遵循这一路线。请注意,这不会保留您的设置。

基础设施

我提供了一个docker compose文件,可以为您设置vault和postgres。

Link

只需使用

docker compose up

看着码头工人做自己的事。当一切都在运行时,vault将打印一个根令牌。把它写下来,以后你会需要的。

如果需要,可以使用此令牌登录到web uihttp://127.0.0.1:8200/。此外,现在应该可以从主机访问数据库,如果您安装了postgres客户端,请继续使用

psql -h 127.0.0.1 -U postgres

以及docker compose文件中设置的密码。

这是基础设施的问题,请不要重新启动vault,因为所有数据都只存储在内存中。

设置Vault

我们需要告诉vault有关postgres数据库的信息,因此我们需要创建一个“数据库”类型的秘密引擎。

与其使用笨拙的UI,不如使用cli。在与vault交互之前,需要登录vault。设置保管库地址,并使用我之前告诉您的根令牌登录。

export VAULT_ADDR='http://127.0.0.1:8200'

vault登录████████████████████████

现在我们可以创建秘密引擎了。您可以在infra/vault下的示例存储库中找到完整文件。使用的JSON如下:

{ ...
  "allowed_roles": "quotes_readonly",
  "connection_url": "postgresql://{{username}}:{{password}}@db:5432/postgres?sslmode=disable",
  "username": "postgres",
  "password": "supersecure",
... }

Allowed_roles定义允许使用此数据库连接的角色(稍后创建)。此处使用的帐户必须能够创建具有允许角色所需权限的新帐户。我们进一步禁用了连接的TLS,因为证书管理超出了此帖子的范围。

使用以下命令启用并创建机密引擎。

vault secrets enable database
vault write database/config/postgresql @vault_postgres_connection.json

现在我们可以创建上面提到的角色。

{
  “db_name”: “postgresql”,
  “creation_statements”: “CREATE ROLE \”{{name}}\” WITH LOGIN PASSWORD ‘{{password}}’ VALID UNTIL ‘{{expiration}}’; GRANT SELECT ON public.quotes TO \”{{name}}\”;”,  “default_ttl”: “2m”,
  “max_ttl”: “0”
}

创建语句精确地定义了vault在数据库中应该做什么,撤销和续订也是可能的。在这里,我们定义了使用此角色创建的帐户所具有的数据库权限,可以随意设置一些限制。

default_ttl是此角色的铸造令牌的ttl。可以刷新此令牌,直到达到max_ttl。注意,如果设置了max_ttl,应用程序将不得不请求一个全新的令牌。Spring Cloud Vault Config也不支持这一点,这将导致您的应用程序在一段时间后无法访问数据库。恶心的虫子接踵而至。

创建名为“quotes_readonly”的角色,此名称稍后将被我们的服务使用。

vault write database/roles/quotes_readonly @vault_postgres_role.json

现在我们需要定义策略以允许Spring应用程序使用quotes_readolly数据库角色。

如果您不耐烦,只想看到事情进展,请跳过本节的其余部分,使用根令牌进行身份验证。出于明显的原因,不要在严重部署时这样做。

我们创建了两个策略,一个允许创建新令牌的部署策略和一个允许访问数据库的quote_service策略。

部署策略非常简单:

path “auth/token/create” { capabilities = [“create”, “update”] }

quote_service策略同样简单,我们声明,附加了此策略的令牌可以使用quotes_readoly角色。

path “database/creds/quotes_readonly” { capabilities = [“read”] }

在您使用以下内容创建策略后,我们可以创建一些令牌。

vault policy write quote_service quote_service_policy.hcl
vault policy write deployment deployment_policy.hcl

首先,让我们创建一个将在部署机制中使用的令牌:

vault token create -policy=deployment -policy=quote_service

此令牌具有附加的两个角色。这是由于vault的要求,即每个策略只能创建其自身子策略的令牌。因此,如果我们希望这个令牌能够创建quote_service-tokens,我们需要赋予它角色。这不会影响安全性:如果我们可以为策略创建令牌,那么我们自己没有该策略是没有意义的(更重要的是,因为vault不需要您使用用户或服务帐户的概念)。

记下创建的令牌,以备以后使用。

使用部署令牌,我们可以创建一次性令牌来引导spring应用程序。这些代币被包装起来,这样保险库就可以确保代币还没有用完。因此,作为第一步,应用程序需要将初始引导令牌交换为“真正的”引导令牌。如下所示。

不,让我们继续讨论其他组件。

设置Postgres

在infra/postgres中,我提供了一个脚本来创建所需的表。不要忘记这样做,因为spring应用程序没有权限为您这样做。

编写Spring应用程序

一些样板

在Repo中,我创建了一个实体,一个JpaRepository,以及一个RestController,稍后使用它来展示如何工作,可以自由地使用另一种方法。

Spring Cloud Config Vault依存关系

我使用以下两个依赖项启用使用vault自动配置数据源。

implementation(“org.springframework.cloud:spring-cloud-starter-vault-config:3.0.2”)
implementation(“org.springframework.cloud:spring-cloud-vault-config-databases:3.0.2”)

请注意,spring cloud starter vault配置与默认配置中的spring vault核心(此处未使用,但可用且诱人)冲突,因为它尝试注册一些bean两次。

对于postgres,插入驱动程序:

implementation(“org.postgresql:postgresql”)

现在只剩下一个文件:配置。其余的魔法发生在引擎盖下,

配置

让我们从JPA配置开始。为了防止Spring抱怨缺少特权,我们在这里禁用了自动DDL功能。

唯一重要的部分是jpa.datasource。我们设置了postgres数据库的url,但没有设置用户名和密码参数。这些稍后由Spring Cloud Config Vault注入。

jpa:
hibernate:
ddl-auto: none
database-platform: org.hibernate.dialect.PostgreSQLDialect
datasource:
url: “jdbc:postgresql://127.0.0.1:5432/postgres”
# username injected by cloud config
# password injected by cloud config

不,让我们来看看有趣的部分,Vault的配置。

主机端口和方案很无聊,但需要告诉Spring在哪里找到Vault。在生产环境中,显然不应该使用http。

身份验证定义了身份验证类型,我们使用CUBYHOLE来获得一次性令牌。使用的令牌由令牌字段定义,您很可能希望从其他地方将此属性注入到文件中。稍后将详细介绍。

我们禁用KV存储以防止vault抱怨权限,并启用数据库机密引擎。

这里重要的部分是角色的名称,与前面使用的名称一致。在末尾定义的属性映射到上面配置中留空的属性。

cloud.vault:
  host: 127.0.0.1
  port: 8200
  scheme: http
  authentication: CUBBYHOLE 
  token: s.████████████████████████  
  kv:
    enabled: false
  database:
    enabled: true
    role: quotes_readonly
    backend: database
    username-property: spring.datasource.username
    password-property: spring.datasource.passwordconfig.import: vault://

试驾一下吧

要启动应用程序(或者说让它正确启动),我们需要有一个有效的令牌。

我希望您将部署令牌保存在某处,我们现在可以使用它登录vault。

现在,我们可以为应用程序创建一个新的一次性令牌。为此,我们定义了所需的策略和ttl,其中应用程序必须将令牌交换为新的令牌(由Spring Cloud Config Vault为您完成)。

vault token create -wrap-ttl=”5m” -policy=sample_service

只需将令牌值插入配置中的令牌字段,就可以开始了。使用提供的HTTP端点,您应该能够验证连接是否成功。

幕后

让我们快速看看幕后,好吗?

首先,让我们检查数据库中的用户。使用\du,我们可以看到为应用程序创建的用户。它有一个有效性有限的密码,您会注意到它将在您关闭服务后不久消失。


postgres用户«»我真的很讨厌文本截图,但媒体无法让我正确格式化

如果您对vault上执行的操作感兴趣,例如弹簧自动续订,可以启用并观察审核日志。

为此,对vault容器执行以下操作:

vault audit enable file file_path=/vault/logs/vault_audit.log
apk add jq
tail -f /vault/logs/vault_audit.log|jq ‘.request.path’

我们在这里使用jq来摆脱冗长的json日志记录,并专注于json对象的相关部分。

如果需要,您甚至可以在CLI(或WebUI)中手动撤销租约,以导致数据库连接中的注销。然而,请注意,这不会导致spring应用程序完全取消身份验证,因为它使用了不同的令牌进行了身份验证。如果我们撤销了这一条,所有的访问都将被禁止。

更进一步

那么,我们如何将初始令牌放入应用程序?您可以从大量的身份验证方法中进行选择,比如让Kubernetes为您注入它们,或者让EC2解决它。可能会有关于这些选项的后续帖子。

结论

使用SpringCloudConfigVault访问数据库具有有趣的好处:凭据只存在于运行的应用程序中。这使得意外泄漏的可能性很小,但当然并不能保证系统不会损坏。

值得记住的是,从应用程序和数据库的角度来看,您必须绝对信任Vault。此外,依靠vault来保持帐户的活力意味着,如果您的vault发生故障,您的生产环境就会停止。这可能是不可取的。

在生产中使用vault之前,请注意它是如何实现高可用性的,并始终有办法(或有人)在凌晨4点重新启动并解封它。

下面显示了所有交互,供您参考。

感谢阅读!如果您对该主题有任何疑问、建议或批评,请随时回复或联系我。

文章链接