© 2016-2021 原作者。

本文档的副本可以供您自己使用和分发给其他人,前提是您不对此类副本收取任何费用,并且进一步前提是每份副本都包含本版权声明,无论是印刷版还是电子版。

前言

Spring Vault 项目将核心 Spring 概念应用于使用 HashiCorp Vault 开发解决方案。我们提供“模板”作为存储和查询文档的高级抽象。您会注意到与 Spring Framework 中的 REST 支持的相似之处。

本文档是 Spring Vault 的参考指南。它解释了 Vault 的概念和语义以及语法。

这部分参考文档解释了 Spring Vault 提供的核心功能。

Vault 支持引入了 Vault 模块功能集。

1. 文件结构

本节提供 Spring 和 Vault 的基本介绍。它包含有关后续开发以及如何获得支持的详细信息。

文档的其余部分涉及 Spring Vault 功能,并假设用户熟悉HashiCorp Vault 以及 Spring 概念。

2.认识Spring

Spring Vault 使用 Spring 框架的核心功能,例如IoC容器。虽然了解 Spring API 并不重要,但了解它们背​​后的概念才是重要的。对于您选择使用的任何 IoC 容器,至少应该熟悉 IoC 背后的理念。

Vault 支持的核心功能可以直接使用,无需调用 Spring Container 的 IoC 服务。这很像RestTemplate可以在没有任何其他 Spring 容器服务的情况下“独立”使用。要利用 Spring Vault 文档的所有功能,例如会话支持,您需要使用 Spring 配置库的某些部分。

要了解有关 Spring 的更多信息,您可以参考详细解释 Spring 框架的全面(有时是解除武装)文档。有很多关于这个问题的文章、博客条目和书籍——请查看 Spring 框架主页以获取更多信息。

3.认识保险柜

安全性和使用机密是每个使用数据库、用户凭证或 API 密钥的开发人员关心的问题。Vault 通过提供与访问控制、撤销、密钥滚动和审计相结合的安全存储来介入。简而言之:Vault 是一种用于安全访问和存储机密的服务。机密是您想要严格控制访问的任何内容,例如 API 密钥、密码、证书等。

了解 Vault 的起点是www.vaultproject.io。以下是有用资源的列表:

  • 该手册介绍了 Vault 并包含指向入门指南、参考文档和教程的链接。

  • 在线 shell 结合在线教程提供了一种与 Vault 实例交互的便捷方式。

  • HashiCorp Vault 介绍

  • HashiCorp Vault 文档

Spring Vault 为访问、存储和撤销机密提供客户端支持。使用HashiCorp 的 Vault,您可以集中管理所有环境中应用程序的外部机密数据。Vault 可以管理静态和动态机密,例如应用程序数据、远程应用程序/资源的用户名/密码,并为 MySQL、PostgreSQL、Apache Cassandra、Consul、AWS 等外部服务提供凭证。

4. 要求

Spring Vault 2.x 二进制文件需要 JDK 级别 8.0 及以上,以及Spring Framework 5.3.4 及以上。

就Vault而言,Vault至少0.6。

5. 其他帮助资源

学习一个新的框架并不总是直截了当的。在本节中,我们尝试提供我们认为易于遵循的 Spring Vault 模块入门指南。但是,如果您遇到问题或只是寻求建议,请随时使用以下链接之一:

5.1。支持

有几个可用的支持选项:

5.1.1。社区论坛

在Stackoverflow上发布有关 Spring Vault 的问题,以共享信息并互相帮助。请注意,发布时需要注册。

5.1.2. 专业支持

Spring Vault 和 Spring 背后的公司Pivotal Software, Inc.提供专业的源代码支持和有保证的响应时间。

5.2. 跟随发展

有关 Spring Vault 源代码存储库、夜间构建和快照工件的信息,请参阅Spring Vault 主页您可以通过Stackoverflow上的社区与开发人员互动,帮助 Spring Vault 更好地满足 Spring 社区的需求。如果您遇到错误或想提出改进建议,请在 Spring Vault 问题跟踪器上创建票证。要及时了解 Spring 生态系统中的最新消息和公告,请订阅 Spring 社区门户。最后,您可以在 Twitter ( SpringCentral )上关注 Spring博客或项目团队。

6.新的和值得注意的

6.1。Spring Vault 2.3 中的新增功能

  • 支持用于密钥库和信任库的 PEM 编码证书。

  • ReactiveVaultEndpointProvider用于非阻塞查找VaultEndpoint.

  • VaultKeyValueMetadataOperations用于键值元数据交互。

  • 支持transform后端(企业功能)。

  • 有关如何使用 Vault 机密后端的文档。

  • 每次登录尝试都会重新加载 Kubernetes 和 PCF 身份验证的登录凭据。

  • SecretLeaseContainer发布SecretLeaseRotatedEvent而不是成功SecretLeaseExpiredEventSecretLeaseCreatedEvent秘密轮换。

  • AbstractVaultConfiguration.threadPoolTaskScheduler()bean 类型更改为TaskSchedulerWrapper而不是ThreadPoolTaskScheduler.

6.2. Spring Vault 2.2 中的新功能

  • 通过@VaultPropertySource.

  • SpEL 支持@Secret.

  • 添加对 Jetty 作为响应式 HttpClient 的支持。

  • LifecycleAwareSessionManager现在ReactiveLifecycleAwareSessionManager发出AuthenticationEvent

  • PCF 认证

  • 弃用AppIdAuthentication. AppRoleAuthentication按照 HashiCorp Vault 的建议使用。

  • CubbyholeAuthentication并且包装AppRoleAuthentication现在sys/wrapping/unwrap默认使用端点。

  • Kotlin 协程支持ReactiveVaultOperations.

6.3. Spring Vault 2.1 中的新增功能

  • GCP 计算GCP IAMAzure身份验证。

  • 模板 API 支持版本化和非版本化的键/值后端以及 Vault 包装操作。

  • 在反应式 AppRole 身份验证中支持完全拉取模式。

  • 改进了 Vault 登录失败的异常层次结构。

6.4. Spring Vault 2.0 中的新功能

6.5。Spring Vault 1.1.0 中的新增功能

6.6. Spring Vault 1.0 中的新功能

  • 初始保险柜支持。

参考文档

7.保险库支持

Vault 支持包含广泛的功能,总结如下。

  • 使用基于 Java 的 @Configuration 类的 Spring 配置支持

  • VaultTemplate帮助程序类,可提高执行常见 Vault 操作的生产力。包括 Vault 响应和 POJO 之间的集成对象映射。

对于大多数任务,您会发现自己使用VaultTemplate的是利用丰富的通信功能。VaultTemplate是寻找访问功能的地方,例如从 Vault 读取数据或发出管理命令。VaultTemplate还提供回调方法,以便您轻松掌握低级 API 工件,例如RestTemplate直接与 Vault 通信。

7.1。依赖项

查找 Spring Vault 依赖项的兼容版本的最简单方法是依靠我们附带定义的兼容版本的 Spring Vault BOM。在 Maven 项目中,您将在以下 <dependencyManagement />部分声明此依赖项pom.xml

示例 1. 使用 Spring Vault BOM
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.vault</groupId>
      <artifactId>spring-vault-dependencies</artifactId>
      <version>2.3.1</version>
      <scope>import</scope>
      <type>pom</type>
    </dependency>
  </dependencies>
</dependencyManagement>

当前版本是2.3.1. 版本名称遵循以下模式:${version}用于 GA 和服务版本以及${version}-${release}用于快照和里程碑。release可以是以下之一:

  • SNAPSHOT- 当前快照

  • M1M2- 里程碑

  • RC1RC2- 发布候选

示例 2. 声明对 Spring Vault 的依赖
<dependencies>
    <dependency>
        <groupId>org.springframework.vault</groupId>
        <artifactId>spring-vault-core</artifactId>
    </dependency>
</dependencies>

7.2. Spring框架

当前版本的 Spring Vault 需要 5.3.4 或更高版本的 Spring Framework。这些模块也可能与该次要版本的旧错误修复版本一起使用。但是,强烈建议使用该代中的最新版本。

8. 入门

Spring Vault 支持需要 Vault 0.6 或更高版本以及 Java SE 6 或更高版本。引导设置工作环境的一种简单方法是在STS中创建一个基于 Spring 的项目。

首先,您需要设置一个正在运行的 Vault 服务器。有关如何启动 Vault 实例的说明,参阅Vault。

要在 STS 中创建 Spring 项目,请转到 File → New → Spring Template Project → Simple Spring Utility Project → 在出现提示时按 Yes。然后输入一个项目和一个包名,例如org.spring.vault.example.

然后将以下内容添加到pom.xml依赖项部分。

示例 3. 添加 Spring Vault 依赖项
<dependencies>

  <!-- other dependency elements omitted -->

  <dependency>
    <groupId>org.springframework.vault</groupId>
    <artifactId>spring-vault-core</artifactId>
    <version>2.3.1</version>
  </dependency>

</dependencies>

如果您使用的是里程碑或候选发布版本,您还需要将 Spring Milestone 存储库的位置添加到您的 Maven中,该位置与您的元素pom.xml处于同一级别。<dependencies/>

<repositories>
  <repository>
    <id>spring-milestone</id>
    <name>Spring Maven MILESTONE Repository</name>
    <url>https://repo.spring.io/libs-milestone</url>
  </repository>
</repositories>

该存储库也可在此处浏览

如果您使用的是 SNAPSHOT,您还需要将 Spring Snapshot 存储库的位置添加到您的 maven中,该位置与您的元素pom.xml处于同一级别。<dependencies/>

<repositories>
  <repository>
    <id>spring-snapshot</id>
    <name>Spring Maven SNAPSHOT Repository</name>
    <url>https://repo.spring.io/libs-snapshot</url>
  </repository>
</repositories>

该存储库也可在此处浏览

创建一个简单的Secrets类来持久化:

示例 4. 映射的数据对象
package org.spring.vault.example;

public class Secrets {

    String username;
    String password;

    public String getUsername() {
        return username;
    }

    public String getPassword() {
        return password;
    }
}

以及要运行的主要应用程序

示例 5. 使用 Spring Vault 的示例应用程序
package org.springframework.vault.example;

import org.springframework.vault.authentication.TokenAuthentication;
import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.support.VaultResponseSupport;

public class VaultApp {

    public static void main(String[] args) {

        VaultTemplate vaultTemplate = new VaultTemplate(new VaultEndpoint(),
                new TokenAuthentication("00000000-0000-0000-0000-000000000000"));

        Secrets secrets = new Secrets();
        secrets.username = "hello";
        secrets.password = "world";

        vaultTemplate.write("secret/myapp", secrets);

        VaultResponseSupport<Secrets> response = vaultTemplate.read("secret/myapp", Secrets.class);
        System.out.println(response.getData().getUsername());

        vaultTemplate.delete("secret/myapp");
    }
}

即使在这个简单的例子中,也没有什么需要注意的

  • 您可以 VaultTemplate使用org.springframework.vault.client.VaultEndpoint 对象和ClientAuthentication. 您无需启动 Spring Context 即可使用 Spring Vault。

  • Vault 应配置 00000000-0000-0000-0000-000000000000为运行此应用程序的根令牌。

  • 映射器针对标准 POJO 对象工作,不需要任何额外的元数据(尽管您可以选择提供该信息)。

  • 映射约定可以使用字段访问。请注意,Secrets该类只有吸气剂。

  • 如果构造函数参数名称与存储文档的字段名称匹配,则它们将用于实例化对象。

9. VaultTemplate简介

VaultTemplate位于 package中的类org.springframework.vault.core是 Spring 的 Vault 支持的中心类,提供了与 Vault 交互的丰富功能集。该模板提供了在 Vault 中读取、写入和删除数据的便利操作,并提供了域对象和 Vault 数据之间的映射。

配置后,VaultTemplate它是线程安全的,可以跨多个实例重用。

Vault 文档和域类之间的映射是通过委派给 RestTemplate. Spring Web 支持提供了映射基础设施。

该类VaultTemplate实现接口VaultOperations。尽可能地VaultOperations以 Vault API 上可用的方法命名方法,以使 API 为习惯于 API 和 CLI 的现有 Vault 开发人员所熟悉。例如,您会发现“write”、“delete”、“read”和“revoke”等方法。设计目标是尽可能轻松地在使用 Vault API 和VaultOperations. 两个 API 之间的一个主要区别是VaultOperations可以传递域对象而不是 JSON 键值对。

引用实例上的操作的首选方法VaultTemplate是通过其接口VaultOperations

虽然有许多方便的方法VaultTemplate可以帮助您轻松执行常见任务,但如果您需要直接访问 Vault API 以访问未明确公开的功能,VaultTemplate您可以使用几种执行回调方法之一来访问底层 API。执行回调将为您提供对RestOperations对象的引用。有关详细信息,请参阅执行回调部分。

现在让我们看一个如何在 Spring 容器的上下文中使用 Vault 的示例。

9.1。注册和配置 Spring Vault bean

使用 Spring Vault 不需要 Spring 上下文。VaultTemplate但是,在托管上下文中注册的实例SessionManager将参与 Spring IoC 容器提供的生命周期事件。这对于在应用程序关闭时处理活动的 Vault 会话很有用。您还可以从在您的应用程序中重用相同的VaultTemplate 实例中受益。

Spring Vault 带有一个支持配置类,它提供了在 Spring 上下文中使用的 bean 定义。应用程序配置类通常扩展自AbstractVaultConfiguration并需要提供特定于环境的其他详细信息。

扩展AbstractVaultConfiguration需要实现 `VaultEndpoint vaultEndpoint()` 和ClientAuthentication clientAuthentication() 方法。

示例 6. 使用基于 Java 的 bean 元数据注册 Spring Vault 对象
@Configuration
public class AppConfig extends AbstractVaultConfiguration {

    /**
     * Specify an endpoint for connecting to Vault.
     */
    @Override
    public VaultEndpoint vaultEndpoint() {
        return new VaultEndpoint();                            (1)
    }

    /**
     * Configure a client authentication.
     * Please consider a more secure authentication method
     * for production use.
     */
    @Override
    public ClientAuthentication clientAuthentication() {
        return new TokenAuthentication("…");                   (2)
    }
}
1 创建一个新VaultEndpoint的,默认指向https://localhost:8200
2 此示例用于TokenAuthentication快速入门。有关支持的身份验证方法的详细信息,请参阅身份验证方法。
示例 7. 注册 Spring Vault 应用注入的属性
@Configuration
public class AppConfig extends AbstractVaultConfiguration {

    @Value("${vault.uri}")
    URI vaultUri;

    /**
     * Specify an endpoint that was injected as URI.
     */
    @Override
    public VaultEndpoint vaultEndpoint() {
        return VaultEndpoint.from(vaultUri);                          (1)
    }

    /**
     * Configure a Client Certificate authentication.
     * {@link RestOperations} can be obtained from {@link #restOperations()}.
     */
    @Override
    public ClientAuthentication clientAuthentication() {
        return new ClientCertificateAuthentication(restOperations()); (2)
    }
}
1 VaultEndpoint可以使用各种工厂方法构造,例如 from(URI uri)VaultEndpoint.create(String host, int port)
2 ClientAuthentication可以从 AbstractVaultConfiguration您的配置中获取或提供方法的依赖关系。
在某些情况下,创建自定义配置类可能很麻烦。看看EnvironmentVaultConfiguration允许使用来自现有属性源和 Spring 的Environment. 在使用EnvironmentVaultConfiguration中阅读更多内容。

9.2. 会话管理

Spring Vault 需要ClientAuthentication登录和访问 Vault。有关身份验证的详细信息,请参阅身份验证方法。Vault 登录不应发生在每个经过身份验证的 Vault 交互上,但必须在整个会话中重复使用。此方面由 SessionManager实现处理。ASessionManager决定它获得令牌的频率,关于撤销和更新。Spring Vault 带有两个实现:

  • SimpleSessionManager:仅从提供的令牌中获取令牌, ClientAuthentication无需刷新和撤销

  • LifecycleAwareSessionManagerSessionManager如果令牌可更新并在处置时撤销登录令牌,这会安排令牌更新。续订计划使用AsyncTaskExecutor. LifecycleAwareSessionManager 如果使用AbstractVaultConfiguration.

9.3. 使用EnvironmentVaultConfiguration

Spring Vault 包括EnvironmentVaultConfiguration从 Spring 配置 Vault 客户端Environment和一组预定义的属性键。EnvironmentVaultConfiguration支持常用配置。通过从最合适的配置类派生来支持其他配置。包括现有EnvironmentVaultConfiguration@Import(EnvironmentVaultConfiguration.class)基于 Java 的配置类,并通过任何 Spring 的PropertySources 提供配置属性。

示例 8. 将 EnvironmentVaultConfiguration 与属性文件一起使用
基于Java的配置类
@PropertySource("vault.properties")
@Import(EnvironmentVaultConfiguration.class)
public class MyConfiguration{
}
保险库属性
vault.uri=https://localhost:8200
vault.token=00000000-0000-0000-0000-000000000000

属性键

  • 保险柜 URI:vault.uri

  • SSL 配置

    • 密钥库资源:(vault.ssl.key-store可选)

    • 密钥库密码:(vault.ssl.key-store-password可选)

    • 密钥库类型:(vault.ssl.key-store-type可选,通常jks,也支持pem

    • 信任库资源:(vault.ssl.trust-store可选)

    • 信任库密码:(vault.ssl.trust-store-password可选)

    • 信任库类型:(vault.ssl.trust-store-type可选,通常jks,也支持pem

  • 认证方式:(vault.authentication默认为TOKEN,支持的认证方式有:TOKEN, APPID, APPROLE, AWS_EC2, AZURE, CERT, CUBBYHOLE, KUBERNETES

特定于身份验证的属性键

  • 保险柜令牌:vault.token

  • AppId 路径:(vault.app-id.app-id-path默认为app-id

  • 应用编号:vault.app-id.app-id

  • 用户标识:vault.app-id.user-idMAC_ADDRESSIP_ADDRESS使用MacAddressUserId,各自的IpAddressUserId用户ID机制。任何其他值都与 一起使用StaticUserId

  • AppRole 路径:(vault.app-role.app-role-path默认为approle

  • 角色标识:vault.app-role.role-id

  • SecretId:(vault.app-role.secret-id可选)

没有配置选项。

  • 初始保险库令牌:vault.token

  • Kubernetes 路径:(vault.kubernetes.kubernetes-path默认为kubernetes

  • 角色:vault.kubernetes.role

  • 服务帐户令牌文件的路径:(vault.kubernetes.service-account-token-file默认为/var/run/secrets/kubernetes.io/serviceaccount/token

9.4。执行回调

所有 Spring 模板类的一个共同设计特征是所有功能都路由到模板执行回调方法之一。这有助于确保异常和任何可能需要的资源管理执行的一致性。虽然这在 JDBC 和 JMS 的情况下比在 Vault 中更需要,但它仍然为访问和日志记录提供了一个单一的位置。因此,使用执行回调是访问 Vault API 以执行我们未作为方法公开的不常见操作的首选方式VaultTemplate

这是执行回调方法的列表。

  • <T> T doWithVault (RestOperationsCallback<T> callback)执行给定的 ,允许在不需要会话的情况下RestOperationsCallback与 Vault 交互。RestOperations

  • <T> T doWithSession (RestOperationsCallback<T> callback)执行给定的 RestOperationsCallback,允许在经过身份验证的会话中与 Vault 交互。

下面是一个使用ClientCallback来初始化 Vault 的示例:

vaultOperations.doWithVault(new RestOperationsCallback<VaultInitializationResponse>() {

  @Override
  public VaultInitializationResponse doWithRestOperations(RestOperations restOperations) {

    ResponseEntity<VaultInitializationResponse> exchange = restOperations
                       .exchange("/sys/init", HttpMethod.PUT,
                                 new HttpEntity<Object>(request),
                                 VaultInitializationResponse.class);

    return exchange.getBody();
    }
});

10. 支持Vault的秘密引擎

Spring Vault 附带了几个扩展来支持 Vault 的各种秘密引擎。

具体来说,Spring Vault 附带以下扩展:

VaultTemplate您可以通过直接 ( VaultTemplate.read(…), VaultTemplate.write(…))上的方法使用所有其他后端。

10.1。键值版本 1(“未版本化的秘密”)

秘密引擎用于在kv为 Vault 配置的物理存储中存储任意秘密。

kv以非版本化方式运行秘密引擎时,仅保留最近写入的密钥值。非版本化 kv 的好处是减少了每个密钥的存储大小,因为没有存储额外的元数据或历史记录。此外,以这种方式配置的发送到后端的请求性能更高,因为存储调用更少,并且任何给定请求都没有锁定。

Spring Vault 附带一个专用的 Key-Value API 来封装各个 Key-Value API 实现之间的差异。 VaultKeyValueOperations遵循 Vault CLI 设计。这是 Vault 的主要命令行工具,提供诸如 等vault kv get命令vault kv put

通过指定版本和挂载路径,您可以将此 API 与 Key-Value 引擎版本一起使用。以下示例使用 Key-Value 版本 1:

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultKeyValueOperations keyValueOperations = operations.opsForKeyValue("secret",
							VaultKeyValueOperationsSupport.KeyValueBackend.KV_1);

keyValueOperations.put("elvis", Collections.singletonMap("password", "409-52-2002"));

VaultResponse read = keyValueOperations.get("elvis");
read.getRequiredData().get("social-security-number");

VaultKeyValueOperations支持所有 Key-Value 操作,例如put, get, delete, list.

或者,由于其直接映射和简单使用,可以通过 APIVaultTemplate使用,因为键和响应直接映射到输入和输出键。以下示例说明了在 处写入和读取密钥mykey。秘密kv引擎安装在secret

VaultOperations operations = new VaultTemplate(new VaultEndpoint());

operations.write("secret/elvis", Collections.singletonMap("social-security-number", "409-52-2002"));

VaultResponse read = operations.read("secret/elvis");
read.getRequiredData().get("social-security-number");

您可以在 Vault 参考文档中找到有关Vault Key-Value 版本 1 API的更多详细信息。

10.2. 键值版本 2(“版本化机密”)

您可以kv在两个版本之一中运行机密引擎。本节介绍如何使用版本 2。运行kv后端的版本 2 时,密钥可以保留可配置数量的版本。您可以检索旧版本的元数据和数据。此外,您可以使用检查和设置操作来避免无意覆盖数据。

Key-Value Version 1 (“unversioned secrets”)类似,Spring Vault 附带了一个专用的 Key-Value API 来封装各个 Key-Value API 实现之间的差异。Spring Vault 附带一个专用的 Key-Value API 来封装各个 Key-Value API 实现之间的差异。 VaultKeyValueOperations遵循 Vault CLI 设计。这是 Vault 的主要命令行工具,提供诸如vault kv get、等命令vault kv put

通过指定版本和挂载路径,您可以将此 API 与 Key-Value 引擎版本一起使用。以下示例使用 Key-Value 版本 2:

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultKeyValueOperations keyValueOperations = operations.opsForKeyValue("secret",
							VaultKeyValueOperationsSupport.KeyValueBackend.KV_2);

keyValueOperations.put("elvis", Collections.singletonMap("social-security-number", "409-52-2002"));

VaultResponse read = keyValueOperations.get("elvis");
read.getRequiredData().get("social-security-number");

VaultKeyValueOperations支持所有 Key-Value 操作,例如put, get, delete, list.

您还可以与版本化键值 API 的细节进行交互。如果您想获取特定机密或需要访问元数据,这将非常有用。

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultVersionedKeyValueOperations versionedOperations = operations.opsForVersionedKeyValue("secret");

Versioned.Metadata metadata = versionedOperations.put("elvis",							(1)
					Collections.singletonMap("social-security-number", "409-52-2002"));

Version version = metadata.getVersion();												(2)

Versioned<Object> ssn = versionedOperations.get("elvis", Version.from(42));				(3)

Versioned<SocialSecurityNumber> mappedSsn = versionedOperations.get("elvis",			(4)
											Version.from(42), SocialSecurityNumber.class);

Versioned<Map<String,String>> versioned = Versioned.create(Collections					(5)
						.singletonMap("social-security-number", "409-52-2002"),
						Version.from(42));

versionedOperations.put("elvis", version);
1 将秘密存储在坐骑elvis下方可用的位置。secret/
2 将数据存储在版本化后端会返回元数据,例如版本号。
3 版本化的键值 API 允许检索由版本号标识的特定版本。
4 版本化的键值秘密可以映射到值对象中。
5 使用 CAS 更新版本化机密时,输入必须引用先前获得的版本。

虽然可以使用kvv2 机密引擎VaultTemplate。这不是最方便的方法,因为 API 为上下文路径和输入/输出的表示方式提供了不同的方法。具体来说,与实际秘密的交互需要对数据部分进行包装和解包,并data/在挂载和秘密密钥之间引入一个路径段。

VaultOperations operations = new VaultTemplate(new VaultEndpoint());

operations.write("secret/data/elvis", Collections.singletonMap("data",
			Collections.singletonMap("social-security-number", "409-52-2002")));

VaultResponse read = operations.read("secret/data/ykey");
Map<String,String> data = (Map<String, String>) read.getRequiredData().get("data");
data.get("social-security-number");

您可以在 Vault 参考文档中找到有关Vault Key-Value 版本 2 API的更多详细信息。

10.3. PKI(公钥基础设施)

pki秘密引擎通过实现证书颁发机构操作来代表证书的后端。

PKI 机密引擎生成动态 X.509 证书。使用这个秘密引擎,服务可以获得证书,而无需通过通常的手动过程来生成私钥和 CSR,提交给 CA,并等待验证和签名过程完成。Vault 的内置身份验证和授权机制提供了验证功能。

Spring Vault 支持通过VaultPkiOperations. 所有其他 PKI 功能都可以通过VaultOperations.

以下示例简要说明了如何颁发和撤销证书的使用:

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultPkiOperations pkiOperations = operations.opsForPki("pki");

VaultCertificateRequest request = VaultCertificateRequest.builder()								(1)
			.ttl(Duration.ofHours(48))
			.altNames(Arrays.asList("prod.dc-1.example.com", "prod.dc-2.example.com"))
			.withIpSubjectAltName("1.2.3.4")
			.commonName("hello.example.com")
			.build();

VaultCertificateResponse response = pkiOperations.issueCertificate("production", request); 		(2)
CertificateBundle certificateBundle = response.getRequiredData();

KeyStore keyStore = certificateBundle.createKeyStore("my-keystore");							(3)

KeySpec privateKey = certificateBundle.getPrivateKeySpec();										(4)
X509Certificate certificate = certificateBundle.getX509Certificate();
X509Certificate caCertificate = certificateBundle.getX509IssuerCertificate();

pkiOperations.revoke(certificateBundle.getSerialNumber());										(5)
1 使用构建器构造证书请求VaultCertificateRequest
2 从 Vault 请求证书。Vault 充当证书颁发机构,并使用签名的 X.509 证书进行响应。实际响应是CertificateBundle.
3 您可以直接获取生成的证书作为包含公钥和私钥以及颁发者证书的 Java KeyStore。KeyStore 具有广泛的用途,这使得这种格式适合配置(例如 HTTP 客户端、数据库驱动程序或 SSL 安全的 HTTP 服务器)。
4 CertificateBundle允许直接通过 Java Cryptography Extension API 访问私钥以及公共证书和颁发者证书。
5 一旦证书不再使用(或被泄露),您可以通过其序列号撤销它。Vault 在其 CRL 中包含已撤销的证书。

您可以在 Vault 参考文档中找到有关Vault PKI 机密 API的更多详细信息。

10.4. 令牌认证后端

此后端是不与实际机密交互的身份验证后端。相反,它允许访问访问令牌管理。您可以在身份验证方法一章中阅读有关基于令牌的身份验证的更多信息。

token身份验证方法是内置的,可在/auth/token. 它允许用户使用令牌进行身份验证,以及创建新令牌、通过令牌撤销机密等。

当任何其他 auth 方法返回一个身份时,Vault 核心调用 token 方法为该身份创建一个新的唯一令牌。

您还可以使用令牌存储绕过任何其他身份验证方法。您可以直接创建令牌,也可以对令牌执行各种其他操作,例如续订和撤销。

Spring Vault 使用这个后端来更新和撤销配置的身份验证方法提供的会话令牌。

以下示例展示了如何在应用程序中请求、更新和撤销 Vault 令牌:

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultTokenOperations tokenOperations = operations.opsForToken();

VaultTokenResponse tokenResponse = tokenOperations.create();                          (1)
VaultToken justAToken = tokenResponse.getToken();

VaultTokenRequest tokenRequest = VaultTokenRequest.builder().withPolicy("policy-for-myapp")
									.displayName("Access tokens for myapp")
									.renewable()
									.ttl(Duration.ofHours(1))
									.build();

VaultTokenResponse appTokenResponse = tokenOperations.create(tokenRequest);          (2)
VaultToken appToken = appTokenResponse.getToken();

tokenOperations.renew(appToken);                                                     (3)

tokenOperations.revoke(appToken);                                                    (4)
1 通过应用角色默认值来创建令牌。
2 使用构建器 API,您可以为要请求的令牌定义细粒度设置。请求一个令牌会返回一个VaultToken,它被用作 Vault 令牌的值对象。
3 您可以通过令牌 API 更新令牌。通常,这是通过SessionManager跟踪 Vault 会话令牌来完成的。
4 如果需要,可以通过令牌 API 撤销令牌。通常,这是通过SessionManager跟踪 Vault 会话令牌来完成的。

您可以在 Vault 参考文档中找到有关Vault Token Auth Method API的更多详细信息。

10.5。中转后端

传输机密引擎处理传输中数据的加密功能。Vault 不存储发送到此机密引擎的数据。它也可以被视为“加密即服务”或“加密即服务”。传输秘密引擎还可以对数据进行签名和验证,生成数据的散列和 HMAC,并充当随机字节源。

传输的主要用例是加密来自应用程序的数据,同时仍将加密数据存储在一些主数据存储中。这减轻了应用程序开发人员正确加密和解​​密的负担,并将负担推到了 Vault 的运营商身上。

Spring Vault 支持广泛的 Transit 操作:

  • 密钥创建

  • 密钥重新配置

  • 加密/解密/重新包装

  • HMAC 计算

  • 签名和签名验证

里面的所有操作transit都以键为中心。Transit 引擎支持密钥的版本控制和多种密钥类型。请注意,密钥类型可能会对可以使用的操作施加限制。

以下示例显示了如何创建密钥以及如何加密和解密数据:

VaultOperations operations = new VaultTemplate(new VaultEndpoint());
VaultTransitOperations transitOperations = operations.opsForTransit("transit");

transitOperations.createKey("my-aes-key", VaultTransitKeyCreationRequest.ofKeyType("aes128-gcm96"));	(1)

String ciphertext = transitOperations.encrypt("my-aes-key", "plaintext to encrypt");					(2)

String plaintext = transitOperations.decrypt("my-aes-key", ciphertext);									(3)
1 首先,我们需要一把钥匙。每个键都需要指定类型。aes128-gcm96支持加密、解密、密钥推导和收敛加密,本例需要加密和解密。
2 接下来,我们加密一个String包含应该加密的纯文本。输入String使用默认值Charset将字符串编码为其二进制表示。请求一个令牌会返回一个VaultToken,它被用作 Vault 令牌的值对象。该encrypt方法返回 Base64 编码的密文,通常以vault:.
3 要将密文解密为纯文本,请调用该decrypt方法。它解密密文并返回String使用默认字符集解码的 a。

前面的示例使用简单的字符串进行加密操作。虽然这是一种简单的方法,但它存在字符集配置错误的风险,并且不是二进制安全的。当纯文本对图像、压缩数据或二进制数据结构等数据使用二进制表示时,需要二进制安全。

要加密和解密二进制数据,请使用可以保存二进制值的对象PlaintextCiphertext值对象:

byte [] plaintext = "plaintext to encrypt".getBytes();

Ciphertext ciphertext = transitOperations.encrypt("my-aes-key", Plaintext.of(plaintext));			(1)

Plaintext decrypttedPlaintext = transitOperations.decrypt("my-aes-key", ciphertext);				(2)
1 假设密钥my-aes-key已经到位,我们正在加密Plaintext对象。作为回报,该encrypt方法返回一个Ciphertext对象。
2 Ciphertext对象可以直接用于解密并返回一个Plaintext对象。

PlaintextCiphertext附带一个上下文对象,VaultTransitContext. 它用于为收敛加密提供 nonce 值,并为使用密钥派生的上下文值提供。

Transit 允许签署纯文本并验证给定纯文本的签名。签名操作需要非对称密钥,通常使用椭圆曲线密码术或 RSA。

签名使用公钥/私钥拆分来确保真实性。
签名者使用其私钥创建签名。否则,任何人都可以以您的名义签署消息。验证者使用公钥部分来验证签名。实际签名通常是哈希值。

在内部,使用私钥计算和加密哈希以创建最终签名。验证解密签名消息,为纯文本计算自己的哈希,并比较两个哈希值以检查签名是否有效。
byte [] plaintext = "plaintext to sign".getBytes();

transitOperations.createKey("my-ed25519-key", VaultTransitKeyCreationRequest.ofKeyType("ed25519"));	(1)

Signature signature = transitOperations.sign("my-ed25519-key", Plaintext.of(plaintext));			(2)

boolean valid = transitOperations.verify("my-ed25519-key", Plaintext.of(plaintext), signature);		(3)
1 签名需要非对称密钥。您可以使用任何椭圆曲线密码术或 RSA 密钥类型。创建密钥后,您就具备了创建签名的所有先决条件。
2 为纯文本消息创建签名。返回Signature的内容包含一个使用 Base64 字符的 ASCII 安全字符串。
3 要验证签名,验证需要一个 Signature 对象和纯文本消息。作为返回值,您可以获得签名是否有效。

您可以在 Vault 参考文档中找到有关Vault Transit Backend的更多详细信息。

11. ReactiveVaultTemplate 简介

本节介绍有关使用 Spring Vault 的反应式编程支持的基本信息。

11.1。什么是反应式编程?

简而言之,反应式编程是关于异步和事件驱动的非阻塞应用程序,并且需要少量线程来垂直扩展(即在 JVM 内)而不是水平扩展(即通过集群)。

反应式应用程序的一个关键方面是背压的概念,它是一种确保生产者不会压倒消费者的机制。例如,在从数据库扩展到 HTTP 响应的反应组件管道中,当 HTTP 连接太慢时,数据存储库也可能减慢或完全停止,直到网络容量释放。

11.2. 反应式 Vault 客户端

Spring Vault 的响应式客户端支持建立在可组合的身份验证步骤和 SpringWebClient通过 Reactor Netty 或 Jetty 的功能之上,它们具有完全非阻塞、事件驱动的 HTTP 客户端。

VaultTokenSupplier它作为提供者公开VaultToken以验证 HTTP 请求并ReactiveVaultOperations作为主要入口点。和SSLVaultEndpoint的核心配置 在各种客户端实现中重复使用。ClientOptions

ReactiveVaultTemplate位于 package中的类org.springframework.vault.core是 Spring 反应式 Vault 支持的中心类,提供了与 Vault 交互的丰富功能集。该模板提供了在 Vault 中读取、写入和删除数据的便利操作,并提供了域对象和 Vault 数据之间的映射。

配置后,ReactiveVaultTemplate它是线程安全的,可以跨多个实例重用。

Vault 文档和域类之间的映射是通过委托 WebClient及其编解码器完成的。

该类ReactiveVaultTemplate实现接口ReactiveVaultOperations。尽可能地ReactiveVaultOperations以 Vault API 上可用的方法命名方法,以使 API 为习惯于 API 和 CLI 的现有 Vault 开发人员所熟悉。例如,您会发现诸如“写入”、“删除”和“读取”之类的方法。设计目标是尽可能轻松地在使用 Vault API 和ReactiveVaultOperations. 两个 API 之间的一个主要区别是ReactiveVaultOperations可以传递域对象而不是 JSON 键值对。

引用实例上的操作的首选方法ReactiveVaultTemplate是通过其接口ReactiveVaultOperations

ReactiveVaultTemplate您可以使用几种执行回调方法之一来访问底层 API未明确公开的功能。执行回调将为您提供对WebClient对象的引用。有关详细信息,请参阅执行回调部分。

现在让我们看一个如何在 Spring 容器的上下文中使用 Vault 的示例。

11.3. 注册和配置 Spring Vault bean

使用 Spring Vault 不需要 Spring 上下文。ReactiveVaultTemplate但是,在托管上下文中注册的实例 VaultTokenSupplier将参与 Spring IoC 容器提供的生命周期事件。这对于在应用程序关闭时处理活动的 Vault 会话很有用。您还可以从在您的应用程序中重用相同的ReactiveVaultTemplate 实例中受益。

Spring Vault 带有一个支持配置类,它提供了在 Spring 上下文中使用的 bean 定义。应用程序配置类通常扩展自AbstractVaultConfiguration并需要提供特定于环境的其他详细信息。

扩展AbstractVaultConfiguration需要实现 `VaultEndpoint vaultEndpoint()` 和ClientAuthentication clientAuthentication() 方法。

示例 9. 使用基于 Java 的 bean 元数据注册 Spring Vault 对象
@Configuration
public class AppConfig extends AbstractReactiveVaultConfiguration {

    /**
     * Specify an endpoint for connecting to Vault.
     */
    @Override
    public VaultEndpoint vaultEndpoint() {
        return new VaultEndpoint();                            (1)
    }

    /**
     * Configure a client authentication.
     * Please consider a more secure authentication method
     * for production use.
     */
    @Override
    public ClientAuthentication clientAuthentication() {
        return new TokenAuthentication("…");                   (2)
    }
}
1 创建一个新VaultEndpoint的,默认指向https://localhost:8200
2 此示例用于TokenAuthentication快速入门。有关支持的身份验证方法的详细信息,请参阅身份验证方法。

11.4. 会话管理

Spring Vault 需要一个令牌来验证 Vault 请求。有关身份验证的详细信息,请参阅身份验证方法。响应式客户端需要一个非阻塞代币供应商,其合约在VaultTokenSupplier. 令牌可以是静态的,也可以通过 声明的身份验证流程获得。Vault 登录不应发生在每个经过身份验证的 Vault 交互上,但会话令牌应在整个会话中保留。这方面由会话管理器实现处理ReactiveSessionManager,例如ReactiveLifecycleAwareSessionManager.

11.5。执行回调

所有 Spring 模板类的一个共同设计特征是所有功能都路由到模板执行回调方法之一。这有助于确保异常和任何可能需要的资源管理执行的一致性。虽然这在 JDBC 和 JMS 的情况下比在 Vault 中更需要,但它仍然为访问和日志记录提供了一个单一的位置。因此,使用执行回调是访问 Vault API 以执行我们未作为方法公开的不常见操作的首选方式ReactiveVaultTemplate

这是执行回调方法的列表。

  • <T> T doWithVault (Function<WebClient, ? extends T> clientCallback)组成一个给定的反应序列WebClient,允许在没有会话上下文的情况下与 Vault 交互。

  • <T> T doWithSession (Function<WebClient, ? extends T> clientCallback)组成一个给定的反应序列WebClient,允许在经过身份验证的会话中与 Vault 交互。

这是一个使用回调初始化 Vault 的示例:

reactiveVaultOperations.doWithVault(webClient -> {

    return webClient.put()
                    .uri("/sys/init")
                    .syncBody(request)
                    .retrieve()
                    .toEntity(VaultInitializationResponse.class);
});

12. Vault 属性源支持

保险柜可以以多种不同的方式使用。一个特定的用例是使用 Vault 来存储加密的属性。Spring Vault 支持 Vault 作为属性源,使用 Spring 的PropertySource 抽象来获取配置属性。

您可以在其他属性源中引用存储在 Vault 中的属性,或将值注入与@Value(…). 在引导需要存储在 Vault 中的数据的 bean 时需要特别注意。AVaultPropertySource必须在那时初始化才能从 Vault 中检索属性。
Spring Boot/Spring Cloud 用户可以受益于Spring Cloud Vault的配置集成,该集成在应用程序启动期间初始化各种属性源。

12.1. 注册VaultPropertySource

Spring Vault 提供了一个VaultPropertySource与 Vault 一起使用来获取属性的方法。它使用嵌套data元素来公开在 Vault 中存储和加密的属性。

ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new VaultPropertySource(vaultTemplate, "secret/my-application"));

在上面的代码中,VaultPropertySource已在搜索中以最高优先级添加。如果它包含一个 'foo' 属性,它将foo在任何 other 中的任何属性之前被检测并返回PropertySourceMutablePropertySources公开了许多允许精确操作属性源集的方法。

12.2. @VaultPropertySource

@VaultPropertySource注解提供了一种方便且声明性的机制,用于将 a 添加到PropertySourceSpringEnvironment 以与@Configuration类一起使用。

@VaultPropertySource采用 Vault 路径,例如secret/my-application 并公开存储在PropertySource. @VaultPropertySource支持与租约相关的秘密的租约更新(即来自mysql后端的凭据)和终端租约到期时的凭据轮换。默认情况下禁用续租。

示例 10. 存储在 Vault 中的属性
{
  // …

  "data": {
    "database": {
      "password": ...
    },
    "user.name": ...,
  }

  // …
}
示例 11. 声明一个@VaultPropertySource
@Configuration
@VaultPropertySource("secret/my-application")
public class AppConfig {

    @Autowired Environment env;

    @Bean
    public TestBean testBean() {
        TestBean testBean = new TestBean();
        testBean.setUser(env.getProperty("user.name"));
        testBean.setPassword(env.getProperty("database.password"));
        return testBean;
    }
}
示例 12.@VaultPropertySource使用凭证轮换和前缀声明 a
@Configuration
@VaultPropertySource(value = "aws/creds/s3-access",
                     propertyNamePrefix = "aws.",
                     renewal = Renewal.ROTATE)
public class AppConfig {
  // provides aws.access_key and aws.secret_key properties
}
generic秘密后端获得的秘密与 TTL ( refresh_interval) 相关联,但与租约 ID 无关。Spring VaultPropertySource在达到其 TTL 时会轮换通用机密。
您可以使用@VaultPropertySource从版本化的 Key-Value 后端获取最新的密钥版本。确保不在data/路径中包含该段。

路径中存在的任何${…​}占位符都会@VaultPropertySource根据已针对环境注册的属性源集进行解析,如以下示例所示:

示例 13. 使用占位符声明@VaultPropertySource路径
@Configuration
@VaultPropertySource(value = "aws/creds/${my.placeholder:fallback/value}",
                     propertyNamePrefix = "aws.",
                     renewal = Renewal.ROTATE)
public class AppConfig {
}

假设my.placeholder存在于已注册的属性源之一(例如,系统属性或环境变量)中,则占位符被解析为相应的值。如果不是,则将fallback/value其用作默认值。如果未指定默认值且无法解析属性,IllegalArgumentException则抛出 an。

@VaultPropertySource在某些情况下,在使用注释时严格控制属性源排序可能是不可能或不切实际的。例如,如果@Configuration上面的类是通过组件扫描注册的,那么很难预测排序。在这种情况下 - 如果覆盖很重要 - 建议用户回退到使用程序化 PropertySource API。有关详细信息,请参见ConfigurableEnvironmentMutablePropertySources

13. 保险库存储库

使用VaultTemplate和映射到 Java 类的响应允许基本的数据操作,如读取、写入和删除。Vault 存储库在 Vault 之上应用 Spring Data 的存储库概念。Vault 存储库公开了基本的 CRUD 功能,并支持使用限制 Id 属性、分页和排序的谓词进行查询派生。

在Spring Data Commons 参考文档 中阅读有关 Spring Data Repositories 的更多信息。参考文档将向您介绍 Spring Data 存储库。

13.1. 用法

要访问存储在 Vault 中的域实体,您可以利用存储库支持来显着简化实施这些实体。

示例 14. 示例凭证实体
@Secret
public class Credentials {

  @Id String id;
  String password;
  String socialSecurityNumber;
  Address address;
}

我们这里有一个非常简单的域对象。请注意,它有一个名为idannotated with 的属性org.springframework.data.annotation.Id和一个@Secret关于其类型的注释。这两个负责创建用于将对象作为 JSON 保存在 Vault 中的实际密钥。

带有注释的属性@Id以及命名id的属性被视为标识符属性。带有注释的人比其他人更受青睐。

下一步是声明一个使用域对象的存储库接口。

Credentials示例 15.实体的基本存储库接口
public interface CredentialsRepository extends CrudRepository<Credentials, String> {

}

随着我们的存储库的扩展CrudRepository,它提供了基本的 CRUD 和查询方法。Vault 存储库需要 Spring Data 组件。确保在您的类路径中包含spring-data-commons和工件。spring-data-keyvalue

实现这一点的最简单方法是设置依赖管理并将工件添加到您的pom.xml

然后将以下内容添加到pom.xml依赖项部分。

示例 16. 使用 Spring Data BOM
<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>org.springframework.data</groupId>
      <artifactId>spring-data-bom</artifactId>
      <version>2020.0.2</version>
      <scope>import</scope>
      <type>pom</type>
    </dependency>
  </dependencies>
</dependencyManagement>

<dependencies>

  <!-- other dependency elements omitted -->

  <dependency>
    <groupId>org.springframework.vault</groupId>
    <artifactId>spring-vault-core</artifactId>
    <version>2.3.1</version>
  </dependency>

  <dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-keyvalue</artifactId>
    <!-- Version inherited from the BOM -->
  </dependency>

</dependencies>

我们需要将它们粘合在一起的东西是相应的 Spring 配置。

示例 17. Vault 存储库的 JavaConfig
@Configuration
@EnableVaultRepositories
public class ApplicationConfig {

  @Bean
  public VaultTemplate vaultTemplate() {
    return new VaultTemplate(…);
  }
}

鉴于上面的设置,我们可以继续并注入CredentialsRepository到我们的组件中。

示例 18. 访问人员实体
@Autowired CredentialsRepository repo;

public void basicCrudOperations() {

  Credentials creds = new Credentials("heisenberg", "327215", "AAA-GG-SSSS");
  rand.setAddress(new Address("308 Negra Arroyo Lane", "Albuquerque", "New Mexico", "87104"));

  repo.save(creds);                                        (1)

  repo.findOne(creds.getId());                             (2)

  repo.count();                                            (3)

  repo.delete(creds);                                      (4)
}
1 Credentials使用密钥模式存储 Vault Hash 内部的属性keyspace/id,在这种情况下credentials/heisenberg,存储在通用机密后端中。
2 使用提供的 id 来检索存储在的对象keyspace/id
3 计算on定义的密钥空间凭据中可用的实体总数。@SecretCredentials
4 从 Vault 中删除给定对象的键。

13.2. 对象到 Vault JSON 映射

Vault 存储库使用 JSON 作为交换格式将对象存储在 Vault 中。JSON 和实体之间的对象映射由VaultConverter. 转换器读取和写入SecretDocument包含来自VaultResponse. VaultResponses 从 Vault 中读取,Jackson 将主体反序列化为MapofStringObject。默认VaultConverter实现读取Map具有嵌套值ListMap对象并将它们转换为实体,反之亦然。

给定Credentials前几节的类型,默认映射如下:

{
  "_class": "org.example.Credentials",                 (1)
  "password", "327215",                                (2)
  "socialSecurityNumber": "AAA-GG-SSSS",
  "address": {                                         (3)
    "street": "308 Negra Arroyo Lane",
    "city": "Albuquerque",
    "state": "New Mexico",
    "zip":"87104"
  }
}
1 _class属性包含在根级别以及任何嵌套接口或抽象类型上。
2 简单属性值按路径映射。
3 复杂类型的属性映射为嵌套对象。
@Id属性必须映射到String
表 1. 默认映射规则
类型 样本 映射值

简单类型
(例如字符串)

字符串名字 = "沃尔特";

名字=“沃尔特”

复杂类型
(例如地址)

地址 address = new Address("308 Negra Arroyo Lane");

地址:{“街道”:“内格拉阿罗约巷 308 号”}


简单类型列表

List<String> 昵称 = asList("walt", "heisenberg");

昵称:[“沃尔特”、“海森堡”]


简单类型图

Map<String, Integer> atts = asMap("age", 51)

atts:{“年龄”:51}


复杂类型列表

List<Address> 地址 = asList(new Address("308…

地址:[{“街道”:“内格拉阿罗约巷 308 号”},…]

Converter您可以通过注册一个in来自定义映射行为VaultCustomConversions。这些转换器可以处理从/到类型的转换,例如,LocalDateSecretDocument 第一个适用于将简单属性和最后一个复杂类型转换为其 JSON 表示。第二个选项提供对结果的完全控制SecretDocument。写入对象Vault 将删除内容并重新创建整个条目,因此未映射的数据将丢失。

13.3. 查询和查询方法

查询方法允许从方法名称自动派生简单查询。Vault 没有查询引擎,但需要直接访问 HTTP 上下文路径。Vault 查询方法将 Vault 的 API 可能性转换为查询。查询方法执行列出上下文路径下的子项,对 Id 应用过滤,可选择使用偏移量/限制限制 Id 流,并在获取结果后应用排序。

示例 19. 示例存储库查询方法
public interface CredentialsRepository extends CrudRepository<Credentials, String> {

  List<Credentials> findByIdStartsWith(String prefix);
}
Vault 存储库的查询方法仅支持对@Id属性进行谓词的查询。

以下是保险柜支持的关键字的概述。

表 2. 查询方法支持的关键字
关键词 样本

After,GreaterThan

findByIdGreaterThan(String id)

GreaterThanEqual

findByIdGreaterThanEqual(String id)

Before,LessThan

findByIdLessThan(String id)

LessThanEqual

findByIdLessThanEqual(String id)

Between

findByIdBetween(String from, String to)

In

findByIdIn(Collection ids)

NotIn

findByIdNotIn(Collection ids)

Like, StartingWith,EndingWith

findByIdLike(String id)

NotLike,IsNotLike

findByIdNotLike(String id)

Containing

findByFirstnameContaining(String id)

NotContaining

findByFirstnameNotContaining(String name)

Regex

findByIdRegex(String id)

(No keyword)

findById(String name)

Not

findByIdNot(String id)

And

findByLastnameAndFirstname

Or

findByLastnameOrFirstname

Is,Equals

findByFirstname, findByFirstnameIs,findByFirstnameEquals

Top,First

findFirst10ByFirstname,findTop5ByFirstname

13.3.1. 排序和分页

查询方法通过在内存中选择从 Vault 上下文路径中检索的子列表(偏移量/限制)ID 来支持排序和分页。与查询方法谓词不同,排序不限于特定字段。在 Id 过滤后应用未分页排序,并且所有生成的秘密都从 Vault 中获取。这样,查询方法只获取也作为结果的一部分返回的结果。

使用分页和排序需要在过滤影响性能的 Id 之前进行秘密提取。即使 Vault 返回的 Id 的自然顺序发生变化,排序和分页也保证返回相同的结果。因此,首先从 Vault 中获取所有 Id,然后应用排序,然后进行过滤和偏移/限制。

示例 20.分页和排序存储库
public interface CredentialsRepository extends PagingAndSortingRepository<Credentials, String> {

  List<Credentials> findTop10ByIdStartsWithOrderBySocialSecurityNumberDesc(String prefix);

  List<Credentials> findByIdStarts(String prefix, Pageable pageRequest);
}

14. 客户支持

Spring Vault 支持各种 HTTP 客户端访问 Vault 的 HTTP API。Spring Vault RestTemplate用作访问 Vault 的主要接口。专用客户端支持源自自定义 SSL 配置 ,该配置仅适用于 Spring Vault 的客户端组件。

Spring Vault 支持以下 HTTP 命令式客户端:

  • Java 的内置HttpURLConnection(默认客户端)

  • Apache Http 组件

  • 网状

  • OkHttp 3

Spring Vault 的响应式集成支持以下响应式 HTTP 客户端:

  • 反应堆网

  • 码头

使用特定客户端需要在类路径上提供相应的依赖项,以便 Spring Vault 可以使用可用客户端与 Vault 进行通信。

14.1. Java的内置HttpURLConnection

Java 的内置HttpURLConnection功能是开箱即用的,无需额外配置。使用HttpURLConnection带有关于 SSL 配置的限制。Spring Vault 不会应用定制的 SSL 配置,因为它需要对 JVM 进行深度重新配置。此配置将影响依赖默认 SSL 上下文的所有组件。使用配置 SSL 设置 HttpURLConnection需要您将这些设置作为系统属性提供。有关详细信息,请参阅 自定义 JSSE

14.2. 外部客户

您可以使用外部客户端访问 Vault 的 API。只需将以下依赖项之一添加到您的项目中。如果使用 Spring Vault 的 Dependency BOM ,则可以省略版本号

示例 21. Apache Http 组件依赖关系
<dependency>
  <groupId>org.apache.httpcomponents</groupId>
  <artifactId>httpclient</artifactId>
</dependency>
可以通过日志配置启用 Apache HttpClient 的有线日志记录。确保不要意外启用线路日志记录,因为日志可能会以纯文本形式公开您的应用程序和 Vault 之间的流量(令牌和机密)。
示例 22. Netty 依赖
<dependency>
  <groupId>io.netty</groupId>
  <artifactId>netty-all</artifactId>
</dependency>
示例 23. 方形 OkHttp 3
<dependency>
  <groupId>com.squareup.okhttp3</groupId>
  <artifactId>okhttp</artifactId>
</dependency>
示例 24. Reactor Netty
<dependency>
  <groupId>io.projectreactor.netty</groupId>
  <artifactId>reactor-netty</artifactId>
</dependency>
示例 25. 码头
<dependency>
  <groupId>org.eclipse.jetty</groupId>
  <artifactId>jetty-reactive-httpclient</artifactId>
</dependency>

14.3. Vault 客户端 SSL 配置

可以SslConfiguration通过设置各种属性来配置 SSL。您可以设置javax.net.ssl.trustStore为配置 JVM 范围的 SSL 设置或配置SslConfiguration 为仅为 Spring Vault 设置 SSL 设置。

SslConfiguration sslConfiguration = SslConfiguration.create(            (1)
		new FileSystemResource("client-cert.jks"), "changeit".toCharArray(),
		new FileSystemResource("truststore.jks"), "changeit".toCharArray());

SslConfiguration.forTrustStore(new FileSystemResource("keystore.jks"),  (2)
                                      "changeit".toCharArray())

SslConfiguration.forKeyStore(new FileSystemResource("keystore.jks"),    (3)
                                      "changeit".toCharArray())

SslConfiguration.forKeyStore(new FileSystemResource("keystore.jks"),    (4)
                                      "changeit".toCharArray(),
                                      KeyConfiguration.of("key-password".toCharArray(),
                                      "my-key-alias"))
1 完整配置。
2 仅配置信任存储设置。
3 仅配置密钥存储设置。
4 通过提供密钥配置仅配置密钥存储设置。

请注意,SslConfiguration仅当 Apache Http 组件或 OkHttp 客户端位于您的类路径上时,才能应用提供。

SSL 配置还支持 PEM 编码证书作为 Java Key Store 的替代方案。

KeyStoreConfiguration keystore = KeyStoreConfiguration
        .of(new ClassPathResource("ca.pem")).withStoreType("PEM");
SslConfiguration configuration = SslConfiguration.forTrustStore(keystore);

PEM 文件可能包含一个或多个证书(-----BEGIN CERTIFICATE-----和的块-----END CERTIFICATE-----)。添加到底层的证书KeyStore使用完整的主题名称作为别名。

15. 认证方法

不同的组织对安全性和身份验证有不同的要求。Vault 通过提供多种身份验证方法来反映这种需求。Spring Vault 支持多种身份验证机制。

15.1. 外部化登录凭据

获得对安全系统的首次访问称为安全引入。任何客户端都需要临时或永久凭据才能访问 Vault。外部化凭证是保持代码可维护性高的一种很好的模式,但存在增加披露的风险。

向任何一方披露登录凭据允许登录到 Vault 并访问底层角色允许的机密。选择适当的客户端身份验证并将凭据注入应用程序需要进行风险评估。

Spring 的PropertySource 抽象非常适合将配置保留在应用程序代码之外。您可以使用系统属性、环境变量或属性文件来存储登录凭据。每种方法都有自己的属性。请记住,命令行和环境属性可以通过适当的操作系统访问级别进行自省。

示例 26. 外部化vault.token到属性文件
@PropertySource("configuration.properties")
@Configuration
public class Config extends AbstractVaultConfiguration {

    @Override
    public ClientAuthentication clientAuthentication() {
        return new TokenAuthentication(getEnvironment().getProperty("vault.token"));
    }
}
Spring 允许多种方式获取Environment. 使用时VaultPropertySource,注入 via@Autowired Environment environment将不会提供,Environment因为环境 bean 仍在构建中,并且自动装配在稍后阶段进行。您的配置类应该实现ApplicationContextAware并获取Environmentfrom ApplicationContext

SecurePropertyUsage.java 有关在组件和其他属性源中引用属性的示例,请参阅。

15.2. 令牌认证

令牌是 Vault 中进行身份验证的核心方法。令牌身份验证需要提供静态令牌。

令牌认证是默认的认证方法。如果令牌被意外泄露,它可以访问 Vault 并可以访问预期客户的机密。

通常,令牌身份验证用于令牌在外部创建和更新的场景(例如HashiCorp Vault 服务代理)。根据实际设置,您可能需要也可能不需要令牌更新和撤销。LifecycleAwareSessionManager有关 TTL 和令牌撤销的详细信息,请参阅。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {
        return new TokenAuthentication("…");
    }

    // …
}

也可以看看:

15.3. AppId 认证

Vault 不推荐使用 AppId 身份验证。请改用AppRole 身份验证

Vault 支持 由两个难以猜测的令牌组成的AppId身份验证。AppId 默认spring.application.name为静态配置。第二个标记是 UserId,它是由应用程序确定的部分,通常与运行时环境有关。IP 地址、Mac 地址或 Docker 容器名称都是很好的例子。Spring Vault 支持 IP 地址、Mac 地址和静态 UserId(例如通过系统属性提供)。IP 和 Mac 地址表示为十六进制编码的 SHA256 哈希。

基于 IP 地址的 UserId 使用本地主机的 IP 地址。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {
        AppIdAuthenticationOptions options = AppIdAuthenticationOptions.builder()
                .appId("myapp")
                .userIdMechanism(new IpAddressUserId())
                .build();

        return new AppIdAuthentication(options, restOperations());
    }

    // …
}

从命令行生成 IP 地址 UserId 的相应命令是:

$回声-n 192.168.99.1 | sha256sum
包含导致不同哈希值的换行符,echo因此请确保包含-n标志。

基于 Mac 地址的 UserId 从本地主机绑定设备获取他们的网络设备。该配置还允许指定network-interface提示以选择正确的设备。的值 network-interface是可选的,可以是接口名称或接口索引(从 0 开始)。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        AppIdAuthenticationOptions options = AppIdAuthenticationOptions.builder()
                .appId("myapp")
                .userIdMechanism(new MacAddressUserId())
                .build();

        return new AppIdAuthentication(options, restOperations());
    }

    // …
}

从命令行生成 Mac 地址 UserId 的相应命令是:

$回声-n 0AFEDE1234AC | sha256sum
Mac 地址指定为大写且不带冒号。包含导致不同哈希值的换行符,echo因此请确保包含-n标志。

15.3.1. 自定义用户 ID

更高级的方法可以让您实现自己的AppIdUserIdMechanism. 此类必须在您的类路径中,并且必须实现org.springframework.vault.authentication.AppIdUserIdMechanism接口和createUserId方法。Spring VaultcreateUserId每次使用 AppId 进行身份验证时都会调用来获取 UserId 以获取令牌。

MyUserIdMechanism.java
public class MyUserIdMechanism implements AppIdUserIdMechanism {

  @Override
  public String createUserId() {

    String userId = …
    return userId;
  }
}

15.4. 应用角色认证

AppRole允许机器身份验证,例如已弃用(自 Vault 0.6.1 起)的AppId authentication。AppRole 身份验证由两个难以猜测的(秘密)令牌组成:RoleId 和 SecretId。

Spring Vault 通过仅提供 RoleId 或与提供的 SecretId 一起提供并从 Vault 获取 RoleId/SecretId 来支持 AppRole 身份验证(具有响应展开的推送和拉取模式)。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
                .roleId(RoleId.provided("…"))
                .secretId(SecretId.wrapped(VaultToken.of("…")))
                .build();

        return new AppRoleAuthentication(options, restOperations());
    }

    // …
}

Spring Vault 还支持完全拉取模式:如果没有提供 RoleId 和 SecretId,Spring Vault 将使用角色名称和初始令牌检索它们。初始令牌可能与 TTL 和使用限制相关联。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        VaultToken initialToken = VaultToken.of("…");
        AppRoleAuthenticationOptions options = AppRoleAuthenticationOptions.builder()
                .appRole("…")
                .roleId(RoleId.pull(initialToken))
                .secretId(SecretId.pull(initialToken))
                .build();

        return new AppRoleAuthentication(options, restOperations());
    }

    // …
}

15.5。AWS-EC2 身份验证

aws-ec2 auth 后端为 AWS EC2 实例提供安全的 引入机制,允许自动检索 Vault 令牌。与大多数 Vault 身份验证后端不同,此后端不需要首先部署或提供对安全敏感的凭据(令牌、用户名/密码、客户端证书等)。相反,它将 AWS 视为受信任的第三方,并使用唯一代表每个 EC2 实例的加密签名动态元数据信息。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {
        return new AwsEc2Authentication(restOperations());
    }

    // …
}

AWS-EC2 身份验证默认启用随机数以遵循首次使用信任 (TOFU) 原则。任何获得 PKCS#7 身份元数据访问权限的非预期方都可以针对 Vault 进行身份验证。

在第一次登录期间,Spring Vault 会生成一个 nonce,该 nonce 存储在 auth 后端的实例 ID 旁边。重新认证需要发送相同的随机数。任何其他方都没有随机数,可以在 Vault 中发出警报以进行进一步调查。

nonce 保存在内存中,并在应用程序重新启动期间丢失。

AWS-EC2 身份验证角色是可选的,默认为 AMI。您可以通过在 中设置身份验证角色来配置身份验证角色AwsEc2AuthenticationOptions

15.6。AWS-IAM 身份验证

aws auth 后端允许使用 现有 AWS IAM 凭证登录 Vault。

AWS IAM 身份验证创建一个签名的 HTTP 请求,由 Vault 执行以使用 AWS STS GetCallerIdentity方法获取签名者的身份。AWSv4 签名需要 IAM 凭证。

IAM 凭证可以从运行时环境中获取,也可以从外部提供。运行时环境(如 AWS-EC2、Lambda 和 ECS)具有分配的 IAM 委托人不需要特定于客户端的凭证配置,但可以从其元数据源获取这些。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        AwsIamAuthenticationOptions options = AwsIamAuthenticationOptions.builder()
                .credentials(new BasicAWSCredentials(…)).build();

        return new AwsIamAuthentication(options, restOperations());
    }

    // …
}
示例 27. 使用 AWS-EC2 实例配置文件作为凭证源
@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        AwsIamAuthenticationOptions options = AwsIamAuthenticationOptions.builder()
                .credentialsProvider(InstanceProfileCredentialsProvider.getInstance()).build();

        return new AwsIamAuthentication(options, restOperations());
    }

    // …
}

AwsIamAuthentication需要 AWS Java 开发工具包依赖项 ( com.amazonaws:aws-java-sdk-core),因为身份验证实施使用 AWS 开发工具包类型进行凭证和请求签名。

您可以通过 配置身份验证AwsIamAuthenticationOptions

也可以看看:

15.7. Azure (MSI) 身份验证

azure auth 后端为 Azure VM 实例提供了一种安全的 引入机制,允许自动检索 Vault 令牌。与大多数 Vault 身份验证后端不同,此后端不需要首先部署或提供对安全敏感的凭据(令牌、用户名/密码、客户端证书等)。相反,它将 Azure 视为受信任的第三方,并使用可绑定到 VM 实例的托管服务标识和实例元数据信息。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        AzureMsiAuthenticationOptions options = AzureMsiAuthenticationOptions.builder()
                    .role(…).build();

        return new AzureMsiAuthentication(options, restOperations());
    }

    // …
}

Azure 身份验证需要有关 VM 环境的详细信息(订阅 ID、资源组名称、VM 名称)。这些详细信息可以通过配置 AzureMsiAuthenticationOptionsBuilder。如果未配置,则AzureMsiAuthentication查询 Azure 的实例元数据服务以获取这些详细信息。

也可以看看:

15.8. GCP-GCE 认证

gcp auth 后端允许使用现有的 GCP(谷歌云平台)IAM 和 GCE 凭证登录保险柜。

GCP GCE(Google Compute Engine)身份验证以 JSON Web Token (JWT) 的形式为服务帐户创建签名。Compute Engine 实例的 JWT 是使用Instance Identification从 GCE 元数据服务获取的。此 API 创建可用于确认实例身份的 JSON Web 令牌。

与大多数 Vault 身份验证后端不同,此后端不需要首先部署或提供对安全敏感的凭据(令牌、用户名/密码、客户端证书等)。相反,它将 GCP 视为受信任的第三方,并使用唯一代表每个 GCP 服务帐户的加密签名动态元数据信息。

您可以通过 配置身份验证GcpComputeAuthenticationOptions

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        GcpComputeAuthenticationOptions options = GcpComputeAuthenticationOptions.builder()
				.role(…).build();

		GcpComputeAuthentication authentication = new GcpComputeAuthentication(options,
				restOperations());
    }

    // …
}

也可以看看:

15.9。GCP-IAM 身份验证

gcp auth 后端允许使用现有的 GCP(谷歌云平台)IAM 和 GCE 凭证登录保险柜。

GCP IAM 身份验证以 JSON Web 令牌 (JWT) 的形式为服务帐户创建签名。服务账户的 JWT 是通过调用 GCP IAM 的projects.serviceAccounts.signJwtAPI 获得的。调用者针对 GCP IAM 进行身份验证并由此证明其身份。此保险柜后端将 GCP 视为受信任的第三方。

IAM 凭证可以从运行时环境中获得,也可以从外部以 JSON 等形式提供。JSON 是首选格式,因为它携带调用所需的项目 ID 和服务帐户标识符 projects.serviceAccounts.signJwt

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        GcpIamAuthenticationOptions options = GcpIamAuthenticationOptions.builder()
				.role(…).credential(GoogleCredentials.getApplicationDefault()).build();

		GcpIamAuthentication authentication = new GcpIamAuthentication(options,
				restOperations());
    }

    // …
}

GcpIamAuthenticationOptions需要 Google Cloud Java SDK 依赖项 (com.google.apis:google-api-services-iamcom.google.auth:google-auth-library-oauth2-http),因为身份验证实现使用 Google API 进行凭据和 JWT 签名。

您可以通过 配置身份验证GcpIamAuthenticationOptions

Google 凭据需要维护令牌生命周期的 OAuth 2 令牌。因此,所有 API 都是同步的,GcpIamAuthentication不支持AuthenticationSteps响应式使用所需的 API。

也可以看看:

15.10。PCF认证

pcf auth 后端允许 PCF 实例的 Vault 登录。它利用了 PCF 的应用程序和容器身份保证

PCF 身份验证使用实例密钥和证书来创建由 Vault 验证的签名。如果签名匹配,并且可能绑定的组织/空间/应用程序 ID 匹配,则 Vault 会发出一个适当范围的令牌。

实例凭证可从文件 atCF_INSTANCE_CERTCF_INSTANCE_KEYvariables 中获得。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        PcfAuthenticationOptions options = PcfAuthenticationOptions.builder()
                .role(…).build();

        PcfAuthentication authentication = new PcfAuthentication(options,
                restOperations());
    }

    // …
}

PcfAuthenticationOptions需要BouncyCastle 库来创建 RSA-PSS 签名。

您可以通过 配置身份验证PcfAuthenticationOptions

也可以看看:

15.11。TLS证书认证

certauth 后端允许使用由 CA 签名或自签名的 SSL/TLS 客户端证书进行身份验证。

要启用cert身份验证,您需要:

  1. 使用 SSL,请参阅Vault 客户端 SSL 配置

  2. 配置Keystore包含客户端证书和私钥的 Java

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        ClientCertificateAuthenticationOptions options = ClientCertificateAuthenticationOptions.builder()
                .path(…).build();

        return new ClientCertificateAuthentication(options, restOperations());
    }

    // …
}

15.12。Cubbyhole 身份验证

Cubbyhole 身份验证使用 Vault 原语来提供安全的身份验证工作流程。Cubbyhole 身份验证使用令牌作为主要登录方法。临时令牌用于从 Vault 的 Cubbyhole 秘密后端获取第二个登录 VaultToken。登录令牌通常寿命较长,用于与 Vault 交互。可以从包装的响应或data部分中检索登录令牌。

创建一个包装的令牌

用于创建令牌的响应包装需要 Vault 0.6.0 或更高版本。
示例 28. 创建和存储令牌
$ vault token-create -wrap-ttl="10m"
Key                            Value
---                            -----
wrapping_token:                397ccb93-ff6c-b17b-9389-380b01ca2645
wrapping_token_ttl:            0h10m0s
wrapping_token_creation_time:  2016-09-18 20:29:48.652957077 +0200 CEST
wrapped_accessor:              46b6aebb-187f-932a-26d7-4f3d86a68319
示例 29. 包装的令牌响应使用
@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        CubbyholeAuthenticationOptions options = CubbyholeAuthenticationOptions
                .builder()
                .initialToken(VaultToken.of("…"))
                .wrapped()
                .build();

        return new CubbyholeAuthentication(options, restOperations());
    }

    // …
}

使用存储的令牌

示例 30. 创建和存储令牌
$ vault token create
Key                    Value
---                    -----
token                  f9e30681-d46a-cdaf-aaa0-2ae0a9ad0819
token_accessor         4eee9bd9-81bb-06d6-af01-723c54a72148
token_duration         0s
token_renewable        false
token_policies         [root]

$ vault token create -use-limit=2 -orphan -no-default-policy -policy=none
Key                    Value
---                    -----
token                  895cb88b-aef4-0e33-ba65-d50007290780
token_accessor         e84b661c-8aa8-2286-b788-f258f30c8325
token_duration         0s
token_renewable        false
token_policies         [none]

$ export VAULT_TOKEN=895cb88b-aef4-0e33-ba65-d50007290780
$ vault write cubbyhole/token token=f9e30681-d46a-cdaf-aaa0-2ae0a9ad0819
示例 31. 存储的令牌响应使用
@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        CubbyholeAuthenticationOptions options = CubbyholeAuthenticationOptions
                .builder()
                .initialToken(VaultToken.of("…"))
                .path("cubbyhole/token")
                .build();

        return new CubbyholeAuthentication(options, restOperations());
    }

    // …
}

剩余 TTL/可再生性

从 Cubbyhole 检索到的与非零 TTL 相关联的令牌在创建令牌时开始其 TTL。该时间不一定与应用程序启动相同。为了补偿初始延迟,Cubbyhole 身份验证对与非零 TTL 关联的令牌执行自我查找,以检索剩余的 TTL。Cubbyhole 身份验证不会在没有 TTL 的情况下自行查找包装的令牌,因为零 TTL 表示没有关联的 TTL。

未包装的令牌不通过仅检索令牌来提供有关可更新性和 TTL 的详细信息。自查找将查找可更新性和剩余的 TTL。

也可以看看:

15.13。Kubernetes 身份验证

自 0.8.3 起,Vault 支持使用 Kubernetes 令牌的基于 kubernetes 的身份验证

使用 Kubernetes 身份验证需要一个 Kubernetes 服务帐户令牌,通常安装在/var/run/secrets/kubernetes.io/serviceaccount/token. 该文件包含读取并发送到 Vault 的令牌。Vault 在登录期间使用 Kubernetes 的 API 验证其有效性。

配置 Kubernetes 身份验证至少需要提供角色名称:

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        KubernetesAuthenticationOptions options = KubernetesAuthenticationOptions.builder()
                .role(…).jwtSupplier(…).build();

        return new KubernetesAuthentication(options, restOperations());
    }

    // …
}

您可以通过 配置身份验证KubernetesAuthenticationOptions

也可以看看:

15.14。认证步骤

ClientAuthentication对象描述身份验证流程并执行实际的身份验证步骤。预先组合的身份验证易于使用,并且可以通过与同步执行的紧密绑定进行配置。

身份验证方法的组合和重用常见步骤(例如将登录有效负载发布到 Vault 或从 HTTP 源检索身份验证输入)不适用于ClientAuthentication对象。

身份验证步骤提供了常见身份验证活动的可重用性。通过以功能样式描述身份验证流程创建的步骤AuthenticationSteps将实际身份验证执行留给特定的执行者。

示例 32. 存储的令牌认证流程。
AuthenticationSteps.just(VaultToken.of(…));                              (1)
1 仅从. AuthenticationSteps_VaultToken

可以从单个输入创建单步身份验证流程。声明多个身份验证步骤的流程以SupplierHttpRequest提供身份验证状态对象开头,该对象可用于映射或发布到 Vault 以进行登录。

示例 33. AppRole 身份验证流程
AuthenticationSteps.fromSupplier(                                       (1)

    () -> getAppRoleLogin(options.getRoleId(), options.getSecretId()))  (2)

    .login("auth/{mount}/login", options.getPath());                    (3)
1 开始声明AuthenticationSteps接受一个Supplier<T>. 状态对象类型取决于Supplier可以在后续步骤中映射的响应类型。
2 实际Supplier执行。Map在这种情况下创建一个。
3 通过将状态对象 ( Map) 发布到 Vault 端点以创建 Vault 令牌来执行 Vault 登录。请注意,模板变量会受到 URL 转义的影响。

身份验证流程需要执行程序来执行实际登录。我们为不同的执行模型提供了两个执行器:

  • AuthenticationStepsExecutor作为同步的替代品ClientAuthentication

  • AuthenticationStepsOperator用于反应式执行。

许多ClientAuthentication's 带有静态工厂方法来AuthenticationSteps 为其特定于身份验证的选项创建:

示例 34. 同步AuthenticationSteps执行
CubbyholeAuthenticationOptions options = …
RestOperations restOperations = …

AuthenticationSteps steps = CubbyholeAuthentication.createAuthenticationSteps(options);

AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor(steps, restOperations);

VaultToken token = executor.login();

15.15。代币生命周期

Vault 的代币可以与生存时间相关联。只要会话处于活动状态,就可以使用通过身份验证方法获得的令牌,并且在应用程序处于活动状态时不应过期。

Spring Vault 提供了LifecycleAwareSessionManager一个会话管理器,它可以更新令牌,直到它达到其终端 TTL,然后执行另一个登录以获得与会话关联的下一个令牌。

根据身份验证方法,登录可以创建两种令牌:

  • VaultToken:封装实际令牌的通用令牌。

  • LoginToken:与可再生性/TTL 相关的令牌。

身份验证方法,例如TokenAuthentication仅创建VaultToken不携带任何可更新性/TTL 详细信息的身份验证方法。LifecycleAwareSessionManager将对令牌运行自我查找以从 Vault 检索可更新性和 TTL。 VaultToken如果启用了自我查找,则会定期更新。请注意,VaultToken永远不会被撤销,只会LoginToken被撤销。

直接创建的身份验证方法LoginToken(所有基于登录的身份验证方法)已经提供了设置令牌更新所需的所有详细信息。LifecycleAwareSessionManager如果会话管理器关闭,则从登录获得的令牌将被撤销。

16. 杂项

在本章中学习有关 Spring Security 集成等值得一提的细节。

16.1. Spring安全

Spring Vault 通过提供 和 的实现与 Spring SecurityBytesKeyGenerator集成BytesEncryptor。两种实现都使用 Vault 的transit后端。

示例 35.VaultBytesKeyGenerator示例
VaultOperations operations = …;
VaultBytesKeyGenerator generator = new VaultBytesKeyGenerator(operations);

byte[] key = generator.generateKey();
示例 36.VaultBytesEncryptor示例
VaultTransitOperations transit = …;

VaultBytesEncryptor encryptor = new VaultBytesEncryptor(transit, "my-key-name");

byte[] ciphertext = encryptor.encrypt(plaintext);

byte[] result = encryptor.decrypt(ciphertext);

Vault 封装了一个熵源,它与您的 JVM 以及服务器端密钥管理分离。这减轻了应用程序开发人员进行适当加密/解密的负担,并将负担推到了 Vault 的运营商身上。Vault 的运营商通常包括组织中的安全团队,这意味着他们可以确保数据被正确加密/解密。此外,由于加密/解密操作必须进入审计日志,任何解密事件都会被记录下来。

后端还支持密钥轮换,允许生成命名密钥的新版本。所有使用密钥加密的数据都将使用最新版本的密钥;以前加密的数据可以使用旧版本的密钥解密。管理员可以控制哪些以前版本的密钥可用于解密,以防止攻击者获得密文的旧副本以成功解密。

保险柜毕竟是一种网络服务,每次操作都会有延迟。大量使用加密或随机字节生成的组件可能会遇到吞吐量和性能方面的差异。


1. see XML Configuration