Git Product home page Git Product logo

vault-java-driver's Introduction

Vault Java Driver

A zero-dependency Java client for the Vault secrets management solution from HashiCorp.

This driver strives to implement Vault's full HTTP API, along with supporting functionality such as automatic retry handling. It does so without relying on any other external libraries beyond the Java standard library, and is compatible with Java 8 and up. So it will play nice with all of your projects, greenfield and legacy alike, without causing conflicts with any other dependency.

NOTE: Although the binary artifact produced by the project is backwards-compatible with Java 8, you do need JDK 9 or higher to modify or build the source code of this library itself.

This Change

Table of Contents

Installing the Driver

The driver is available from Maven Central, for all modern Java build systems.

Gradle:

dependencies {
    implementation 'com.bettercloud:vault-java-driver:5.1.0'
}

Maven:

<dependency>
    <groupId>com.bettercloud</groupId>
    <artifactId>vault-java-driver</artifactId>
    <version>5.1.0</version>
</dependency>

Initializing a Driver Instance

The com.bettercloud.vault.VaultConfig class is used to initialize a driver instance with desired settings. In the most basic use cases, where you are only supplying a Vault server address and perhaps a root token, there are convenience constructor methods available:

final VaultConfig config = new VaultConfig()
                                  .address("http://127.0.0.1:8200")
                                  .token("3c9fd6be-7bc2-9d1f-6fb3-cd746c0fc4e8")
                                  .build();

// You may choose not to provide a root token initially, if you plan to use
// the Vault driver to retrieve one programmatically from an auth backend.
final VaultConfig config = new VaultConfig().address("http://127.0.0.1:8200").build();

To explicitly set additional config parameters (*), you can use a builder pattern style to construct the VaultConfig instance. Either way, the initialization process will try to populate any unset values by looking to environment variables.

final VaultConfig config =
    new VaultConfig()
        .address("http://127.0.0.1:8200")               // Defaults to "VAULT_ADDR" environment variable
        .token("3c9fd6be-7bc2-9d1f-6fb3-cd746c0fc4e8")  // Defaults to "VAULT_TOKEN" environment variable
        .openTimeout(5)                                 // Defaults to "VAULT_OPEN_TIMEOUT" environment variable
        .readTimeout(30)                                // Defaults to "VAULT_READ_TIMEOUT" environment variable
        .sslConfig(new SslConfig().build())             // See "SSL Config" section below
        .build();

Once you have initialized a VaultConfig object, you can use it to construct an instance of the Vault primary driver class:

final Vault vault = new Vault(config);

Key Value Secret Engine Config

Shortly before its 1.0 release, Vault added a Version 2 of its Key/Value Secrets Engine. This supports some addition features beyond the Version 1 that was the default in earlier Vault builds (e.g. secret rotation, soft deletes, etc).

Unfortunately, K/V V2 introduces some breaking changes, in terms of both request/response payloads as well as how URL's are constructed for Vault's REST API. Therefore, version 4.0.0 of this Vault Driver likewise had to introduce some breaking changes, to allow support for both K/V versions.

  • If you are using the new K/V V2 across the board, then no action is needed. The Vault Driver now assumes this by default.

  • If you are still using the old K/V V1 across the board, then you can use the Vault class constructor: public Vault(final VaultConfig vaultConfig, final Integer engineVersion), supplying a 1 as the engine version parameter. constructor, then you can declare whether to use Version 1 or 2 across the board.

  • If you are using a mix, of some secret paths mounted with V1 and others mounted with V2, then you have two options:

    • You can explicitly specify your Vault secret paths, and which K/V version each one is using. Construct your Vault objects with the constructor public Vault(final VaultConfig vaultConfig, final Boolean useSecretsEnginePathMap, final Integer globalFallbackVersion).
      Within the VaultConfig object, supply a map of Vault secret paths to their associated K/V version (1 or 2).

    • You can rely on the Vault Driver to auto-detect your mounts and K/V versions upon instantiation. Use the same constructor as above, but leave the map null. Note that this option requires your authentication credentials to have access to read Vault's /v1/sys/mounts path.

Version 2 of the K/V engine dynamically injects a qualifier element into your secret paths, which varies depending on the type of for read and write operations, in between the root version operation. For example, for read and write operations, the secret path:

v1/mysecret

... has a "data" qualifier injected:

v1/data/mysecret

The default behavior of this driver is to insert the appropriate qualifier one level deep (i.e. in between the root version number and the rest of the path). However, if your secret path is prefixed, such that the qualifier should be injected further down:

v1/my/long/prefix/data/anything/else

... then you should accordingly set the VaultConfig.prefixPathDepth property when constructing your Vault instance.

SSL Config

If your Vault server uses a SSL certificate, then you must supply that certificate to establish connections. Also, if you are using certificate-based client authentication, then you must supply a client certificate and private key that have been previously registered with your Vault server.

SSL configuration has been broken off from the VaultConfig class, and placed in its own SslConfig class. This class likewise using a builder pattern.

General Options

.verify(false)    // Defaults to "VAULT_SSL_VERIFY" environment variable (or else "true")

To disable SSL certificate verification altogether, set sslVerify(false). YOU SHOULD NOT DO THIS IS A REAL PRODUCTION SETTING! However, it can be useful in a development or testing server context. If this value is explicitly set to false, then all other SSL config is basically unused.

Java Keystore (JKS) based config

You can provide the driver with a JKS truststore, containing Vault's server-side certificate for basic SSL, using one of the following three options:

.trustStore(object) - Supply an in-memory java.security.KeyStore file, containing Vault server cert(s) that can be trusted.

.trustStoreFile(path) - Same as the above, but the path references a JKS file on the filesystem.

.trustStoreResource(path) - Same as the above, but the path references a classpath resource rather than a filesystem path (e.g. if you've bundled the JKS file into your application's JAR, WAR, or EAR file).

If you are only using basic SSL, then no keystore need be provided. However, if you would like to use Vault's TLS Certificate auth backend for client side auth, then you need to provide a JKS keystore containing your client-side certificate and private key:

.keyStore(object, password) - Supply an in-memory java.security.KeyStore file containing a client certificate and private key, and the password needed to access it (can be null). can be trusted.

.keyStoreFile(path, password) - Same as the above, but the path references a JKS file on the filesystem.

.keyStoreResource(path, password) - Same as the above, but the path references a classpath resource rather than a filesystem path (e.g. if you've bundled the JKS file into your application's JAR, WAR, or EAR file).

NOTE: JKS-based config trumps PEM-based config (see below). If for some reason you build an SslConfig object with both JKS and PEM data present, then only the JKS data will be used. You cannot "mix-and-match", providing a JKS-based truststore and PEM-based client auth data.

OpenSSL (PEM) based config

To supply Vault's server-side certificate for basic SSL, you can use one of the following three options:

.pemFile(path) - Supply the path to an X.509 certificate in unencrypted PEM format, using UTF-8 encoding (defaults to "VAULT_SSL_CERT" environment variable).

.pemResource(path) - Same as above, but the path references a classpath resource rather than a filesystem path (e.g. if you've bundled the PEM file into your applications's JAR, WAR, or EAR file).

.pemUTF8(contents) - The string contents extracted from the PEM file. For Java to parse the certificate properly, there must be a line-break in between the certificate header and body (see the SslConfig Javadocs for more detail).

If SSL verification is enabled, no JKS-based config is provided, AND none of these three methods are called, then SslConfig will by default check for a VAULT_SSL_CERT environment variable. If that's setw then it will be treated as a filesystem path.

To use Vault's TLS Certificate auth backend for SSL client auth, you must provide your client certificate and private key, using some pair from the following options:

.clientPemUTF8(path) - Supply the path to an X.509 certificate in unencrypted PEM format, using UTF-8 encoding.

.clientPemResource(path) - Same as above, but the path references a classpath resource rather than a filesystem path (e.g. if you've bundled the PEM file into your applications's JAR, WAR, or EAR file).

.clientPemUTF8(contents) - The string contents extracted from the PEM file. For Java to parse the certificate properly, there must be a line-break in between the certificate header and body (see the SslConfig Javadocs for more detail).

.clientKeyPemUTF8(path) - Supply the path to an RSA private key in unencrypted PEM format, using UTF-8 encoding.

.clientKeyPemResource(path) - Same as above, but the path references a classpath resource rather than a filesystem path (e.g. if you've bundled the PEM file into your applications's JAR, WAR, or EAR file).

.clientKeyPemUTF8(contents) - The string contents extracted from the PEM file. For Java to parse the certificate properly, there must be a line-break in between the certificate header and body (see the SslConfig Javadocs for more detail).

Using the Driver

Like the VaultConfig class, Vault too supports a builder pattern DSL style:

final Map<String, String> secrets = new HashMap<String, String>();
secrets.put("value", "world");
secrets.put("other_value", "You can store multiple name/value pairs under a single key");

// Write operation
final LogicalResponse writeResponse = vault.logical()
                                        .write("secret/hello", secrets);

...

// Read operation
final String value = vault.logical()
                       .read("secret/hello")
                       .getData().get("value");

Vault has a number of methods for accessing the classes that implement the various endpoints of Vault's HTTP API:

  • logical(): Contains core operations such as reading and writing secrets.
  • auth(): Exposes methods for working with Vault's various auth backends (e.g. to programmatically retrieve a token by authenticating with a username and password).
  • pki(): Operations on the PKI backend (e.g. create and delete roles, issue certificate credentials).
  • debug(): Health check endpoints.

The driver DSL also allows you to specify retry logic, by chaining the withRetries() ahead of accessing the endpoint implementation:

// Retry up to 5 times if failures occur, waiting 1000 milliseconds in between each retry attempt.
final LogicalResponse response = vault.withRetries(5, 1000)
                                   .logical()
                                   .read("secret/hello");

API Reference (Javadocs)

Full Javadoc documentation.

Version History

Note that changes to the major version (i.e. the first number) represent possible breaking changes, and may require modifications in your code to migrate. Changes to the minor version (i.e. the second number) should represent non-breaking changes. The third number represents any very minor bugfix patches.

  • 5.1.0: This release contains the following updates:

    • Supports path prefixes when using K/V engine V2. (PR #189)
    • Fixes issues with bulk requests in the transit API. (PR #195)
    • Adds response body to exception for Auth failures. (PR #198)
    • Support all options for the createToken operation. (PR #199)
  • 5.0.0: This release contains the following updates:

    • Changes the retry behavior, to no longer attempt retries on 4xx response codes (for which retries generally won't succeed anyway). This is the only (mildly) breaking change in this release, necessitating a major version bump. (PR #176)
    • Implements support for the Database secret engine. (PR #175)
    • Makes the "x-vault-token" header optional, to allow use of Vault Agent. (PR #184)
    • Removes stray uses of System.out.println in favor of java.util.logging. (PR #178)
    • Adds the enum constant MountType.KEY_VALUE_V2. (PR #182)
  • 4.1.0: This release contains the following updates:

    • Support for JWT authentication, for use by Kubernetes and other JWT-based authentication providers. (PR #164)
    • Updates the lease revoke method, to support changes in the underlying Vault API. (PR #163)
    • Changes the VaultConfig.secretsEnginePathMap(...) method from default access level to public, to allow for manual setting (PR #164)
    • Adds the nonce value to AuthResponse, to facilitate re-authentication with Vault via AWS. (PR #168)
    • Establishes a module-info file, updates the JDK requirement for building this library to Java 9 (although the built library artifact remains compatible as a dependency in Java 8 projects). (PR #165)
    • Updates Gradle, and various test dependencies to their latest versions. Integration tests now target Vault 1.1.3.
  • 4.0.0: This is a breaking-change release, with two primary updates:

    • Adds support for Version 2 of the Key/Value Secrets Engine. The driver now assumes that your Vault instance uses Version 2 of the Key/Value Secrets Engine across the board. To configure this, see the Key/Value Secret Engine Config section above.
    • Adds support for the namespaces feature of Vault Enterprise.
  • 3.1.0: Several updates.

    • Adds support for seal-related operations (i.e. /sys/seal, /sys/unseal, /sys/seal-status).
    • Adds support for the AWS auth backend.
    • Adds support for the Google Cloud Platform auth backend.
    • Adds support for the LDAP auth Backend.
    • Allows auth backend methods to be configured for non-default mount points.
    • Adds "revoke-self" capability for auth tokens.
    • Adds support for response-wrapping token validation
    • Support for signing a new certificate based on a CSR (i.e. the /v1/pki/sign endpoint).
    • Support for the PKI backend revoke method, and addition of a useCsrSans property in PKI role object
    • Gives VaultConfig the ability to disable loading from environment variables if desired.
    • Cleans up issues with the new Docker-based integration test suite.
    • Updates all dependencies to their latest versions (including switching to Vault 0.9.1 for integration testing).
  • 3.0.0: This is a breaking-change release, with several updates.

    • API changes:
      • Adds support for writing arbitrary objects to Vault, instead of just strings (i.e. the com.bettercloud.vault.api.Logical.write(...) method now accepts a Map<String. Object> rather than a Map<String, String>).
      • Refactors the VaultConfig class, forcing use of the builder pattern and breaking off SSL-related config into a separate SslConfig class.
      • Refactors the Auth.createToken() method, to encapsulate the possible options within a config object rather than having the method signature contain 8 optional arguments.
    • SSL and Auth Backend support:
      • Adds support for authenticating with the TLS Certificate auth backend.
      • Updates SSL support in general, allowing users to configure the driver with Java-friendly JKS keystore and truststore files (in addition to continuing to support Vault-friendly PEM format).
      • Implements the /v1/auth/token/lookup-self endpoint.
      • Supports creating tokens against a role.
    • Major re-vamp of the integration test suite:
      • The tests now use the TestContainers library to setup and launch a Vault server instance from within a Docker container, in a completely automated manner. You no longer have to manually configure and run a Vault server to use the test suite!
      • The tests are now going against a regular Vault server, rather than one running in "dev mode". Therefore, they are now able to use HTTPS connections rather than plain HTTP.
      • Upgrades tests to use Java 8 (although the library itself still targets Java 7).
    • Misc / quality-of-life:
      • Includes the REST response body in VaultException messages for basic read and write operations.
      • Makes numerous classes implement Serializable.
      • Upgrades the project to Gradle 4.0.
  • 2.0.0: This is breaking-change release, with numerous deprecated items cleaned up.

    • Adds support for authentication via the AppRole auth backend.
    • Adds support for renewing secret leases.
    • Removes the com.bettercloud.vault.api.Sys class, deprecated in the 1.2.0 release.
    • Removes the com.bettercloud.vault.api.Auth.loginByUsernamePassword method, deprecated in the 1.2.0 release.
    • Removes the fields leaseId, leaseDuration, and renewable from the VaultResponse base class, instead including them only in the subclasses for specific response types where they are found.
    • Changes the com.bettercloud.vault.response.AuthReponse class field authLeaseDuration from type int to long.
    • Refactors and removes various deprecated private methods, with no change to the exposed API.
  • 1.2.0: This is a substantial release, with numerous additions. It's a minor version number only because there should be no breaking changes. The changes include the following:

    • Switches from Vault 0.5.x to 0.6.x for automated tests.
    • Adds a field to VaultException for capturing the HTTP response code (if any) from Vault.
    • Updates the Gradle build, so that you no longer need empty placeholder values for certain variables elsewhere in your environment.
    • Updates integration test suite to account for breaking changes in Vault 0.6.x (e.g. you can no longer use a token that was obtained from one of the authentication backends to perform tasks such as creating and deleting roles, etc).
    • Deprecates the App ID authentication backend, and adds a new version of the Userpass authentication backend that doesn't require a path prefix. Adds support for the GitHub authentication backend.
    • If the VAULT_TOKEN environment parameter is not set, then the driver will now check for a file named .vault-token in the executing user's home directory, and try to read a token value from that.
    • Deprecates the com.bettercloud.vault.api.Sys class, moving the debug-related methods into their own specific com.bettercloud.vault.api.Debug class instead.
    • Implements some of the lease related endpoints (i.e. revoke, revoke-prefix, revoke-force).
    • Supports PKI backends that are mounted on non-default paths.
  • 1.1.1: Changes the ttl argument to Pki.issue() from Integer to String, to fix a bug preventing you from specifying the time suffix (e.g. "1h").

  • 1.1.0: Switches from Vault 0.4.x to 0.5.x for automated tests. Adds support to the Logical API wrapper for listing and deleting secrets. Implements the /v1/sys/health health-check HTTP API endpoint. Implements portions of the PKI backend (e.g. creating and deleting roles, issuing credentials). Marks numerous methods as deprecated, to be removed in a future major release.

  • 1.0.0: Drops support for Java 6. Removes all methods marked as @Deprecated in version 0.5.0. Adds support for response metadata (i.e. "lease_id", "renewable", "lease_duration") to all response types, rather than just AuthResponse. Changes leaseDuration type from int to Long in AuthResponse. Removes final declarations on all classes (outside of the JSON package). Various bugfixes. Adds support for auth token self-renewal. Adds support for writing values that return content.

  • 0.5.0: Adds support for supplying SSL certificates, and for toggling whether or not the Vault server's SSL certificate will be verified. Also adds support for "openTimeout" and "readTimeout" settings. Deprecates the "timeout", "sslTimeout", "proxyAddress", "proxyPort", "proxyUsername", and "proxyPassword" settings (the proxy settings may return in a future version, but it's too misleading to have methods exposed for settings that won't really be supported soon).

  • 0.3.0: Initial public release. Support for writing and reading secrets, authenticating with the "AppID" or "Username & Password" auth backends. All over-the-wire methods support automatic retry logic.

Development

Pull requests are welcomed for bugfixes or enhancements that do not alter the external facing class and method signatures. For any breaking changes that would alter the contract provided by this driver, please open up an issue to discuss it first.

All code changes should include unit test and/or integration test coverage as appropriate. Unit tests are any that can be run in isolation, with no external dependencies. Integration tests are those which require a Vault server instance (at least a Dev Server) up and running.

Unit tests are located under the src/test directory, and can be run with the Grade unitTest task.

Integration tests are located under the src/test-integration directory, and can be run with the Gradle integrationTest task. See the additional README.md file in this directory for more detailed information.

Although this library now includes a module-info class for use by Java 9+, the library currently targets Java 8 compatibility. Please do not attempt to introduce any features or syntax not compatible with Java 8 (the Gradle build script should prevent you from doing so without modification).

License

The MIT License (MIT)

Copyright (c) 2016-2019 BetterCloud

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Other Notes

The Vault server system itself is a product of HashiCorp, a completely separate organization.

This client driver adapts JSON parsing code from Ralf Sternberg's excellent minimal-json library, likewise available under the MIT License. Package names have all been changed, to prevent any conflicts should you happen to be using a different version of that library elsewhere in your project dependencies.

vault-java-driver's People

Contributors

adkhare avatar aiwaniuk avatar bitbrain avatar danharris-asurion avatar dubreuia avatar eirslett avatar elupu avatar expeddrewery avatar eyal-lupu avatar gm2211 avatar goodrum avatar happybob007 avatar innossh avatar issacg avatar jarrodcodes avatar jeinwag avatar jetersen avatar markussammallahti avatar mikegrass avatar mrunesson avatar naktibalda avatar nathanc avatar paulburlumi avatar robm-openjdk avatar schulzh avatar sinneduy avatar steve-perkins avatar steve-perkins-bc avatar vladrassokhin avatar zglozman avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

vault-java-driver's Issues

Support Response Wrapping

There's great feature introduced recently named 'Resource Wrapping'.
It would be useful to support in in Java client library.

Use case:

  1. Service A authenticates with /auth/approle/login endpoint providing App Role role_id, secret_id and X-Vault-Wrap-TTL header.
  2. Response contains AccessorID and WrappedTokenID, first one left on Service A, second passed to Service B.
  3. Service B connects to Vault server and performs GET on /sys/wrapping/unwrap?token=${WrappedTokenId} and receives proper token, then reconfigures itself and proceeds with operations.

Example was shown during Securing Applications Using Nomad
presentation.

Tag 1.1.0

Could you please tag the v1.1.0 version? Keep up the good work!

LogicalResponse doesn't include lease information

It seems that LogicalResponse only includes the data portion of the REST reply.

It does not include important metadata such as:

"lease_id": "<secret backend>/creds/<role>/<uuid>",
"renewable": true,
"lease_duration": 3600,

This metadata is necessary in order for an application to honor the TTL for credentials.

I'm happy to submit a pull request if that seems reasonable. These would be provided as new properties of LogicalResponse.

Adding body to Pki Respone exceptions:

currently, most exceptions are returned like this:
throw new VaultException("Vault responded with HTTP status code: " + restResponse.getStatus(), restResponse.getStatus());

as such it is impossible to debug from calling code, as the body is not included in the exception, how do you guys feel about changing to restResponse.getBody()?

Cannot compile code with gradle

Steps to reproduce:

  • Clone the project
  • Execute gradle compile
  • Expected: the code compiles, Actual: exception
gradle compileOnly

FAILURE: Build failed with an exception.

* Where:
Build file '/home/alexandre/Documents/project/vault-java-driver/build.gradle' line: 79

* What went wrong:
A problem occurred evaluating root project 'vault-java-driver'.
> Could not get unknown property 'classesDirs' for unit tests classes of type org.gradle.api.internal.tasks.DefaultSourceSetOutput.

* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.

BUILD FAILED

Total time: 0.577 secs

NullPointerException if Vault token is specified during initialization

Setup Vault as explained at:

https://github.com/arun-gupta/kubernetes-aws-workshop/tree/master/config-secrets#vault

Java application deployed as Pod in Kubernetes that reads secrets from Vault is:

https://github.com/arun-gupta/java-app-secrets/blob/master/src/main/java/org/examples/java/App.java

Pod spec is:

apiVersion: v1
kind: Pod
metadata:
  name: java-app-secrets
spec:
  containers:
  - name: java-app-secrets
    image: arungupta/java-app-secrets:latest
    env:
      - name: VAULT_ADDR
        value: http://ec2-54-237-223-40.compute-1.amazonaws.com:8200
      - name: VAULT_TOKEN
        value: 4e93b3c6-c459-f166-e7e9-6c48044cfdb6
  restartPolicy: Never

VaultConfig initialized as shown below works and is able to retrieve the secrets:

final VaultConfig config = new VaultConfig()
                .address(System.getenv("VAULT_ADDR"))
                .token(System.getenv("VAULT_TOKEN"))
                .sslConfig(new SslConfig().verify(false).build())
                .build();

VaultConfig initialized as shown below returns a NPE:

final VaultConfig config = new VaultConfig()
                .address(System.getenv("VAULT_ADDR"))
                .sslConfig(new SslConfig().verify(false).build())
                .build();

Here is the NPE:

Exception in thread "main" com.bettercloud.vault.VaultException: java.lang.NullPointerException
	at com.bettercloud.vault.api.Logical.read(Logical.java:84)
	at org.examples.java.App.main(App.java:26)
Caused by: java.lang.NullPointerException
	at java.net.URLEncoder.encode(URLEncoder.java:204)
	at com.bettercloud.vault.rest.Rest.header(Rest.java:173)
	at com.bettercloud.vault.api.Logical.read(Logical.java:56)
	... 1 more

SslConfig does not support TLSv1.2

Line 518 hard-codes "TLS". This breaks my ability to use TLSv1.2. Overriding it to TLSv1.2 in a debugger allows me to get past errors like:

http: TLS handshake error from XXXXXXX: tls: client offered an unsupported, maximum protocol version of 301

Unable to run the new integrationTest suite on MacOS

Hi there!

I've forked this repo to extend the API endpoint coverage to sys/policy for getting/creating policies, but I'm having trouble running 3.0.0's integration test suite. I have a strong feeling that the problem may be PEBKAC but I wanted to bring it to your attention in the event that I'm wrong.

Here's an example stack trace that I'm seeing:

17:26:19.664 [DEBUG] [org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor] Executing test class com.bettercloud.vault.api.AuthBackendAppRoleTests
17:26:14.533 [null] [org.gradle.internal.progress.DefaultBuildOperationExecutor] 
> Task :integrationTest
17:26:19.660 [DEBUG] [TestEventLogger]     [Test worker] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy - Could not find a valid Docker environment. Please check configuration. Attempted configurations were:
17:26:19.660 [DEBUG] [TestEventLogger]     [Test worker] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy -     EnvironmentAndSystemPropertyClientProviderStrategy: failed with exception ExceptionInInitializerError (null). Root cause IllegalStateException (Only supported on Linux)
17:26:19.660 [DEBUG] [TestEventLogger]     [Test worker] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy -     UnixSocketClientProviderStrategy: failed with exception InvalidConfigurationException (this strategy is only applicable to Linux)
17:26:19.661 [DEBUG] [TestEventLogger]     [Test worker] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy -     ProxiedUnixSocketClientProviderStrategy: failed with exception IllegalArgumentException (Socket file does not exist: /var/run/docker.sock)
17:26:19.661 [DEBUG] [TestEventLogger]     [Test worker] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy -     DockerMachineClientProviderStrategy: failed with exception InvalidConfigurationException (Exception when executing docker-machine status )
17:26:19.662 [DEBUG] [TestEventLogger]     [Test worker] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy -     WindowsClientProviderStrategy: failed with exception TimeoutException (Timeout waiting for result with exception). Root cause ConnectException (Connection refused: localhost/127.0.0.1:2375)
17:26:19.662 [DEBUG] [TestEventLogger]     [Test worker] ERROR org.testcontainers.dockerclient.DockerClientProviderStrategy - As no valid configuration was found, execution cannot continue
17:26:19.664 [DEBUG] [TestEventLogger] 
17:26:19.664 [DEBUG] [TestEventLogger] com.bettercloud.vault.api.AuthBackendAppIdTests > classMethod STARTED
17:26:19.692 [DEBUG] [TestEventLogger] 
17:26:19.692 [DEBUG] [TestEventLogger] com.bettercloud.vault.api.AuthBackendAppIdTests > classMethod FAILED
17:26:19.692 [DEBUG] [TestEventLogger]     java.lang.ExceptionInInitializerError
17:26:19.692 [DEBUG] [TestEventLogger]         at sun.misc.Unsafe.ensureClassInitialized(Native Method)
17:26:19.692 [DEBUG] [TestEventLogger]         at sun.reflect.UnsafeFieldAccessorFactory.newFieldAccessor(UnsafeFieldAccessorFactory.java:43)
17:26:19.692 [DEBUG] [TestEventLogger]         at sun.reflect.ReflectionFactory.newFieldAccessor(ReflectionFactory.java:156)
17:26:19.692 [DEBUG] [TestEventLogger]         at java.lang.reflect.Field.acquireFieldAccessor(Field.java:1088)
17:26:19.692 [DEBUG] [TestEventLogger]         at java.lang.reflect.Field.getFieldAccessor(Field.java:1069)
17:26:19.692 [DEBUG] [TestEventLogger]         at java.lang.reflect.Field.get(Field.java:393)
17:26:19.692 [DEBUG] [TestEventLogger]         at org.junit.runners.model.FrameworkField.get(FrameworkField.java:73)
17:26:19.692 [DEBUG] [TestEventLogger]         at org.junit.runners.model.TestClass.getAnnotatedFieldValues(TestClass.java:230)
17:26:19.692 [DEBUG] [TestEventLogger]         at org.junit.runners.ParentRunner.classRules(ParentRunner.java:255)
17:26:19.692 [DEBUG] [TestEventLogger]         at org.junit.runners.ParentRunner.withClassRules(ParentRunner.java:244)
17:26:19.692 [DEBUG] [TestEventLogger]         at org.junit.runners.ParentRunner.classBlock(ParentRunner.java:194)
17:26:19.692 [DEBUG] [TestEventLogger]         at org.junit.runners.ParentRunner.run(ParentRunner.java:362)
17:26:19.692 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:114)
17:26:19.692 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:57)
17:26:19.692 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66)
17:26:19.692 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
17:26:19.692 [DEBUG] [TestEventLogger]         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
17:26:19.692 [DEBUG] [TestEventLogger]         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
17:26:19.692 [DEBUG] [TestEventLogger]         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
17:26:19.692 [DEBUG] [TestEventLogger]         at java.lang.reflect.Method.invoke(Method.java:498)
17:26:19.692 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
17:26:19.693 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
17:26:19.693 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
17:26:19.693 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
17:26:19.693 [DEBUG] [TestEventLogger]         at com.sun.proxy.$Proxy3.processTestClass(Unknown Source)
17:26:19.693 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:109)
17:26:19.693 [DEBUG] [TestEventLogger]         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
17:26:19.693 [DEBUG] [TestEventLogger]         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
17:26:19.693 [DEBUG] [TestEventLogger]         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
17:26:19.693 [DEBUG] [TestEventLogger]         at java.lang.reflect.Method.invoke(Method.java:498)
17:26:19.693 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
17:26:19.693 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
17:26:19.693 [DEBUG] [TestEventLogger]         at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:146)
17:26:19.693 [DEBUG] [TestEventLogger]         at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:128)
17:26:19.693 [DEBUG] [TestEventLogger]         at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
17:26:19.693 [DEBUG] [TestEventLogger]         at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
17:26:19.693 [DEBUG] [TestEventLogger]         at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:46)
17:26:19.693 [DEBUG] [TestEventLogger]         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
17:26:19.693 [DEBUG] [TestEventLogger]         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
17:26:19.693 [DEBUG] [TestEventLogger]         at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
17:26:19.693 [DEBUG] [TestEventLogger]         at java.lang.Thread.run(Thread.java:748)
17:26:19.693 [DEBUG] [TestEventLogger] 
17:26:19.693 [DEBUG] [TestEventLogger]         Caused by:
17:26:19.693 [DEBUG] [TestEventLogger]         java.lang.IllegalStateException: Could not find a valid Docker environment. Please see logs and check configuration
17:26:19.693 [DEBUG] [TestEventLogger]             at org.testcontainers.dockerclient.DockerClientProviderStrategy.getFirstValidStrategy(DockerClientProviderStrategy.java:90)
17:26:19.693 [DEBUG] [TestEventLogger]             at org.testcontainers.DockerClientFactory.client(DockerClientFactory.java:83)
17:26:19.693 [DEBUG] [TestEventLogger]             at org.testcontainers.containers.GenericContainer.<init>(GenericContainer.java:111)
17:26:19.693 [DEBUG] [TestEventLogger]             at com.bettercloud.vault.api.VaultContainer.<init>(VaultContainer.java:105)
17:26:19.693 [DEBUG] [TestEventLogger]             at com.bettercloud.vault.api.AuthBackendAppIdTests.<clinit>(AuthBackendAppIdTests.java:18)
17:26:19.693 [DEBUG] [TestEventLogger]             ... 41 more
17:26:19.694 [DEBUG] [TestEventLogger] 
17:26:19.694 [DEBUG] [TestEventLogger] com.bettercloud.vault.api.AuthBackendAppIdTests FAILED

I did some browsing of TestContainer issues for similar stuff, and it looked like a similar problem was resolved for those who made sure docker-machine was available on their path. I confirmed both docker and docker-machine are available:

bmcfeeley-rMBP:vault-java-driver bmcfeeley$ which docker
/usr/local/bin/docker
bmcfeeley-rMBP:vault-java-driver bmcfeeley$ which docker-machine
/usr/local/bin/docker-machine

I couldn't find any more docs to see if I'm missing some setup or configuration piece of the puzzle so I thought I'd give you a shout.

Hope you have a nice weekend!

Support for App-ID

I took a quick look around the code and it looks like it's focussed around using tokens as the only auth method. I was wondering if there was any plan to support app-id?

We're running on Docker where passing the token via ENV doesn't seem to be the best of ideas. App-id seems like a reasonable compromise sitting in between cubbyhole and tokens.

[Documentation] Constructor of VaultConfig no longer accepts parameters

In v3.0.0 constructor of VaultConfig no longer accepts parameters.

Result: groovy.lang.GroovyRuntimeException: Could not find matching constructor for: com.bettercloud.vault.VaultConfig(java.lang.String, java.lang.String)

However the first 2 examples in documentation still use constructor parameters
https://github.com/BetterCloud/vault-java-driver/blob/v3.0.0/README.md#initializing-a-driver-instance

final VaultConfig config = new VaultConfig("http://127.0.0.1:8200", "3c9fd6be-7bc2-9d1f-6fb3-cd746c0fc4e8");

// You may choose not to provide a root token initially, if you plan to use
// the Vault driver to retrieve one programmatically from an auth backend.
final VaultConfig config = new VaultConfig("http://127.0.0.1:8200");

Improve resilience when client can not connect?

Right now (v. 1.2.0) there will be thrown a VaultException when calling vault.logical().write(...) when the client can not connect. The problem is that, for instance, a token (passed by the VaultConfig) may not be valid anymore (because it is expired or configuration on the server has changed), or the Vault server might be offline for a few seconds which leads to the same outcome (depending on the timeouts).

My question is: should this driver take the responsibility (e.g. by using Hystrix) to ensure reconnecting properly/recovering? Or should I write a "wrapper" around this driver to ensure connectivity and retrying? I don't know if this would be out-of-scope for this project.

๐Ÿ’ฌ Discussion appreciated.

Unexpected end of file from server

Hi,

I starting to play around with vault and used these examples to connect to a vault instance running out of a docker container on my local system.

I am using this command to start the docker container - docker run -p 8200:8200 --cap-add IPC_LOCK -e 'VAULT_LOCAL_CONFIG={"backend": {"file": {"path": "/vault/file"}}, "default_lease_ttl": "168h", "max_lease_ttl": "720h", "listener": {"tcp":{"address":"127.0.0.1:8200", "tls_disable": "true"}}}' --hostname vault --name vault docker.artifactory.abc/shared/vault:0.9.5 server

Post which, I am logging into the container, and executing the following commands to initialise the vault -

/ # export VAULT_ADDR=http://127.0.0.1:8200
/ # vault operator init

From the output of this command, I note the root token and am using it for the test case outlined below.

Now, I have the following test case and intend to write to the vault and read from it.

package abc.co.nz;

import org.junit.Test;
import org.junit.Assert.*;
import static org.junit.Assert.assertEquals;

import java.util.Map;
import java.util.HashMap;
import com.bettercloud.vault.response.*;
import com.bettercloud.vault.*;


public class VaultTest {

  @Test
  public void testVault() throws Exception {
    final VaultConfig config = new VaultConfig()
            .address("http://127.0.0.1:8200")
            .token("6d14c36e-c975-ee50-62fc-fd3afefad4e0")
            .openTimeout(5)
            .readTimeout(3)
            .sslConfig(new SslConfig().verify(false).build())
            .build();

    final Vault vault = new Vault(config);
    final Map<String, Object> secrets = new HashMap<String, Object>();
    secrets.put("value", "world");
    secrets.put("other_value", "You can store multiple name/value pairs under a single key");

    // Write operation
    final LogicalResponse writeResponse = vault.logical().write("secret/hello", secrets);

    // Read operation
    //return vault.logical().read("secret/hello").getData().get("value");

    assertEquals("world", vault.logical().read("secret/hello").getData().get("value"));
  }
}

I get an error - com.bettercloud.vault.VaultException: com.bettercloud.vault.rest.RestException: java.net.SocketException: Unexpected end of file from server

What am I missing?

need aws-ec2 auth support

In order to work with ec2 roles, it needs a new auth method as it require different parameters, pkcs7 and nonce

Add root/intermediate CA to vault

I couldn't find the APIs to do this. Is there a way for me to create and add a new root CA/intermediate CA to vault and then get certs issued from them?

License clarification

Hello there! Thanks for building this! Would it be possible to clarify the license? Maybe by adding a LICENSE file to the root of the repository?

Permission denied error could be handled better

Vault returns status 400 with body {"errors":["permission denied"]} if access token is incorrect or has insufficient permissions.

On receiving such status, vault-java-driver throws com.bettercloud.vault.VaultException: Expecting HTTP status 204 or 200, but instead receiving 400.
Which is very confusing, because Vault documentation says that it means 400 - Invalid request, missing or invalid data. See the "validation" section for more details on the error response.
And it doesn't provide any more details.

vault-java-driver makes it hard to debug by not exposing response body.
I think that vault-java-driver should incorporate contents of errors field in exception message.

Add support for /auth/token/create/[role_name] in Auth

Hi there,

Vault introduced the concept of roles in 0.6.0, with a new auth API endpoint in /auth/token/create/[role_name]. It'll be good if the Auth class would support a method that hits that endpoint directly.

I am internally writing something to provide this, and I'll look to submit a PR at a later date if others haven't gotten to it yet later.

Thanks,
Alex

Support overriding`VaultConfig#environmentLoader` with NoOp loader

I'd suggest to make VaultConfig#environmentLoader public and add NoOpEnvironmentLoader class that actually wouldn't load anything or maybe some another approach.

Use case: I'd like to be sure that environment cannot affect code.
As workaround next code works:

final VaultConfig config = new VaultConfig() {{
    environmentLoader(new EnvironmentLoader(){
        @Override
        public String loadVariable(String name) {
            return null;
        }
    });
}}

Please make less classes 'final'

This makes it very difficult to unit test. It can be impossible to mock out the library, instead requiring the user to wrap every type in their own types so that they can be mocked out.

Any plans to support token renew?

A feature that would be very useful to us would be /auth/token/renew-self. Is there any interest or plans to include this?

I would be willing to help with a PR if necessary, but it looked like a new type of API request (and so I am not sure how exactly you would want it to fit into the library). Would it make sense to make this accessible from Logical?

New feature: encrypt / decrypt in batch mode

In Vault transit/encrypt or transit/decrypt rest APIs, the batch mode is supported by specifying {"batch_input":[{"ciphertext": ""}, {"ciphertext": ""}]} in the request body. However, the write function in Logical.java does not support batch mode. Could we provide a write function that takes as input a path and a body bytes, for example write(String path, byte[] body) that will support any kind of rest API call?

error running integration test on MacOS

Hi, I'm trying to run the integration test on my mac, but all the test that involve the Vault TestContainer returns the following errors

13:45:28.612 [DEBUG] [TestEventLogger] com.bettercloud.vault.api.LeasesTests STARTED
13:45:34.440 [DEBUG] [TestEventLogger] 
13:45:34.440 [DEBUG] [TestEventLogger] com.bettercloud.vault.api.LeasesTests STANDARD_ERROR
13:45:34.440 [DEBUG] [TestEventLogger]     [Test worker] INFO ๐Ÿณ [vault:0.7.3] - Creating container for image: vault:0.7.3
13:45:34.571 [DEBUG] [TestEventLogger]     [Test worker] INFO ๐Ÿณ [vault:0.7.3] - Starting container with ID: 9dfa9693d83f196eaede324c33cc96f5695d6cc3740d8ba186fb3839329bdfef
13:45:34.820 [DEBUG] [TestEventLogger]     [Test worker] INFO ๐Ÿณ [vault:0.7.3] - Container vault:0.7.3 is starting: 9dfa9693d83f196eaede324c33cc96f5695d6cc3740d8ba186fb3839329bdfef
13:45:34.827 [DEBUG] [TestEventLogger]     [Test worker] INFO ๐Ÿณ [vault:0.7.3] - Container vault:0.7.3 started
13:45:34.827 [DEBUG] [TestEventLogger]     [Test worker] INFO com.bettercloud.vault.api.VaultContainer - Command: vault init -ca-cert=/vault/config/ssl/cert.pem -key-shares=1 -key-threshold=1
13:45:34.900 [DEBUG] [TestEventLogger]     [Test worker] INFO com.bettercloud.vault.api.VaultContainer - Command stdout: 
13:45:34.900 [DEBUG] [TestEventLogger]     [Test worker] INFO com.bettercloud.vault.api.VaultContainer - Command stderr: Error initializing Vault: Put https://127.0.0.1:8200/v1/sys/init: dial tcp 127.0.0.1:8200: getsockopt: connection refused
13:45:34.900 [DEBUG] [TestEventLogger] 
13:45:35.292 [DEBUG] [TestEventLogger] 
13:45:35.292 [DEBUG] [TestEventLogger] com.bettercloud.vault.api.LeasesTests > classMethod STARTED
13:45:35.292 [null] [org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor] 
13:45:35.292 [DEBUG] [org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor] Executing test class com.bettercloud.vault.api.LogicalTests
13:45:36.096 [DEBUG] [org.gradle.process.internal.health.memory.MemoryManager] Emitting OS memory status event {Total: 17179869184, Free: 8128143360}
13:45:36.096 [DEBUG] [org.gradle.launcher.daemon.server.health.LowMemoryDaemonExpirationStrategy] Received memory status update: {Total: 17179869184, Free: 8128143360}
13:45:36.096 [DEBUG] [org.gradle.process.internal.health.memory.MemoryManager] Emitting JVM memory status event {Maximum: 954728448, Committed: 574095360}
13:45:38.756 [DEBUG] [org.gradle.launcher.daemon.server.Daemon] DaemonExpirationPeriodicCheck running
13:45:38.756 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Waiting to acquire shared lock on daemon addresses registry.
13:45:38.756 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Lock acquired on daemon addresses registry.
13:45:38.756 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Releasing lock on daemon addresses registry.
13:45:38.756 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Waiting to acquire shared lock on daemon addresses registry.
13:45:38.756 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Lock acquired on daemon addresses registry.
13:45:38.756 [DEBUG] [org.gradle.cache.internal.DefaultFileLockManager] Releasing lock on daemon addresses registry.
13:45:35.386 [null] [org.gradle.internal.progress.DefaultBuildOperationExecutor] 
> Task :integrationTest
13:45:35.294 [DEBUG] [TestEventLogger] 
13:45:35.294 [DEBUG] [TestEventLogger] com.bettercloud.vault.api.LeasesTests > classMethod FAILED
13:45:35.294 [DEBUG] [TestEventLogger]     java.lang.ArrayIndexOutOfBoundsException: 1
13:45:35.294 [DEBUG] [TestEventLogger]         at com.bettercloud.vault.api.VaultContainer.initAndUnsealVault(VaultContainer.java:154)
13:45:35.294 [DEBUG] [TestEventLogger]         at com.bettercloud.vault.api.LeasesTests.setupClass(LeasesTests.java:43)
13:45:35.294 [DEBUG] [TestEventLogger]         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
13:45:35.294 [DEBUG] [TestEventLogger]         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
13:45:35.294 [DEBUG] [TestEventLogger]         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
13:45:35.294 [DEBUG] [TestEventLogger]         at java.lang.reflect.Method.invoke(Method.java:498)
13:45:35.294 [DEBUG] [TestEventLogger]         at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
13:45:35.294 [DEBUG] [TestEventLogger]         at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
13:45:35.294 [DEBUG] [TestEventLogger]         at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
13:45:35.294 [DEBUG] [TestEventLogger]         at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:24)
13:45:35.294 [DEBUG] [TestEventLogger]         at org.testcontainers.containers.FailureDetectingExternalResource$1.evaluate(FailureDetectingExternalResource.java:30)
13:45:35.294 [DEBUG] [TestEventLogger]         at org.junit.rules.RunRules.evaluate(RunRules.java:20)
13:45:35.294 [DEBUG] [TestEventLogger]         at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
13:45:35.294 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:114)
13:45:35.294 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:57)
13:45:35.294 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66)
13:45:35.294 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
13:45:35.295 [DEBUG] [TestEventLogger]         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
13:45:35.295 [DEBUG] [TestEventLogger]         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
13:45:35.295 [DEBUG] [TestEventLogger]         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
13:45:35.295 [DEBUG] [TestEventLogger]         at java.lang.reflect.Method.invoke(Method.java:498)
13:45:35.295 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
13:45:35.295 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
13:45:35.295 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
13:45:35.295 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
13:45:35.295 [DEBUG] [TestEventLogger]         at com.sun.proxy.$Proxy3.processTestClass(Unknown Source)
13:45:35.295 [DEBUG] [TestEventLogger]         at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:108)
13:45:35.295 [DEBUG] [TestEventLogger]         at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
13:45:35.295 [DEBUG] [TestEventLogger]         at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
13:45:35.295 [DEBUG] [TestEventLogger]         at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
13:45:35.295 [DEBUG] [TestEventLogger]         at java.lang.reflect.Method.invoke(Method.java:498)
13:45:35.295 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
13:45:35.295 [DEBUG] [TestEventLogger]         at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
13:45:35.295 [DEBUG] [TestEventLogger]         at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:146)
13:45:35.295 [DEBUG] [TestEventLogger]         at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:128)
13:45:35.295 [DEBUG] [TestEventLogger]         at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:404)
13:45:35.295 [DEBUG] [TestEventLogger]         at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:63)
13:45:35.295 [DEBUG] [TestEventLogger]         at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:46)
13:45:35.295 [DEBUG] [TestEventLogger]         at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
13:45:35.295 [DEBUG] [TestEventLogger]         at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
13:45:35.295 [DEBUG] [TestEventLogger]         at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:55)
13:45:35.295 [DEBUG] [TestEventLogger]         at java.lang.Thread.run(Thread.java:748)
13:45:35.296 [DEBUG] [TestEventLogger] 
13:45:35.296 [DEBUG] [TestEventLogger] com.bettercloud.vault.api.LeasesTests FAILED

I tried to connect to the Vault container while it's running from outside the test, and I always get a connection refused error, with and without the mac firewall disabled.

My docker versions are:

docker-machine version 0.12.2, build 9371605
Docker version 17.09.0-ce, build afdb6d4

am I doing something wrong or missing something?

Keys with null values cause UnsupportedOperationException

When a logical().read() result includes a null value, the following error is thrown: java.lang.UnsupportedOperationException: Not a string: null

This is because the .asString() call fails.

One example where this is common is the aws backend as it returns 3 keys:
access_key
secret_key
security_token

security_token is always null if STS is not being used.

member.getValue() should include a .isNull() check before calling .asString() to ensure safety.

createToken missing explicit_max_ttl and renewable options

https://www.vaultproject.io/docs/auth/token.html#post includes options for an explicit_max_ttl and whether or not the token should be renewable. v3.0.0 of the vault-java-driver does not support these options. It would be great if these could be added.

Acceptance Criteria:
java-vault-driver should support all options for createToken

Suggested Tech Steps:
Update Auth.TokenRequest (

public static class TokenRequest implements Serializable {
) to take these parameters.

Tagging releases in git

Just curious about releasing this to maven central w/o a corresponding tag in git? We are looking to use your lib in production and we like to review version deltas. W/o tags in git it makes a bit more difficult to confirm what's actually on maven central.

Allow password protected truststores

Presently you can provide a keystore and a keystore password for client certificates, but you cannot supply a keystore password for a supplied truststore for CA certs. This is somewhat surprising since keytool will only make keystores that have a password on them. It seems somewhat trivial to add this, but since it technically changes the SslConfig API I figured it's worth opening here for discussion before submitting a PR.

Can't use PKI backends mounted at paths other than /pki

Let me begin by saying that this may just be a colossal misunderstanding on my part, but as near as I can tell, vault-java-driver's PKI support only works in cases where we're talking to a PKI backend mounted literally at /pki.

We've found ourselves in a situation where we have two pki backends (a root CA and an intermediate CA), and we'd like to issue certificates from the intermediate CA. The trouble is that we've given both of our backends descriptive paths (/root-ca and /intermediate-ca, for example), but the URLs constructed in Pki.java hard-code a path of pki.

I think it might make sense to take an optional path argument for the various PKI operations (that could reasonably default to pki for backward compatibility!) and construct URLs with that in mind. Seem legit?

Add support for AWS auth backend

Can you add support for the AWS Auth backend? My thought was another method on com.bettercloud.vault.api.Auth:

public AuthResponse loginByAwsIam(final String role, final String iamHttpRequestMethod, final String iamRquestUrl, final String iamRequestBody, final String iamRequestHeaders)

and

public AuthResponse loginByAwsEc2(final String role, final String pkcs7, final String nonce)

I'd be happy to submit a pull request if you'd consider it.

DISABLED_SSL_CONTEXT does not send server name for SNI

Hi,
The used SSL context does not send the SNIServerNames SSLProperty when SSL verification is disabled.
So when Vault is running e.g. in OpenShift it does not work because it does not know where to route the requests.

Basically the SSLSocketFactory needs to set the right server name to the created SSLSocket.
There is also the way to do this on our own by setting a custom SSLContext in the SSLConfig, but then the HostnameVerifier is not set to the one which just allows everything and it still fails.

I had to do a dirty hack to get it working and it will also only work for one vault connection in the whole JVM:

		final SSLContext sslContext = SSLContext.getInstance("TLS");
		SSLContextSpi spi = new SSLContextSpi() {

			@Override
			protected void engineInit(final KeyManager[] km, final TrustManager[] tm, final SecureRandom sr) throws KeyManagementException {
				sslContext.init(km, tm, sr);
			}

			@Override
			protected SSLSocketFactory engineGetSocketFactory() {
				SSLParameters params = new SSLParameters();
				params.setServerNames(Arrays.asList(new SNIHostName("my-vault-connector.foobar.com")));
				return new SSLSocketFactoryWrapper(sslContext.getSocketFactory(), params);
			}

			@Override
			protected SSLServerSocketFactory engineGetServerSocketFactory() {
				return sslContext.getServerSocketFactory();
			}

			@Override
			protected SSLSessionContext engineGetServerSessionContext() {
				return sslContext.getServerSessionContext();
			}

			@Override
			protected SSLSessionContext engineGetClientSessionContext() {
				return sslContext.getClientSessionContext();
			}

			@Override
			protected SSLEngine engineCreateSSLEngine(final String host, final int port) {
				return sslContext.createSSLEngine(host, port);
			}

			@Override
			protected SSLEngine engineCreateSSLEngine() {
				return sslContext.createSSLEngine();
			}
		};


		sslContext.init(null, new TrustManager[] { new X509TrustManager() {
			@Override
			public void checkClientTrusted(final X509Certificate[] x509Certificates, final String s)
					throws CertificateException {
			}

			@Override
			public void checkServerTrusted(final X509Certificate[] x509Certificates, final String s)
					throws CertificateException {
			}

			@Override
			public X509Certificate[] getAcceptedIssuers() {
				return new X509Certificate[0];
			}
		} }, new java.security.SecureRandom());

		class SSLContextWrapper extends SSLContext {
			public SSLContextWrapper(final SSLContextSpi spi, final java.security.Provider provider, final String protocol) {
				super(spi, provider, protocol);
			}
		}

		Field field = Rest.class.getDeclaredField("DISABLED_SSL_CONTEXT");
		field.setAccessible(true);
		field.set(null, new SSLContextWrapper(spi, sslContext.getProvider(), sslContext.getProtocol()));

		vault = new Vault(new VaultConfig()
				.address(address)
				.token(token)
				.sslConfig(new SslConfig().verify(false).build())
				.build());
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import java.net.UnknownHostException;

import javax.net.ssl.SSLParameters;
import javax.net.ssl.SSLSocket;
import javax.net.ssl.SSLSocketFactory;

public class SSLSocketFactoryWrapper extends SSLSocketFactory {

	private final SSLSocketFactory wrappedFactory;
	private final SSLParameters sslParameters;

	public SSLSocketFactoryWrapper(final SSLSocketFactory factory, final SSLParameters sslParameters) {
		this.wrappedFactory = factory;
		this.sslParameters = sslParameters;
	}

	@Override
	public Socket createSocket(final String host, final int port) throws IOException, UnknownHostException {
		SSLSocket socket = (SSLSocket) wrappedFactory.createSocket(host, port);
		socket.setSSLParameters(sslParameters);
		return socket;
	}

	@Override
	public Socket createSocket(final String host, final int port, final InetAddress localHost, final int localPort)
			throws IOException, UnknownHostException {
		SSLSocket socket = (SSLSocket) wrappedFactory.createSocket(host, port, localHost, localPort);
		socket.setSSLParameters(sslParameters);
		return socket;
	}

	@Override
	public Socket createSocket(final InetAddress host, final int port) throws IOException {
		SSLSocket socket = (SSLSocket) wrappedFactory.createSocket(host, port);
		socket.setSSLParameters(sslParameters);
		return socket;
	}

	@Override
	public Socket createSocket(final InetAddress address, final int port, final InetAddress localAddress,
			final int localPort) throws IOException {
		SSLSocket socket = (SSLSocket) wrappedFactory.createSocket(address, port, localAddress, localPort);
		socket.setSSLParameters(sslParameters);
		return socket;

	}

	@Override
	public Socket createSocket() throws IOException {
		SSLSocket socket = (SSLSocket) wrappedFactory.createSocket();
		socket.setSSLParameters(sslParameters);
		return socket;
	}

	@Override
	public String[] getDefaultCipherSuites() {
		return wrappedFactory.getDefaultCipherSuites();
	}

	@Override
	public String[] getSupportedCipherSuites() {
		return wrappedFactory.getSupportedCipherSuites();
	}

	@Override
	public Socket createSocket(final Socket s, final String host, final int port, final boolean autoClose)
			throws IOException {
		SSLSocket socket = (SSLSocket) wrappedFactory.createSocket(s, host, port, autoClose);
		socket.setSSLParameters(sslParameters);
		return socket;
	}

}

Use 'java-test-containers' for Integration Tests

Right now the integration tests expect a locally running Vault server and particular environment variables set. By using java-test-containers a Docker container is created when running the tests, all integration tests are running against a Vault dev server running in Docker. After all tests have run the container will be destroyed automatically.

arbitrary object value serialization for write()

Currently, logical().write() supports writing only string/string key-value pairs. However, Vault's API supports writing arbitrary JSON or YAML records into the database. In particular, this is important for arrays.

This API should reflect that, by changing the API as write(String path, Map<String, Object> keyValuePairs). This would be a source compatible change, and the objects could be internally converted to JSON.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.