All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.bettercloud.vault.VaultConfig Maven / Gradle / Ivy

There is a newer version: 5.1.0
Show newest version
package com.bettercloud.vault;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.file.Files;
import java.nio.file.Paths;

/**
 * 

A container for the configuration settings needed to initialize a Vault driver instance.

* *

There are two ways to create and setup a VaultConfig instance. The full-featured approach * uses a builder pattern, calling setter methods for each value and then terminating with a call to build():

* *
*
{@code
 * final VaultConfig config = new VaultConfig()
 *                              .address("http://127.0.0.1:8200")
 *                              .token("eace6676-4d78-c687-4e54-03cad00e3abf")
 *                              .sslVerify(true)
 *                              .timeout(30)
 *                              .build();
 * }
*
* *

If the only values that you need to set are address and token, then as a * shortcut there is also a constructor method taking those two values:

* *
*
{@code
 * final VaultConfig config = new VaultConfig("http://127.0.0.1:8200", "eace6676-4d78-c687-4e54-03cad00e3abf");
 * }
*
* *

Note that when using the shorthand convenience constructor, you should NOT set additional properties on the * same instance afterward.

*/ public class VaultConfig { /** *

The code used to load environment variables is encapsulated within an inner class, * so that a mock version of that environment loader can be used by unit tests.

*/ static class EnvironmentLoader { public String loadVariable(final String name) { String value = null; if ("VAULT_TOKEN".equals(name)) { // Special handling for the VAULT_TOKEN variable, since it can be read from the filesystem if it's not // found in the environment if (System.getenv("VAULT_TOKEN") != null) { // Found it in the environment value = System.getenv(name); } else { // Not in the environment, looking for a ".vault-token" file in the executing user's home directory instead try { final byte[] bytes = Files.readAllBytes(Paths.get(System.getProperty("user.home")).resolve(".vault-token")); value = new String(bytes, "UTF-8").trim(); } catch (IOException e) { // No-op... there simple isn't a token value available } } } else { // Normal handling for all other variables. We just check the environment. value = System.getenv(name); } return value; } } private EnvironmentLoader environmentLoader; private String address; private String token; private String sslPemUTF8; private Boolean sslVerify; private Integer openTimeout; private Integer readTimeout; private int maxRetries; private int retryIntervalMilliseconds; /** *

Default constructor. Should be used in conjunction with the builder pattern, calling additional * property setter methods and ultimately finishing with a call to build().

* *

Note that when using this builder pattern approach, you must either set address * and token explicitly, or else have them available as runtime environment variables.

*/ public VaultConfig() { } /** *

A convenience constructor, for quickly creating a VaultConfig instance with its * address and token fields populated.

* *

Although address and token are the only two properties explicitly passed, the * constructor will still look to the runtime environment variables to populate any other fields when values * are present.

* *

When using this approach to creating a VaultConfig instance, you should NOT make additional * setter method calls after construction. If you need other properties set explicitly, then use the builder * pattern approach.

* * @param address The URL of the target Vault server * @param token The access token to enable Vault access * @throws VaultException If any error occurs while loading and parsing config values */ public VaultConfig(final String address, final String token) throws VaultException { this(address, token, new EnvironmentLoader()); } /** *

A convenience constructor, for quickly creating a VaultConfig instance with its * address field populated.

* *

While the other convenience constructor requires root token parameter, this constructor version does not. * So it IS possible to construct a VaultConfig object with no root token present. However, such * an object will be of no use with most actual Vault API calls. This constructor is therefore meant to be used * when you plan to programmatically retrieve a token (e.g. from the "userpass" backend) and populate it prior * to making other API calls.

* *

When using this approach to creating a VaultConfig instance, you should NOT make additional * setter method calls after construction... other than the token scenario described immediately above. If you * need any other properties set explicitly, then use the builder pattern approach.

* * @param address The URL of the target Vault server * @throws VaultException If any error occurs while loading and parsing config values */ public VaultConfig(final String address) throws VaultException { this(address, new EnvironmentLoader()); } /** * An overloaded version of the normal convenience constructor, used by unit tests to inject a mock environment * variable loader and validate that loading logic. * * @param address The URL of the target Vault server * @param token The access token to enable Vault access * @param environmentLoader A (mock) environment loader implementation * @throws VaultException If any error occurs while loading and parsing config values */ protected VaultConfig(final String address, final String token, final EnvironmentLoader environmentLoader) throws VaultException { this.address = address; this.token = token; this.environmentLoader = environmentLoader; build(); } /** * An overloaded version of the normal convenience constructor, used by unit tests to inject a mock environment * variable loader and validate that loading logic. * * @param address The URL of the target Vault server * @param environmentLoader A (mock) environment loader implementation * @throws VaultException If any error occurs while loading and parsing config values */ protected VaultConfig(final String address, final EnvironmentLoader environmentLoader) throws VaultException { this.address = address; this.environmentLoader = environmentLoader; build(); } /** *

The code used to load environment variables is encapsulated within an inner class, so that a mock version of * that environment loader can be used by unit tests.

* *

This method is used by unit tests, to inject a mock environment variable when constructing a * VaultConfig instance using the builder pattern approach rather than the convenience constructor. * There really shouldn't ever be a need to call this method outside of a unit test context (hence the * protected access level).

* * @param environmentLoader An environment variable loader implementation (presumably a mock) * @return This object, with environmentLoader populated, ready for additional builder-pattern method calls or else finalization with the {@link this#build()} method */ VaultConfig environmentLoader(final EnvironmentLoader environmentLoader) { this.environmentLoader = environmentLoader; return this; } /** *

Sets the address (URL) of the Vault server instance to which API calls should be sent. * E.g. http://127.0.0.1:8200.

* *

If no address is explicitly set, either by this method in a builder pattern approach or else by one of the * convenience constructors, then VaultConfig will look to the VAULT_ADDR environment * variable.

* *

address is required for the Vault driver to function. If you do not supply it explicitly AND no * environment variable value is found, then initialization of the VaultConfig object will fail.

* * @param address The Vault server base URL * @return This object, with address populated, ready for additional builder-pattern method calls or else finalization with the build() method */ public VaultConfig address(final String address) { this.address = address; return this; } /** *

Sets the token used to access Vault.

* *

If no token is explicitly set, either by this method in a builder pattern approach or else by one of the * convenience constructors, then VaultConfig will look to the VAULT_TOKEN environment * variable.

* *

There are some cases where you might want to instantiate a VaultConfig object without a token * (e.g. you plan to retrieve a token programmatically, with a call to the "userpass" auth backend, and populate * it prior to making any other API calls). In such use cases, you can still use either the builder pattern * approach or the single-argument convenience constructor.

* * @param token The token to use for accessing Vault * @return This object, with token populated, ready for additional builder-pattern method calls or else finalization with the build() method */ public VaultConfig token(final String token) { this.token = token; return this; } /** *

An X.509 certificate, to use when communicating with Vault over HTTPS. This method accepts a string * containing the certificate data. This string should meet the following requirements:

* *
    *
  • Contain an unencrypted X.509 certificate, in PEM format.
  • *
  • Use UTF-8 encoding.
  • *
  • * Contain a line-break between the certificate header (e.g. "-----BEGIN CERTIFICATE-----") and the * rest of the certificate content. It doesn't matter whether or not there are additional line * breaks within the certificate content, or whether there is a line break before the certificate * footer (e.g. "-----END CERTIFICATE-----"). But the Java standard library will fail to properly * process the certificate without a break following the header * (see http://www.doublecloud.org/2014/03/reading-x-509-certificate-in-java-how-to-handle-format-issue/). *
  • *
* *

If no certificate data is provided, either by this method or sslPemFile() * or sslPemResource(), then VaultConfig will look to the * VAULT_SSL_CERT environment variable.

* * @param sslPemUTF8 An X.509 certificate, in unencrypted PEM format with UTF-8 encoding. * @return This object, with sslPemUTF8 populated, ready for additional builder-pattern method calls or else finalization with the build() method */ public VaultConfig sslPemUTF8(final String sslPemUTF8) { this.sslPemUTF8 = sslPemUTF8; return this; } /** *

An X.509 certificate, to use when communicating with Vault over HTTPS. This method accepts the path of * a file containing the certificate data. This file's contents should meet the following requirements:

* *
    *
  • Contain an unencrypted X.509 certificate, in PEM format.
  • *
  • Use UTF-8 encoding.
  • *
  • * Contain a line-break between the certificate header (e.g. "-----BEGIN CERTIFICATE-----") and the * rest of the certificate content. It doesn't matter whether or not there are additional line * breaks within the certificate content, or whether there is a line break before the certificate * footer (e.g. "-----END CERTIFICATE-----"). But the Java standard library will fail to properly * process the certificate without a break following the header * (see http://www.doublecloud.org/2014/03/reading-x-509-certificate-in-java-how-to-handle-format-issue/). *
  • *
* *

If no certificate data is provided, either by this method or sslPemResource() * or sslPemUTF8(), then VaultConfig will look to the * VAULT_SSL_CERT environment variable.

* * @param sslPemFile The path of a file containing an X.509 certificate, in unencrypted PEM format with UTF-8 encoding. * @return This object, with sslPemFile populated, ready for additional builder-pattern method calls or else finalization with the build() method * @throws VaultException If any error occurs while loading and parsing the PEM file */ public VaultConfig sslPemFile(final File sslPemFile) throws VaultException { try (final InputStream input = new FileInputStream(sslPemFile)){ this.sslPemUTF8 = inputStreamToUTF8(input); } catch (IOException e) { throw new VaultException(e); } return this; } /** *

An X.509 certificate, to use when communicating with Vault over HTTPS. This method accepts the path of * a classpath resource containing the certificate data (e.g. you've bundled the cert into your library or * application's JAR/WAR/EAR file). This resource's contents should meet the following requirements:

* *
    *
  • Contain an unencrypted X.509 certificate, in PEM format.
  • *
  • Use UTF-8 encoding.
  • *
  • * Contain a line-break between the certificate header (e.g. "-----BEGIN CERTIFICATE-----") and the * rest of the certificate content. It doesn't matter whether or not there are additional line * breaks within the certificate content, or whether there is a line break before the certificate * footer (e.g. "-----END CERTIFICATE-----"). But the Java standard library will fail to properly * process the certificate without a break following the header * (see http://www.doublecloud.org/2014/03/reading-x-509-certificate-in-java-how-to-handle-format-issue/). *
  • *
* *

If no certificate data is provided, either by this method or sslPemFile() * or sslPemUTF8(), then VaultConfig will look to the * VAULT_SSL_CERT environment variable.

* * @param classpathResource The path of a classpath resource containing an X.509 certificate, in unencrypted PEM format with UTF-8 encoding. * @return This object, with sslPemResource populated, ready for additional builder-pattern method calls or else finalization with the build() method * @throws VaultException If any error occurs while loading and parsing the PEM file */ public VaultConfig sslPemResource(final String classpathResource) throws VaultException { try (final InputStream input = this.getClass().getResourceAsStream(classpathResource)){ this.sslPemUTF8 = inputStreamToUTF8(input); } catch (IOException e) { throw new VaultException(e); } return this; } /** *

Whether or not HTTPS connections to the Vault server should verify that a valid SSL certificate is being * used. Unless this is set to false, the default behavior is to always verify SSL certificates.

* *

SSL CERTIFICATE VERIFICATION SHOULD NOT BE DISABLED IN PRODUCTION! This feature is made available to * facilitate development or testing environments, where you might be using a self-signed cert that will not * pass verification. However, even if you are using a self-signed cert on your Vault server, you can still leave * SSL verification enabled and have your application supply the cert using sslPemFile(), * sslPemResource(), or sslPemUTF8().

* *

If no sslVerify is explicitly set, either by this method in a builder pattern approach or else by one of the * convenience constructors, then VaultConfig will look to the VAULT_SSL_VERIFY * environment variable.

* * @param sslVerify Whether or not to verify the SSL certificate used by Vault with HTTPS connections. Default is true. * @return This object, with sslVerify populated, ready for additional builder-pattern method calls or else finalization with the build() method */ public VaultConfig sslVerify(final Boolean sslVerify) { this.sslVerify = sslVerify; return this; } /** *

The number of seconds to wait before giving up on establishing an HTTP(S) connection to the Vault server.

* *

If no openTimeout is explicitly set, either by this method in a builder pattern approach or else by one of * the convenience constructors, then VaultConfig will look to the VAULT_OPEN_TIMEOUT * environment variable.

* * @param openTimeout Number of seconds to wait for an HTTP(S) connection to successfully establish * @return This object, with openTimeout populated, ready for additional builder-pattern method calls or else finalization with the build() method */ public VaultConfig openTimeout(final Integer openTimeout) { this.openTimeout = openTimeout; return this; } /** *

After an HTTP(S) connection has already been established, this is the number of seconds to wait for all * data to finish downloading.

* *

If no readTimeout is explicitly set, either by this method in a builder pattern approach or else by one of * the convenience constructors, then VaultConfig will look to the VAULT_READ_TIMEOUT * environment variable.

* * @param readTimeout Number of seconds to wait for all data to be retrieved from an established HTTP(S) connection * @return This object, with readTimeout populated, ready for additional builder-pattern method calls or else finalization with the build() method */ public VaultConfig readTimeout(final Integer readTimeout) { this.readTimeout = readTimeout; return this; } /** *

Sets the maximum number of times that an API operation will retry upon failure.

* *

This method is not meant to be called from application-level code outside of this package (hence * the protected access level. It is meant to be invoked via Vault.withRetries() * in a builder pattern DSL-style.

* * @param maxRetries The number of times that API operations will be retried when a failure occurs. */ protected void setMaxRetries(final int maxRetries) { this.maxRetries = maxRetries; } /** *

Sets the period of time (in milliseconds) that the driver will wait in between retry attempts for a * failing API operation.

* *

This method is not meant to be called from application-level code outside of this package (hence * the protected access level. It is meant to be invoked via Vault.withRetries() * in a builder pattern DSL-style.

* * @param retryIntervalMilliseconds The number of milliseconds that the driver will wait in between retries. */ protected void setRetryIntervalMilliseconds(final int retryIntervalMilliseconds) { this.retryIntervalMilliseconds = retryIntervalMilliseconds; } /** *

This is the terminating method in the builder pattern. The method that validates all of the fields that * has been set already, uses environment variables when available to populate any unset fields, and returns * a VaultConfig object that is ready for use.

* * @return This object, with all available config options parsed and loaded * @throws VaultException If the address field was left unset, and there is no VAULT_ADDR environment variable value with which to populate it. */ public VaultConfig build() throws VaultException { if (this.environmentLoader == null) { this.environmentLoader = new EnvironmentLoader(); } if (this.address == null) { final String addressFromEnv = environmentLoader.loadVariable("VAULT_ADDR"); if (addressFromEnv != null) { this.address = addressFromEnv; } else { throw new VaultException("No address is set"); } } if (this.token == null && environmentLoader.loadVariable("VAULT_TOKEN") != null) { this.token = environmentLoader.loadVariable("VAULT_TOKEN"); } if (this.sslPemUTF8 == 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.sslPemUTF8 = inputStreamToUTF8(input); } catch (IOException e) { throw new VaultException(e); } } if (this.sslVerify == null && environmentLoader.loadVariable("VAULT_SSL_VERIFY") != null) { this.sslVerify = Boolean.valueOf(environmentLoader.loadVariable("VAULT_SSL_VERIFY")); } if (this.openTimeout == null && environmentLoader.loadVariable("VAULT_OPEN_TIMEOUT") != null) { try { this.openTimeout = Integer.valueOf(environmentLoader.loadVariable("VAULT_OPEN_TIMEOUT")); } catch (NumberFormatException e) { System.err.printf("The \"VAULT_OPEN_TIMEOUT\" environment variable contains value \"%s\", which cannot be parsed as an integer timeout period.%n", environmentLoader.loadVariable("VAULT_OPEN_TIMEOUT")); } } if (this.readTimeout == null && environmentLoader.loadVariable("VAULT_READ_TIMEOUT") != null) { try { this.readTimeout = Integer.valueOf(environmentLoader.loadVariable("VAULT_READ_TIMEOUT")); } catch (NumberFormatException e) { System.err.printf("The \"VAULT_READ_TIMEOUT\" environment variable contains value \"%s\", which cannot be parsed as an integer timeout period.%n", environmentLoader.loadVariable("VAULT_READ_TIMEOUT")); } } return this; } public String getAddress() { return address; } public String getToken() { return token; } public String getSslPemUTF8() { return sslPemUTF8; } public Boolean isSslVerify() { return sslVerify; } public Integer getOpenTimeout() { return openTimeout; } public Integer getReadTimeout() { return readTimeout; } public int getMaxRetries() { return maxRetries; } public int getRetryIntervalMilliseconds() { return retryIntervalMilliseconds; } private String inputStreamToUTF8(final InputStream input) throws IOException { final BufferedReader in = new BufferedReader(new InputStreamReader(input, "UTF-8")); final StringBuilder utf8 = new StringBuilder(""); String str; while ((str = in.readLine()) != null) { // String concatenation is less efficient, but for some reason the line-breaks (which are necessary // for Java to correctly parse SSL certs) are stripped off when using a StringBuilder. utf8.append(str).append(System.lineSeparator()); } in.close(); return utf8.toString(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy