认证方法

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

外部化登录凭据

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

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

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

示例 1. 将 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 时,通过 @Autowired Environment environment 进行注入将不会提供 Environment,因为环境 bean 仍在构建中,自动装配将在稍后阶段进行。您的配置类应该实现 ApplicationContextAware 并从 ApplicationContext 获取 Environment

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

令牌身份验证

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

令牌身份验证是默认的身份验证方法。如果令牌被意外泄露,则未经授权的方将获得访问 Vault 的权限,并可以访问目标客户端的机密信息。

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

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

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

    // …
}

另请参阅

AppId 身份验证

AppId 身份验证已由 Vault 弃用。请改用 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 的相应命令是

$ echo -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 的相应命令是

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

自定义 UserId

一种更高级的方法允许您实现自己的AppIdUserIdMechanism。此类必须位于您的类路径上,并且必须实现org.springframework.vault.authentication.AppIdUserIdMechanism接口和createUserId方法。Spring Vault 将在每次使用 AppId 进行身份验证以获取令牌时调用createUserId来获取 UserId。

MyUserIdMechanism.java
public class MyUserIdMechanism implements AppIdUserIdMechanism {

  @Override
  public String createUserId() {

    String userId = …
    return userId;
  }
}

AppRole 身份验证

AppRole 允许机器身份验证,例如已弃用(自 Vault 0.6.1 起)的 AppId 身份验证。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());
    }

    // …
}

AWS-EC2 身份验证

aws-ec2 身份验证后端为 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 会生成一个随机数,该随机数存储在身份验证后端,与实例 ID 相关联。重新身份验证需要发送相同的随机数。任何其他方都没有随机数,并且可以在 Vault 中引发警报以供进一步调查。

随机数保存在内存中,并在应用程序重启时丢失。

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

AWS-IAM 身份验证

aws 身份验证后端允许 Vault 通过使用现有的 AWS IAM 凭据登录。

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

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

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

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

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

    // …
}
示例 2. 使用 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 SDK 依赖项(com.amazonaws:aws-java-sdk-core),因为身份验证实现使用 AWS SDK 类型进行凭据和请求签名。

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

另请参阅

Azure (MSI) 身份验证

azure 身份验证后端为 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 的实例元数据服务以获取这些详细信息。

另请参阅

GCP-GCE 身份验证

gcp 身份验证后端允许 Vault 登录使用现有的 GCP(Google Cloud Platform)IAM 和 GCE 凭据。

GCP GCE(Google Compute Engine)身份验证会为服务帐户创建 JSON Web 令牌 (JWT) 形式的签名。Compute Engine 实例的 JWT 是使用 实例标识 从 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());
    }

    // …
}

另请参阅

GCP-IAM 身份验证

gcp 身份验证后端允许 Vault 登录使用现有的 GCP(Google Cloud Platform)IAM 和 GCE 凭据。

GCP IAM 身份验证会为服务帐户创建 JSON Web 令牌 (JWT) 形式的签名。服务帐户的 JWT 是通过调用 GCP IAM 的 projects.serviceAccounts.signJwt API 获取的。调用者会对 GCP IAM 进行身份验证,并以此证明其身份。此 Vault 后端将 GCP 视为可信第三方。

IAM 凭据可以从运行时环境中获取,也可以作为例如 JSON 外部提供。JSON 是首选形式,因为它包含调用 projects.serviceAccounts.signJwt 所需的项目 ID 和服务帐户标识符。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

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

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

    // …
}

GcpIamCredentialsAuthenticationOptions 需要 Google Cloud Java SDK 依赖项 (com.google.cloud:google-cloud-iamcredentials),因为身份验证实现使用 Google API 进行凭据和 JWT 签名。

您可以通过 GcpIamCredentialsAuthenticationOptions 配置身份验证。

Google 凭据需要一个 OAuth 2 令牌来维护令牌生命周期。所有 API 都是同步的,因此 GcpIamCredentialsAuthentication 不支持 AuthenticationSteps,而 AuthenticationSteps 是响应式使用所必需的。
GcpIamCredentialsAuthentication 使用 IAM 凭据 API,并使用已弃用的 IAM API 替换已弃用的 GcpIamAuthentication

另请参阅

PCF 身份验证

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

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

实例凭据可从 CF_INSTANCE_CERTCF_INSTANCE_KEY 变量的 文件中获取。

@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 配置身份验证。

另请参阅

TLS 证书身份验证

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

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

  1. 使用 SSL,参见 [vault.client-ssl]

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

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

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

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

    // …
}

Cubbyhole 认证

Cubbyhole 认证使用 Vault 原语提供安全的认证工作流程。Cubbyhole 认证使用令牌作为主要登录方法。一个短暂的令牌用于从 Vault 的 Cubbyhole 秘密后端获取第二个登录 VaultToken。登录令牌通常具有更长的生命周期,用于与 Vault 交互。登录令牌可以从包装的响应中或从 data 部分中检索。

创建包装令牌

令牌创建的响应包装需要 Vault 0.6.0 或更高版本。
示例 3. 创建和存储令牌
$ 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
示例 4. 包装令牌响应使用
@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

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

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

    // …
}

使用存储的令牌

示例 5. 创建和存储令牌
$ 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
示例 6. 存储的令牌响应使用
@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/可续期

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

非包装令牌仅通过检索令牌不提供有关可续期和 TTL 的详细信息。自查找将查找可续期和剩余 TTL。

另请参阅

JWT 认证

配置 JWT 认证需要令牌或 JWT 提供者。您可以通过 JwtAuthenticationOptions 配置认证。

在 Vault 端,您可以通过启用 JWT 认证后端并创建角色来配置 JWT 后端。您可以使用 oidc_discovery_urljwks_urljwt_validation_pubkeys 来配置 JWT 后端。

@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        JwtAuthenticationOptions options = JwtAuthenticationOptions.builder()
                .role(…).jwt(…).path(…).build();

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

    // …
}

另请参阅

Kubernetes 认证

Vault 从 0.8.3 版本开始支持使用 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 配置认证。

另请参阅

用户名/密码身份验证

用户名/密码通常是最终用户身份验证方案。多个 Vault 身份验证后端支持使用用户名和密码。

  • 用户名和密码 (userpass)

  • LDAP (ldap)

  • Okta (okta,支持额外的基于时间的的一次性令牌)

  • RADIUS (radius)

UserPasswordAuthenticationOptions 可用于所有上述身份验证后端,因为登录 API 在所有机制中都类似。请确保在配置 UserPasswordAuthenticationOptions 时使用适当的认证挂载路径。

示例 7. 配置 UserPasswordAuthentication
@Configuration
class AppConfig extends AbstractVaultConfiguration {

    // …

    @Override
    public ClientAuthentication clientAuthentication() {

        UserPasswordAuthenticationOptions options = UserPasswordAuthenticationOptions.builder()
                .username(…).password(…).build();

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

    // …
}

另请参阅

身份验证步骤

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

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

身份验证步骤提供对常见身份验证活动的重用。通过 AuthenticationSteps 创建的步骤以函数式风格描述身份验证流程,将实际的身份验证执行留给特定的执行器。

示例 8. 存储令牌身份验证流程。
AuthenticationSteps.just(VaultToken.of(…));                              (1)
1 仅从 VaultToken 创建 AuthenticationSteps

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

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

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

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

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

  • AuthenticationStepsExecutor 作为同步 ClientAuthentication 的直接替换。

  • AuthenticationStepsOperator 用于反应式执行。

许多 ClientAuthentication 都带有静态工厂方法,用于为其身份验证特定选项创建 AuthenticationSteps

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

AuthenticationSteps steps = CubbyholeAuthentication.createAuthenticationSteps(options);

AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor(steps, restOperations);

VaultToken token = executor.login();

令牌生命周期

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

Spring Vault 提供了 LifecycleAwareSessionManager,这是一个会话管理器,它可以续订令牌,直到它达到其最终 TTL,然后执行另一个登录以获取与会话关联的下一个令牌。

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

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

  • LoginToken:与可续订性/TTL 关联的令牌。

诸如 TokenAuthentication 之类的身份验证方法只创建 VaultToken,它不包含任何可续订性/TTL 详细信息。LifecycleAwareSessionManager 将在令牌上运行自查找以从 Vault 检索可续订性和 TTL。如果启用了自查找,VaultToken 会定期续订。请注意,VaultToken 从不撤销,只有 LoginToken 会被撤销。

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