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.

Example 1. Externalizing 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.

MyUserIdMechanism.java
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());
    }

    // …
}
Example 2. Using AWS-EC2 instance profile as credentials source
@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:

  1. Use SSL, see [vault.client-ssl]

  2. 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.
Example 3. Crating and storing tokens
$ 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
Example 4. Wrapped token response usage
@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

Example 5. Crating and storing 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
Example 6. Stored token response usage
@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.

Example 7. Configuring 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.

Example 8. Stored token authentication flow.
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.

Example 9. AppRole authentication flow
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 synchronous ClientAuthentication.

  • AuthenticationStepsOperator for reactive execution.

Many ClientAuthentication's come with static factory methods to create AuthenticationSteps for their authentication-specific options:

Example 10. Synchronous AuthenticationSteps execution
CubbyholeAuthenticationOptions 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.