认证方法
不同的组织对安全和认证有不同的要求。Vault 通过提供多种认证方法反映了这一需求。Spring Vault 支持多种认证机制。
外部化登录凭据
首次访问安全系统被称为安全引入。任何客户端都需要临时或永久凭据来访问 Vault。外部化凭据是一个保持代码高可维护性的良好模式,但也带来了增加泄露的风险。
将登录凭据泄露给任何一方都允许登录 Vault 并访问基础角色允许的 Secrets。选择合适的客户端认证并将凭据注入应用程序需要进行风险评估。
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
,了解在组件和其他属性源中引用属性的示例。
Token 认证
Tokens 是 Vault 中核心的认证方法。Token 认证需要提供一个静态 Token。
Token 认证是默认的认证方法。如果 Token 泄露给未经授权的方,则该方将获得 Vault 的访问权限,并可以访问预期客户端的 Secrets。 |
通常,Token 认证用于 Token 在外部创建和续期的场景(例如 HashiCorp Vault service broker)。根据实际设置,您可能希望或不希望进行 Token 续期和撤销。请参见 LifecycleAwareSessionManager
了解有关 TTL 和 Token 撤销的详细信息。
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("…");
}
// …
}
另请参见
AppId 认证
AppId 认证已被 Vault 弃用。请改用 AppRole 认证。 |
Vault 支持 AppId 认证,该认证由两个难以猜测的 Token 组成。AppId 默认为静态配置的 spring.application.name
。第二个 Token 是 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 进行认证以获取 Token 时,通过调用 createUserId
来获取 UserId。
public class MyUserIdMechanism implements AppIdUserIdMechanism {
@Override
public String createUserId() {
String userId = …
return userId;
}
}
AppRole 认证
Spring Vault 支持 AppRole 认证,可以通过仅提供 RoleId,或同时提供 SecretId 并从 Vault 获取 RoleId/SecretId(带响应解包的 push 和 pull 模式)。
@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 还支持完整的 pull 模式:如果未提供 RoleId 和 SecretId,Spring Vault 将使用角色名和初始 Token 来获取它们。初始 Token 可能与 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 Token。与大多数 Vault 认证后端不同,此后端不需要首先部署或配置安全性敏感的凭据(Tokens、用户名/密码、客户端证书等)。相反,它将 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,并将其与实例 Id 一起存储在认证后端。重新认证需要发送相同的 nonce。任何其他方没有该 nonce,可以在 Vault 中引发警报以进行进一步调查。
nonce 保存在内存中,并在应用程序重启时丢失。
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 Token。与大多数 Vault 认证后端不同,此后端不需要首先部署或配置安全性敏感的凭据(Tokens、用户名/密码、客户端证书等)。相反,它将 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 认证后端不同,此后端不需要首先部署或配置安全性敏感的凭据(Tokens、用户名/密码、客户端证书等)。相反,它将 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) 形式的签名。通过调用 GCP IAM 的 projects.serviceAccounts.signJwt
API 获取服务账户的 JWT。调用者对 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 凭据需要维护 Token 生命周期的 OAuth 2 Token。所有 API 都是同步的,因此 GcpIamCredentialsAuthentication 不支持响应式使用所需的 AuthenticationSteps 。 |
GcpIamCredentialsAuthentication 使用 IAM Credentials API,它是替代已弃用的 GcpIamAuthentication (使用已弃用的 IAM API)的方式。 |
另请参见
PCF 认证
pcf 认证后端允许 PCF 实例登录 Vault。它利用了 PCF 的应用程序和容器身份保障。
PCF 认证使用实例密钥和证书创建一个由 Vault 验证的签名。如果签名匹配,并且潜在绑定的组织/空间/应用程序 ID 匹配,Vault 会颁发一个适当范围的 Token。
实例凭据可从 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
认证后端允许使用 SSL/TLS 客户端证书进行认证,这些证书可以由 CA 签名或自签名。
要启用 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 认证使用 Tokens 作为主要的登录方法。一个临时 Token 用于从 Vault 的 Cubbyhole Secret 后端获取第二个登录 VaultToken。登录 Token 通常具有更长的生命周期,用于与 Vault 交互。登录 Token 可以从包装的响应或从 data
部分中获取。
创建包装的 Token
Response Wrapping 创建 Token 需要 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());
}
// …
}
使用存储的 Token
$ 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 相关的 Token 会在 Token 创建时开始计算其 TTL。该时间不一定与应用程序启动时间相同。为了补偿初始延迟,Cubbyhole 认证会对其与非零 TTL 相关的 Token 执行自查询,以检索剩余的 TTL。Cubbyhole 认证不会对没有 TTL 的包装 Token 执行自查询,因为零 TTL 表示没有关联的 TTL。
非包装 Token 仅通过检索 Token 本身无法提供关于可续期性和 TTL 的详细信息。自查询将查找可续期性和剩余 TTL。
另请参见
JWT 认证
配置 JWT 认证需要提供 Token 或 JWT Supplier。您可以通过 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 认证
Vault 自 0.8.3 版本起支持使用 Kubernetes Token 的 kubernetes 认证。
使用 Kubernetes 认证需要一个 Kubernetes Service Account Token,通常挂载在 /var/run/secrets/kubernetes.io/serviceaccount/token
。该文件包含读取并发送到 Vault 的 Token。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
, 额外支持基于时间的一次性 Token) -
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 Token 创建来执行 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();
Token 生命周期
Vault 的 Token 可以关联一个存活时间 (TTL)。通过认证方法获得的 Token 旨在只要会话处于活动状态就使用,并且不应在应用程序活动期间过期。
Spring Vault 提供了 LifecycleAwareSessionManager
会话管理器,它可以续期 Token 直到其达到最终 TTL,然后执行另一次登录以获取与会话关联的下一个 Token。
根据认证方法,登录可以创建两种类型的 Token
-
VaultToken
:封装实际 Token 的通用 Token。 -
LoginToken
:与可续期性/TTL 相关的 Token。
像 TokenAuthentication
这样的认证方法只创建一个不包含任何可续期性/TTL 详细信息的 VaultToken
。如果启用了自查询,LifecycleAwareSessionManager
将对 Token 运行自查询以从 Vault 获取可续期性和 TTL。如果启用了自查询,VaultToken
会定期续期。请注意,VaultToken
从不被撤销,只有 LoginToken
会被撤销。
直接创建 LoginToken
的认证方法(所有基于登录的认证方法)已经提供了设置 Token 续期所需的所有详细信息。如果会话管理器关闭,LifecycleAwareSessionManager
会撤销从登录获得的 Token。