身份验证方法
不同的组织对安全和身份验证有不同的要求。Vault 通过提供多种身份验证方法来反映这一需求。Spring Vault 支持多种身份验证机制。
外部化登录凭据
首次访问受保护系统称为安全引入。任何客户端都需要临时或永久凭据才能访问 Vault。外部化凭据是保持代码可维护性高的良好模式,但存在披露风险增加的问题。
向任何一方披露登录凭据都允许登录 Vault 并访问底层角色允许的秘密。选择合适的客户端身份验证并将凭据注入应用程序需要进行风险评估。
Spring 的 PropertySource 抽象非常适合将配置保留在应用程序代码之外。您可以使用系统属性、环境变量或属性文件来存储登录凭据。每种方法都有其自身的属性。请记住,可以通过适当的操作系统访问级别内省命令行和环境属性。
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("…");
}
// …
}
另请参阅
AppRole 身份验证
AppRole 允许机器身份验证。AppRole 身份验证由两个难以猜测的(秘密)令牌组成:RoleId 和 SecretId。
Spring Vault 通过提供仅 RoleId 或与提供的 SecretId 一起提供 AppRole 身份验证,并从 Vault 获取 RoleId/SecretId(带响应解包的推拉模式)。
@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 身份验证默认启用 nonce 以遵循首次使用信任 (TOFU) 原则。任何未经授权的方,只要获得对 PKCS#7 身份元数据的访问权限,就可以对 Vault 进行身份验证。
首次登录期间,Spring Vault 会生成一个 nonce,该 nonce 与实例 ID 一起存储在身份验证后端中。重新身份验证需要发送相同的 nonce。任何其他方都没有该 nonce,并且可以在 Vault 中引发警报以进行进一步调查。
nonce 保存在内存中,并在应用程序重新启动时丢失。
自 Spring Vault 3.2 起,AWS-EC2 身份验证支持请求/响应 (IMDSv1) 元数据检索和基于会话的变体 (IMDSv2)。
AWS-EC2 身份验证角色是可选的,默认设置为 AMI。您可以通过在 AwsEc2AuthenticationOptions 中设置来配置身份验证角色。
AWS-IAM 身份验证
aws 身份验证后端允许使用现有 AWS IAM 凭据进行 Vault 登录。
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());
}
// …
}
@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 身份验证后端允许使用现有的 GCP (Google Cloud Platform) IAM 和 GCE 凭据进行 Vault 登录。
GCP GCE (Google Compute Engine) 身份验证以 JSON Web Token (JWT) 的形式为服务帐户创建签名。Compute Engine 实例的 JWT 是使用 实例识别 从 GCE 元数据服务获取的。此 API 创建一个可用于确认实例身份的 JSON Web Token。
与大多数 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 身份验证后端允许使用现有的 GCP (Google Cloud Platform) IAM 和 GCE 凭据进行 Vault 登录。
GCP IAM 身份验证以 JSON Web Token (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 的替代品。 |
另请参阅
GitHub 身份验证
github 身份验证后端提供基于 GitHub 令牌的身份验证机制。Vault 不支持 OAuth 工作流来生成 GitHub 令牌,因此它不作为 GitHub 应用程序运行。
身份验证机制需要一个 GitHub 令牌(或供应商)才能将令牌传递给 Vault,然后 Vault 会针对您的 GitHub 帐户进行身份验证。
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
GitHubAuthentication options = GitHubAuthentication.builder()
.token(…).build();
return new GitHubAuthentication(options, restOperations());
}
// …
}
另请参阅
PCF 身份验证
pcf 身份验证后端允许 PCF 实例登录 Vault。它利用了 PCF 的应用程序和容器身份保证。
PCF 身份验证使用实例密钥和证书来创建签名,该签名由 Vault 验证。如果签名匹配,并且可能绑定的组织/空间/应用程序 ID 匹配,Vault 会颁发适当范围的令牌。
实例凭据可从 CF_INSTANCE_CERT 和 CF_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 身份验证,您需要:
-
使用 SSL,请参阅 [vault.client-ssl]
-
配置包含客户端证书和私钥的 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 或更高版本。 |
$ 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
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
CubbyholeAuthenticationOptions options = CubbyholeAuthenticationOptions
.builder()
.initialToken(VaultToken.of("…"))
.wrapped()
.build();
return new CubbyholeAuthentication(options, restOperations());
}
// …
}
使用存储的令牌
$ 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
@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。
另请参阅
JWT 身份验证
配置 JWT 身份验证需要令牌或 JWT 供应商。您可以通过 JwtAuthenticationOptions 配置身份验证。
在 Vault 端,您可以通过启用 JWT 身份验证后端并创建角色来配置 JWT 后端。您可以使用 oidc_discovery_url、jwks_url 或 jwt_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 身份验证
自 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 配置身份验证。
另请参阅
用户名/密码身份验证
用户名/密码通常是一种最终用户身份验证方案。多个 Vault 身份验证后端支持使用用户名和密码
-
用户名和密码 (
userpass) -
LDAP (
ldap) -
Okta (
okta,还支持基于时间的一次性令牌) -
RADIUS (
radius)
UserPasswordAuthenticationOptions 可以与上述所有身份验证后端一起使用,因为登录 API 在所有机制中都相似。配置 UserPasswordAuthenticationOptions 时,请确保使用适当的身份验证挂载路径。
UserPasswordAuthentication@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
UserPasswordAuthenticationOptions options = UserPasswordAuthenticationOptions.builder()
.username(…).password(…).build();
return new UserPasswordAuthentication(options, restOperations());
}
// …
}
另请参阅
身份验证步骤
ClientAuthentication 对象描述身份验证流并执行实际的身份验证步骤。预设的身份验证易于使用,并且可以通过与同步执行的紧密绑定进行配置。
身份验证方法的组合和重用通用步骤(例如将登录负载发布到 Vault 或从 HTTP 源检索身份验证输入)不适用于 ClientAuthentication 对象。
身份验证步骤提供了通用身份验证活动的可重用性。通过 AuthenticationSteps 创建的步骤以函数式样式描述身份验证流,将实际的身份验证执行留给特定的执行器。
AuthenticationSteps.just(VaultToken.of(…)); (1)
| 1 | 仅从 VaultToken 创建 AuthenticationSteps。 |
可以从单个输入创建单步身份验证流。声明多个身份验证步骤的流以提供身份验证状态对象的 Supplier 或 HttpRequest 开始,该状态对象可用于映射或发布到 Vault 以进行登录。
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
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 撤销。