Authentication Methods
Different organizations have different requirements for security and authentication. Vault reflects that need by shipping multiple authentication methods. Spring Vault supports multiple authentications mechanisms.
Externalizing login credentials
Obtaining first-time access to a secured system is known as secure introduction. Any client requires ephemeral or permanent credentials to access Vault. Externalizing credentials is a good pattern to keep code maintainability high but comes at a risk of increased disclosure.
Disclosure of login credentials to any party allows login to Vault and access secrets that are permitted by the underlying role. Picking the appropriate client authentication and injecting credentials into the application is subject to risk evaluation.
Spring’s PropertySource abstraction is a natural fit to keep configuration outside the application code. You can use system properties, environment variables or property files to store login credentials. Each approach comes with its own properties. Keep in mind that the command line and environment properties can be introspected with appropriate OS access levels.
vault.token
to a properties file@PropertySource("configuration.properties")
@Configuration
public class Config extends AbstractVaultConfiguration {
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication(getEnvironment().getProperty("vault.token"));
}
}
Spring allows multiple ways to obtain Environment . When using VaultPropertySource , injection via @Autowired Environment environment will not provide the Environment as the environment bean is still in construction and autowiring comes at a later stage. Your configuration class should rather implement ApplicationContextAware and obtain the Environment from ApplicationContext .
|
See SecurePropertyUsage.java
for a sample on referencing properties in components and other property sources.
Token authentication
Tokens are the core method for authentication within Vault. Token authentication requires a static token to be provided.
Token authentication is the default authentication method. If a token is disclosed an unintended party, it gains access to Vault and can access secrets for the intended client. |
Typically, Token authentication is used in scenarios in which the token is created and renewed
externally (such as HashiCorp Vault service broker).
Depending on the actual setup, you may or may not want token renewal and revocation.
See LifecycleAwareSessionManager
for details about TTL and token revocation.
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
return new TokenAuthentication("…");
}
// …
}
See also:
AppId authentication
AppId authentication is deprecated by Vault. Use AppRole authentication instead. |
Vault supports AppId
authentication that consists of two hard to guess tokens. The AppId
defaults to spring.application.name
that is statically configured.
The second token is the UserId which is a part determined by the application,
usually related to the runtime environment. IP address, Mac address or a
Docker container name are good examples. Spring Vault supports
IP address, Mac address and static UserId’s (e.g. supplied via System properties).
The IP and Mac address are represented as Hex-encoded SHA256 hash.
IP address-based UserId’s use the local host’s IP address.
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
AppIdAuthenticationOptions options = AppIdAuthenticationOptions.builder()
.appId("myapp")
.userIdMechanism(new IpAddressUserId())
.build();
return new AppIdAuthentication(options, restOperations());
}
// …
}
The corresponding command to generate the IP address UserId from a command line is:
$ echo -n 192.168.99.1 | sha256sum
Including the line break of echo leads to a different hash value
so make sure to include the -n flag.
|
Mac address-based UserId’s obtain their network device from the
localhost-bound device. The configuration also allows specifying
a network-interface
hint to pick the right device. The value of
network-interface
is optional and can be either an interface
name or interface index (0-based).
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
AppIdAuthenticationOptions options = AppIdAuthenticationOptions.builder()
.appId("myapp")
.userIdMechanism(new MacAddressUserId())
.build();
return new AppIdAuthentication(options, restOperations());
}
// …
}
The corresponding command to generate the Mac address UserId from a command line is:
$ echo -n 0AFEDE1234AC | sha256sum
The Mac address is specified uppercase and without colons.
Including the line break of echo leads to a different hash value
so make sure to include the -n flag.
|
Custom UserId
A more advanced approach lets you implementing your own AppIdUserIdMechanism
.
This class must be on your classpath and must implement
the org.springframework.vault.authentication.AppIdUserIdMechanism
interface
and the createUserId
method. Spring Vault will obtain the UserId
by calling createUserId
each time it authenticates using AppId to
obtain a token.
public class MyUserIdMechanism implements AppIdUserIdMechanism {
@Override
public String createUserId() {
String userId = …
return userId;
}
}
AppRole authentication
AppRole allows machine authentication, like the deprecated (since Vault 0.6.1) AppId authentication. AppRole authentication consists of two hard to guess (secret) tokens: RoleId and SecretId.
Spring Vault supports AppRole authentication by providing either RoleId only or together with a provided SecretId and fetching RoleId/SecretId from Vault (push and pull modes with response unwrapping).
@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 also support full pull mode: If RoleId and SecretId are not provided, Spring Vault will retrieve them using the role name and an initial token. The initial token may be associated with a TTL and usage limit.
@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 authentication
The aws-ec2 auth backend provides a secure introduction mechanism for AWS EC2 instances, allowing automated retrieval of a Vault token. Unlike most Vault authentication backends, this backend does not require first-deploying, or provisioning security-sensitive credentials (tokens, username/password, client certificates, etc.). Instead, it treats AWS as a Trusted Third Party and uses the cryptographically signed dynamic metadata information that uniquely represents each EC2 instance.
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
return new AwsEc2Authentication(restOperations());
}
// …
}
AWS-EC2 authentication enables nonce by default to follow the Trust On First Use (TOFU) principle. Any unintended party that gains access to the PKCS#7 identity metadata can authenticate against Vault.
During the first login, Spring Vault generates a nonce that is stored in the auth backend aside the instance Id. Re-authentication requires the same nonce to be sent. Any other party does not have the nonce and can raise an alert in Vault for further investigation.
The nonce is kept in memory and is lost during application restart.
AWS-EC2 authentication roles are optional and default to the AMI.
You can configure the authentication role by setting
it in AwsEc2AuthenticationOptions
.
AWS-IAM authentication
The aws auth backend allows Vault login by using existing AWS IAM credentials.
AWS IAM authentication creates a signed HTTP request that is
executed by Vault to get the identity of the signer using AWS STS
GetCallerIdentity
method. AWSv4 signatures require IAM credentials.
IAM credentials can be obtained from either the runtime environment or supplied externally. Runtime environments such as AWS-EC2, Lambda and ECS with assigned IAM principals do not require client-specific configuration of credentials but can obtain these from its metadata source.
@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
requires the AWS Java SDK dependency (com.amazonaws:aws-java-sdk-core
)
as the authentication implementation uses AWS SDK types for credentials and request signing.
You can configure the authentication via AwsIamAuthenticationOptions
.
See also:
Azure (MSI) authentication
The azure auth backend provides a secure introduction mechanism for Azure VM instances, allowing automated retrieval of a Vault token. Unlike most Vault authentication backends, this backend does not require first-deploying, or provisioning security-sensitive credentials (tokens, username/password, client certificates, etc.). Instead, it treats Azure as a Trusted Third Party and uses the managed service identity and instance metadata information that can be bound to a VM instance.
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
AzureMsiAuthenticationOptions options = AzureMsiAuthenticationOptions.builder()
.role(…).build();
return new AzureMsiAuthentication(options, restOperations());
}
// …
}
Azure authentication requires details about the VM environment (subscription Id,
resource group name, VM name). These details can be either configured through
AzureMsiAuthenticationOptionsBuilder
.
If left unconfigured, AzureMsiAuthentication
queries Azure’s instance metadata service to
obtain these details.
See also:
GCP-GCE authentication
The gcp auth backend allows Vault login by using existing GCP (Google Cloud Platform) IAM and GCE credentials.
GCP GCE (Google Compute Engine) authentication creates a signature in the form of a JSON Web Token (JWT) for a service account. A JWT for a Compute Engine instance is obtained from the GCE metadata service using Instance identification. This API creates a JSON Web Token that can be used to confirm the instance identity.
Unlike most Vault authentication backends, this backend does not require first-deploying, or provisioning security-sensitive credentials (tokens, username/password, client certificates, etc.). Instead, it treats GCP as a Trusted Third Party and uses the cryptographically signed dynamic metadata information that uniquely represents each GCP service account.
You can configure the authentication via GcpComputeAuthenticationOptions
.
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
GcpComputeAuthenticationOptions options = GcpComputeAuthenticationOptions.builder()
.role(…).build();
GcpComputeAuthentication authentication = new GcpComputeAuthentication(options,
restOperations());
}
// …
}
See also:
GCP-IAM authentication
The gcp auth backend allows Vault login by using existing GCP (Google Cloud Platform) IAM and GCE credentials.
GCP IAM authentication creates a signature in the form of a JSON Web Token (JWT)
for a service account. A JWT for a service account is obtained by
calling GCP IAM’s projects.serviceAccounts.signJwt
API. The caller authenticates against GCP IAM
and proves thereby its identity. This Vault backend treats GCP as a Trusted Third Party.
IAM credentials can be obtained from either the runtime environment
or supplied externally as e.g. JSON. JSON is the preferred form as it
carries the project id and service account identifier required for calling
projects.serviceAccounts.signJwt
.
@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
requires the Google Cloud Java SDK dependency
(com.google.cloud:google-cloud-iamcredentials
)
as the authentication implementation uses Google APIs for credentials and JWT signing.
You can configure the authentication via GcpIamCredentialsAuthenticationOptions
.
Google credentials require an OAuth 2 token maintaining the token lifecycle. All API
is synchronous therefore, GcpIamCredentialsAuthentication does not support AuthenticationSteps which is
required for reactive usage.
|
GcpIamCredentialsAuthentication uses the IAM Credentials API and is a replacement using the for the deprecated GcpIamAuthentication using the deprecated IAM API.
|
See also:
PCF authentication
The pcf auth backend allows Vault login for PCF instances. It leverages PCF’s App and Container Identity Assurance.
PCF authentication uses the instance key and certificate to create a signature that is validated by Vault. If the signature matches, and potentially bound organization/space/application Id’s match, Vault issues an appropriately-scoped token.
Instance credentials are available from files at CF_INSTANCE_CERT
and
CF_INSTANCE_KEY
variables.
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
PcfAuthenticationOptions options = PcfAuthenticationOptions.builder()
.role(…).build();
PcfAuthentication authentication = new PcfAuthentication(options,
restOperations());
}
// …
}
PcfAuthenticationOptions
requires the BouncyCastle
library for creating RSA-PSS signatures.
You can configure the authentication via PcfAuthenticationOptions
.
See also:
TLS certificate authentication
The cert
auth backend allows authentication using SSL/TLS client
certificates that are either signed by a CA or self-signed.
To enable cert
authentication you need to:
-
Use SSL, see [vault.client-ssl]
-
Configure a Java
Keystore
that contains the client certificate and the private key
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
ClientCertificateAuthenticationOptions options = ClientCertificateAuthenticationOptions.builder()
.path(…).build();
return new ClientCertificateAuthentication(options, restOperations());
}
// …
}
Cubbyhole authentication
Cubbyhole authentication uses Vault primitives to provide a secured authentication
workflow. Cubbyhole authentication uses tokens as primary login method.
An ephemeral token is used to obtain a second, login VaultToken from Vault’s
Cubbyhole secret backend. The login token is usually longer-lived and used to
interact with Vault. The login token can be retrieved either from a wrapped
response or from the data
section.
Creating a wrapped token
Response Wrapping for token creation requires Vault 0.6.0 or higher. |
$ 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());
}
// …
}
Using stored tokens
$ 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());
}
// …
}
Remaining TTL/Renewability
Tokens retrieved from Cubbyhole associated with a non-zero TTL start their TTL at the time of token creation. That time is not necessarily identical with application startup. To compensate for the initial delay, Cubbyhole authentication performs a self lookup for tokens associated with a non-zero TTL to retrieve the remaining TTL. Cubbyhole authentication will not self-lookup wrapped tokens without a TTL because a zero TTL indicates there is no TTL associated.
Non-wrapped tokens do not provide details regarding renewability and TTL by just retrieving the token. A self-lookup will lookup renewability and the remaining TTL.
See also:
JWT authentication
Configuring JWT authentication requires the token or a JWT supplier.
You can configure the authentication via JwtAuthenticationOptions
.
On the Vault side you can configure the JWT backend by enabling the JWT auth backend and creating a role.
You can either use oidc_discovery_url
, jwks_url
or jwt_validation_pubkeys
to configure the JWT backend.
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
JwtAuthenticationOptions options = JwtAuthenticationOptions.builder()
.role(…).jwt(…).path(…).build();
return new JwtAuthentication(options, restOperations());
}
// …
}
See also:
Kubernetes authentication
Vault supports since 0.8.3 kubernetes-based authentication using Kubernetes tokens.
Using Kubernetes authentication requires a Kubernetes Service Account Token,
usually mounted at /var/run/secrets/kubernetes.io/serviceaccount/token
.
The file contains the token which is read and sent to Vault.
Vault verifies its validity using Kubernetes' API during login.
Configuring Kubernetes authentication requires at least the role name to be provided:
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
KubernetesAuthenticationOptions options = KubernetesAuthenticationOptions.builder()
.role(…).jwtSupplier(…).build();
return new KubernetesAuthentication(options, restOperations());
}
// …
}
You can configure the authentication via KubernetesAuthenticationOptions
.
See also:
Username/Password authentication
Username/Password is typically a end-user authentication scheme. Using username and password is supported by multiple Vault authentication backends:
-
Username and Password (
userpass
) -
LDAP (
ldap
) -
Okta (
okta
, supports additionaly time-based one-time tokens) -
RADIUS (
radius
)
UserPasswordAuthenticationOptions
can be used with all above mentioned authentication backends as the Login API is similar across all mechanisms.
Please ensure to use the appropriate auth mount path when configuring UserPasswordAuthenticationOptions
.
UserPasswordAuthentication
@Configuration
class AppConfig extends AbstractVaultConfiguration {
// …
@Override
public ClientAuthentication clientAuthentication() {
UserPasswordAuthenticationOptions options = UserPasswordAuthenticationOptions.builder()
.username(…).password(…).build();
return new UserPasswordAuthentication(options, restOperations());
}
// …
}
See also:
Authentication Steps
ClientAuthentication
objects describe the authentication flow and perform the actual
authentication steps. Pre-composed authentications are easy to use and to configure with
a tight binding to synchronous execution.
The composition of authentication methods and reusing common steps, such as posting login
payload to Vault or retrieving authentication input from an HTTP source is not intended
with ClientAuthentication
objects.
Authentication steps provide reusability of common authentication activity.
Steps created via AuthenticationSteps
describe an authentication flow in a functional
style leaving the actual authentication execution to specific executors.
AuthenticationSteps.just(VaultToken.of(…)); (1)
1 | Creates AuthenticationSteps from just a VaultToken . |
A single-step authentication flow can be created from a single input. Flows declaring
multiple authentication steps start with a Supplier
or HttpRequest
that provide an
authentication state object which can be used to map or post to Vault for login.
AuthenticationSteps.fromSupplier( (1)
() -> getAppRoleLogin(options.getRoleId(), options.getSecretId())) (2)
.login("auth/{mount}/login", options.getPath()); (3)
1 | Start declaring AuthenticationSteps accepting a Supplier<T> .
The state object type depends on the Supplier response type which can be mapped in a later step. |
2 | The actual Supplier implementation.
Creating a Map in this case. |
3 | Perform a Vault login by posting the state object (Map ) to a Vault endpoint for Vault token creation.
Note that template variables are subject to URL escaping. |
Authentication flows require an executor to perform the actual login. We provide two executors for different execution models:
-
AuthenticationStepsExecutor
as a drop-in replacement for synchronousClientAuthentication
. -
AuthenticationStepsOperator
for reactive execution.
Many ClientAuthentication
's come with static factory methods to create AuthenticationSteps
for their authentication-specific options:
AuthenticationSteps
executionCubbyholeAuthenticationOptions options = …
RestOperations restOperations = …
AuthenticationSteps steps = CubbyholeAuthentication.createAuthenticationSteps(options);
AuthenticationStepsExecutor executor = new AuthenticationStepsExecutor(steps, restOperations);
VaultToken token = executor.login();
Token Lifecycle
Vault’s tokens can be associated with a time to live. Tokens obtained by an authentication method are intended to be used as long as the session is active and should not expire while the application is active.
Spring Vault provides with LifecycleAwareSessionManager
a session manager that can renew the token until it reaches its terminal TTL to then perform another login to obtain the next token which is associated with the session.
Depending on the authentication method, a login can create two kinds of tokens:
-
VaultToken
: Generic token encapsulating the actual token. -
LoginToken
: Token associated with renewability/TTL.
Authentication methods such as TokenAuthentication
just create a VaultToken
which does not carry any renewability/TTL details. LifecycleAwareSessionManager
will run a self-lookup on the token to retrieve renewability and TTL from Vault.
VaultToken
are renewed periodically if self-lookup is enabled. Note that VaultToken
are never revoked, only LoginToken
are revoked.
Authentication methods creating LoginToken
directly (all login-based authentication methods) already provide all necessary details to setup token renewal. Tokens obtained from a login are revoked by LifecycleAwareSessionManager
if the session manager is shut down.