Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 39 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ Table of Contents
-----------------
* [Installing the Driver](#installing-the-driver)
* [Initializing a Driver Instance](#initializing-a-driver-instance)
* [Key/Value Secret Engine Config](#key-value-secret-engine-config)
* [SSL Config](#ssl-config)
* [General Options](#general-options)
* [Java Keystore (JKS) based config](#java-keystore-jks-based-config)
Expand All @@ -30,7 +31,7 @@ The driver is available from Maven Central, for all modern Java build systems.
Gradle:
```
dependencies {
compile('com.bettercloud:vault-java-driver:3.1.0')
compile('com.bettercloud:vault-java-driver:4.1.0')
}
```

Expand All @@ -39,7 +40,7 @@ Maven:
<dependency>
<groupId>com.bettercloud</groupId>
<artifactId>vault-java-driver</artifactId>
<version>3.1.0</version>
<version>4.0.0</version>
</dependency>
```

Expand Down Expand Up @@ -81,6 +82,32 @@ 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](https://www.vaultproject.io/docs/secrets/kv/index.html). 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.


SSL Config
----------
If your Vault server uses a SSL certificate, then you must supply that certificate to establish connections. Also, if
Expand Down Expand Up @@ -222,6 +249,15 @@ Note that changes to the major version (i.e. the first number) represent possibl
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.

* **4.1.0**: New health code support:
* Adds support for the new [Vault health codes](https://www.vaultproject.io/api/system/health.html#parameters)

* **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](#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.
Expand Down Expand Up @@ -330,7 +366,7 @@ License
-------
The MIT License (MIT)

Copyright (c) 2016-2018 BetterCloud
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
Expand Down
7 changes: 6 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ apply plugin: 'signing'

group 'com.bettercloud'
archivesBaseName = 'vault-java-driver'
version '3.1.0'
version '4.1.0'
ext.isReleaseVersion = !version.endsWith('SNAPSHOT')

compileJava {
Expand Down Expand Up @@ -171,6 +171,11 @@ uploadArchives {
id 'steve-perkins-bc'
name 'Steve Perkins'
email 'steve.perkins@bettercloud.com'
},
developer {
id 'jarrodcodes'
name 'Jarrod Young'
email 'jarrodsy@gmail.com'
}
]}
}
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/com/bettercloud/vault/SslConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ public SslConfig build() throws VaultException {
} else {
this.verify = true;
}
if (this.verify == true && this.pemUTF8 == null && environmentLoader.loadVariable(VAULT_SSL_CERT) != null) {
if (this.verify && this.pemUTF8 == null && environmentLoader.loadVariable(VAULT_SSL_CERT) != null) {
final File pemFile = new File(environmentLoader.loadVariable(VAULT_SSL_CERT));
try (final InputStream input = new FileInputStream(pemFile)) {
this.pemUTF8 = inputStreamToUTF8(input);
Expand All @@ -477,7 +477,7 @@ public SslConfig build() throws VaultException {
* @throws VaultException
*/
private void buildSsl() throws VaultException {
if (verify == true) {
if (verify) {
if (keyStore != null || trustStore != null) {
this.sslContext = buildSslContextFromJks();
} else if (pemUTF8 != null || clientPemUTF8 != null || clientKeyPemUTF8 != null) {
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/bettercloud/vault/Vault.java
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ public Vault(final VaultConfig vaultConfig) {

/**
* Construct a Vault driver instance with the provided config settings, and use the provided global KV Engine version for all secrets.
*
* @param vaultConfig Configuration settings for Vault interaction (e.g. server address, token, etc)
* @param engineVersion Which version of the Key/Value Secret Engine to use globally (i.e. 1 or 2)
*/
public Vault(final VaultConfig vaultConfig, final Integer engineVersion) {
if (engineVersion < 1 || engineVersion > 2) {
Expand All @@ -99,6 +102,8 @@ public Vault(final VaultConfig vaultConfig, final Integer engineVersion) {
* If a secrets KV Engine version map is not supplied, use Vault APIs to determine the
* KV Engine version for each secret. This call requires admin rights.
* @param globalFallbackVersion The Integer version of the KV Engine to use as a global fallback.
*
* @throws VaultException If any error occurs
*/
public Vault(final VaultConfig vaultConfig, final Boolean useSecretsEnginePathMap, final Integer globalFallbackVersion)
throws VaultException {
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/com/bettercloud/vault/VaultConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,8 @@ public VaultConfig environmentLoader(final EnvironmentLoader environmentLoader)
* @param nameSpace The namespace to use globally in this VaultConfig instance.
* @return This object, with the namespace populated, ready for additional builder-pattern method calls or else
* finalization with the build() method
*
* @throws VaultException If any error occurs
*/
public VaultConfig nameSpace(final String nameSpace) throws VaultException {
if (nameSpace != null && !nameSpace.isEmpty()) {
Expand Down
52 changes: 44 additions & 8 deletions src/main/java/com/bettercloud/vault/api/Debug.java
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,8 @@ public Debug withNameSpace(final String nameSpace) {
* health check and provides a simple way to monitor the health of a Vault instance.</p>
*
* @return The response information returned from Vault
* @throws VaultException If any errors occurs with the REST request (e.g. non-200 status code, invalid JSON payload, etc), and the maximum number of retries is exceeded.
* @throws VaultException If any errors occurs with the REST request (e.g. non-200 status code, invalid JSON payload, etc),
* and the maximum number of retries is exceeded.
* @see <a href="https://www.vaultproject.io/docs/http/sys-health.html">https://www.vaultproject.io/docs/http/sys-health.html</a>
*
* <blockquote>
Expand All @@ -58,7 +59,21 @@ public Debug withNameSpace(final String nameSpace) {
* </blockquote>
*/
public HealthResponse health() throws VaultException {
return health(null, null, null, null);
return health(null, null, null, null, null,
null, null, null);
}

/**
* <p>A deprecated, overloaded version of {@link Debug#health()} that allows for passing one or more of the previous four optional parameters.</p>
* Please consider using the new constructor that adds support for perfStandbyOk, drSecondaryCode, etc/
*/
@Deprecated
public HealthResponse health(
final Boolean standbyOk,
final Integer activeCode,
final Integer standbyCode,
final Integer sealedCode) throws VaultException {
return health(standbyOk, activeCode, standbyCode, sealedCode, null, null, null, null);
}

/**
Expand All @@ -71,18 +86,26 @@ public HealthResponse health() throws VaultException {
* will need to check <code>HealthReponse.getRestResponse().getStatus()</code> to determine the result of
* the operation.</p>
*
* @param standbyOk (optional) Indicates that being a standby should still return the active status code instead of the standby code
* @param activeCode (optional) Indicates the status code that should be returned for an active node instead of the default of 200
* @param standbyCode (optional) Indicates the status code that should be returned for a standby node instead of the default of 429
* @param sealedCode (optional) Indicates the status code that should be returned for a sealed node instead of the default of 500
* @param standbyOk (optional) Indicates that being a standby should still return the active status code instead of the standby code
* @param activeCode (optional) Indicates the status code that should be returned for an active node instead of the default of 200
* @param standbyCode (optional) Indicates the status code that should be returned for a standby node instead of the default of 429
* @param sealedCode (optional) Indicates the status code that should be returned for a sealed node instead of the default of 500
* @param perfStandbyOk (optional) Specifies if being a performance standby should still return the active status code instead of the performance standby status code
* @param drSecondaryCode (optional) Indicates the status code that should be returned for a DR secondary node instead of the default of 472
* @param performanceStandbyCode (optional) Indicates the status code that should be returned for a performance standby node instead of the default of 473
* @param unInitCode (optional) Indicates the status code that should be returned for an uninitialized node instead of the default of 501
* @return The response information returned from Vault
* @throws VaultException If an error occurs or unexpected response received from Vault
*/
public HealthResponse health(
final Boolean standbyOk,
final Integer activeCode,
final Integer standbyCode,
final Integer sealedCode
final Integer sealedCode,
final Boolean perfStandbyOk,
final Integer drSecondaryCode,
final Integer performanceStandbyCode,
final Integer unInitCode
) throws VaultException {
final String path = "sys/health";
int retryCount = 0;
Expand All @@ -105,19 +128,32 @@ public HealthResponse health(
if (activeCode != null) rest.parameter("activecode", activeCode.toString());
if (standbyCode != null) rest.parameter("standbycode", standbyCode.toString());
if (sealedCode != null) rest.parameter("sealedcode", sealedCode.toString());
if (perfStandbyOk != null) rest.parameter("perfstandbyok", perfStandbyOk.toString());
if (drSecondaryCode != null) rest.parameter("drsecondarycode", drSecondaryCode.toString());
if (performanceStandbyCode != null)
rest.parameter("performancestandbycode", performanceStandbyCode.toString());
if (unInitCode != null) rest.parameter("uninitcode", unInitCode.toString());
// Execute request
final RestResponse restResponse = rest.get();

// Validate response
final Set<Integer> validCodes = new HashSet<>();//NOPMD
validCodes.add(200);
validCodes.add(429);
validCodes.add(472);
validCodes.add(473);
validCodes.add(500);
validCodes.add(501);
validCodes.add(503);
if (activeCode != null) validCodes.add(activeCode);
if (standbyCode != null) validCodes.add(standbyCode);
if (sealedCode != null) validCodes.add(sealedCode);
if (drSecondaryCode != null) validCodes.add(drSecondaryCode);
if (performanceStandbyCode != null) validCodes.add(performanceStandbyCode);
if (unInitCode != null) validCodes.add(unInitCode);
if (!validCodes.contains(restResponse.getStatus())) {
throw new VaultException("Vault responded with HTTP status code: " + restResponse.getStatus(), restResponse.getStatus());
throw new VaultException("Vault responded with HTTP status code: " + restResponse.getStatus(),
restResponse.getStatus());
}
return new HealthResponse(restResponse, retryCount);
} catch (RuntimeException | VaultException | RestException e) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/bettercloud/vault/api/Leases.java
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ public VaultResponse renew(final String leaseId, final long increment) throws Va
.readTimeoutSeconds(config.getReadTimeout())
.sslVerification(config.getSslConfig().isVerify())
.sslContext(config.getSslConfig().getSslContext())
.post();
.put();

// Validate response
if (restResponse.getStatus() != 200) {
Expand Down
1 change: 0 additions & 1 deletion src/main/java/com/bettercloud/vault/api/Logical.java
Original file line number Diff line number Diff line change
Expand Up @@ -649,7 +649,6 @@ private Integer engineVersionForSecretPath(final String secretPath) {
*
* @param path The Vault secret path to check (e.g. <code>secret/</code>).
* @return The response information received from Vault
* @throws VaultException If any error occurs, or unexpected response received from Vault
*/
public Integer getEngineVersionForSecretPath(final String path) {
return this.engineVersionForSecretPath(path);
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/com/bettercloud/vault/api/LogicalUtilities.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,8 @@ public static String adjustPathForList(final String path, final Logical.logicalO
*
* @param path The Vault path to check or mutate, based on the operation.
* @param operation The operation being performed, e.g. readV2 or writeV1.
*
* @return The modified path
*/
public static String adjustPathForDelete(final String path, final Logical.logicalOperations operation) {
final List<String> pathSegments = getPathSegments(path);
Expand All @@ -118,6 +120,8 @@ public static String adjustPathForDelete(final String path, final Logical.logica
* When deleting secret versions, you must inject the path segment "delete" right after the lowest-level path segment.
*
* @param path The Vault path to check or mutate, based on the operation.
*
* @return The modified path
*/
public static String adjustPathForVersionDelete(final String path) {
final List<String> pathSegments = getPathSegments(path);
Expand Down
1 change: 0 additions & 1 deletion src/main/java/com/bettercloud/vault/json/JsonObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public AuthResponse(final RestResponse restResponse, final int retries) {
for (final JsonValue authPolicy : authPoliciesJsonArray) {
authPolicies.add(authPolicy.asString());
}
} catch (ParseException e) {
} catch (ParseException ignored) {
}
}

Expand Down
26 changes: 24 additions & 2 deletions src/main/java/com/bettercloud/vault/response/HealthResponse.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.bettercloud.vault.VaultException;
import com.bettercloud.vault.json.Json;
import com.bettercloud.vault.json.JsonObject;
import com.bettercloud.vault.json.JsonValue;
import com.bettercloud.vault.rest.RestResponse;

import java.io.Serializable;
Expand All @@ -21,6 +22,9 @@ public class HealthResponse implements Serializable {
private Boolean sealed;
private Boolean standby;
private Long serverTimeUTC;
private Boolean performanceStandby;
private String replicationPerformanceMode;
private String replicationDrMode;

/**
* <p>Constructs a <code>HealthResponse</code> object from the data received in a health
Expand All @@ -34,7 +38,7 @@ public class HealthResponse implements Serializable {
* {@link com.bettercloud.vault.api.Debug#health(Boolean, Integer, Integer, Integer)}.</p>
*
* @param restResponse The raw HTTP response from Vault
* @param retries The number of retry attempts that occurred during the API call (can be zero)
* @param retries The number of retry attempts that occurred during the API call (can be zero)
* @throws VaultException If any error occurs or unexpected response is received from Vault
*/
public HealthResponse(final RestResponse restResponse, final int retries) throws VaultException {
Expand All @@ -59,7 +63,14 @@ public HealthResponse(final RestResponse restResponse, final int retries) throws
this.sealed = jsonObject.get("sealed") == null ? null : jsonObject.get("sealed").asBoolean();
this.standby = jsonObject.get("standby") == null ? null : jsonObject.get("standby").asBoolean();
this.serverTimeUTC = jsonObject.get("server_time_utc") == null ? null : jsonObject.get("server_time_utc").asLong();
} catch(final Exception e) {
this.performanceStandby = jsonObject.get("performance_standby") == null ? null :
jsonObject.get("performance_standby").asBoolean();
this.replicationPerformanceMode = jsonObject.get("replication_performance_mode") == null ? null :
jsonObject.get("replication_performance_mode").asString();
this.replicationDrMode = jsonObject.get("replication_dr_mode") == null ? null :
jsonObject.get("replication_dr_mode").asString();

} catch (final Exception e) {
throw new VaultException("Unable to parse JSON payload: " + e, restResponse.getStatus());
}
}
Expand Down Expand Up @@ -93,4 +104,15 @@ public Long getServerTimeUTC() {
return serverTimeUTC;
}

public Boolean getPerformanceStandby() {
return performanceStandby;
}

public String getReplicationPerformanceMode() {
return replicationPerformanceMode;
}

public String getReplicationDrMode() {
return replicationDrMode;
}
}
Loading