NIFI-10071: Adding support for HashiCorp Vault K/V version 2 Secrets Engine (#6087)

This commit is contained in:
Joe Gresock 2022-06-01 15:09:01 -04:00 committed by GitHub
parent 0d45fef702
commit 48de70a568
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 155 additions and 55 deletions

View File

@ -46,39 +46,39 @@ public interface HashiCorpVaultCommunicationService {
byte[] decrypt(String transitPath, String cipherText); byte[] decrypt(String transitPath, String cipherText);
/** /**
* Writes a single secret value using Vault's unversioned Key/Value Secrets Engine. * Writes a single secret value using Vault's Key/Value Secrets Engine.
* *
* @see <a href="https://www.vaultproject.io/api-docs/secret/kv/kv-v1">https://www.vaultproject.io/api-docs/secret/kv/kv-v1</a> * @see <a href="https://www.vaultproject.io/api-docs/secret/kv">https://www.vaultproject.io/api-docs/secret/kv</a>
* @param keyValuePath The Vault path to use for the configured Key/Value v1 Secrets Engine * @param keyValuePath The Vault path to use for the configured Key/Value Secrets Engine
* @param secretKey The secret key * @param secretKey The secret key
* @param value The secret value * @param value The secret value
*/ */
void writeKeyValueSecret(String keyValuePath, String secretKey, String value); void writeKeyValueSecret(String keyValuePath, String secretKey, String value);
/** /**
* Reads a single secret value from Vault's unversioned Key/Value Secrets Engine. * Reads a single secret value from Vault's Key/Value Secrets Engine.
* *
* @see <a href="https://www.vaultproject.io/api-docs/secret/kv/kv-v1">https://www.vaultproject.io/api-docs/secret/kv/kv-v1</a> * @see <a href="https://www.vaultproject.io/api-docs/secret/kv">https://www.vaultproject.io/api-docs/secret/kv</a>
* @param keyValuePath The Vault path to use for the configured Key/Value v1 Secrets Engine * @param keyValuePath The Vault path to use for the configured Key/Value Secrets Engine
* @param secretKey The secret key * @param secretKey The secret key
* @return The secret value, or empty if not found * @return The secret value, or empty if not found
*/ */
Optional<String> readKeyValueSecret(String keyValuePath, String secretKey); Optional<String> readKeyValueSecret(String keyValuePath, String secretKey);
/** /**
* Writes a secret with multiple key/value pairs using Vault's unversioned Key/Value Secrets Engine. * Writes a secret with multiple key/value pairs using Vault's Key/Value Secrets Engine.
* *
* @see <a href="https://www.vaultproject.io/api-docs/secret/kv/kv-v1">https://www.vaultproject.io/api-docs/secret/kv/kv-v1</a> * @see <a href="https://www.vaultproject.io/api-docs/secret/kv">https://www.vaultproject.io/api-docs/secret/kv</a>
* @param keyValuePath The Vault path to use for the configured Key/Value v1 Secrets Engine * @param keyValuePath The Vault path to use for the configured Key/Value Secrets Engine
* @param keyValues A map from key to value for keys/values that should be stored in the secret * @param keyValues A map from key to value for keys/values that should be stored in the secret
*/ */
void writeKeyValueSecretMap(String keyValuePath, String secretKey, Map<String, String> keyValues); void writeKeyValueSecretMap(String keyValuePath, String secretKey, Map<String, String> keyValues);
/** /**
* Reads a secret with multiple key/value pairs from Vault's unversioned Key/Value Secrets Engine. * Reads a secret with multiple key/value pairs from Vault's Key/Value Secrets Engine.
* *
* @see <a href="https://www.vaultproject.io/api-docs/secret/kv/kv-v1">https://www.vaultproject.io/api-docs/secret/kv/kv-v1</a> * @see <a href="https://www.vaultproject.io/api-docs/secret/kv">https://www.vaultproject.io/api-docs/secret/kv</a>
* @param keyValuePath The Vault path to use for the configured Key/Value v1 Secrets Engine * @param keyValuePath The Vault path to use for the configured Key/Value Secrets Engine
* @param secretKey The secret key * @param secretKey The secret key
* @return A map from key to value from the secret key/values, or an empty map if not found * @return A map from key to value from the secret key/values, or an empty map if not found
*/ */

View File

@ -25,6 +25,7 @@ import org.springframework.core.env.PropertySource;
import org.springframework.vault.authentication.SimpleSessionManager; import org.springframework.vault.authentication.SimpleSessionManager;
import org.springframework.vault.client.ClientHttpRequestFactoryFactory; import org.springframework.vault.client.ClientHttpRequestFactoryFactory;
import org.springframework.vault.core.VaultKeyValueOperations; import org.springframework.vault.core.VaultKeyValueOperations;
import org.springframework.vault.core.VaultKeyValueOperationsSupport.KeyValueBackend;
import org.springframework.vault.core.VaultTemplate; import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultTransitOperations; import org.springframework.vault.core.VaultTransitOperations;
import org.springframework.vault.support.Ciphertext; import org.springframework.vault.support.Ciphertext;
@ -37,8 +38,6 @@ import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import static org.springframework.vault.core.VaultKeyValueOperationsSupport.KeyValueBackend.KV_1;
/** /**
* Implements the VaultCommunicationService using Spring Vault * Implements the VaultCommunicationService using Spring Vault
*/ */
@ -46,6 +45,7 @@ public class StandardHashiCorpVaultCommunicationService implements HashiCorpVaul
private final VaultTemplate vaultTemplate; private final VaultTemplate vaultTemplate;
private final VaultTransitOperations transitOperations; private final VaultTransitOperations transitOperations;
private final Map<String, VaultKeyValueOperations> keyValueOperationsMap; private final Map<String, VaultKeyValueOperations> keyValueOperationsMap;
private final KeyValueBackend keyValueBackend;
/** /**
* Creates a VaultCommunicationService that uses Spring Vault. * Creates a VaultCommunicationService that uses Spring Vault.
@ -60,6 +60,7 @@ public class StandardHashiCorpVaultCommunicationService implements HashiCorpVaul
new SimpleSessionManager(vaultConfiguration.clientAuthentication())); new SimpleSessionManager(vaultConfiguration.clientAuthentication()));
transitOperations = vaultTemplate.opsForTransit(); transitOperations = vaultTemplate.opsForTransit();
keyValueBackend = vaultConfiguration.getKeyValueBackend();
keyValueOperationsMap = new HashMap<>(); keyValueOperationsMap = new HashMap<>();
} }
@ -94,7 +95,7 @@ public class StandardHashiCorpVaultCommunicationService implements HashiCorpVaul
Objects.requireNonNull(secretKey, "Secret secretKey must be specified"); Objects.requireNonNull(secretKey, "Secret secretKey must be specified");
Objects.requireNonNull(value, "Secret value must be specified"); Objects.requireNonNull(value, "Secret value must be specified");
final VaultKeyValueOperations keyValueOperations = keyValueOperationsMap final VaultKeyValueOperations keyValueOperations = keyValueOperationsMap
.computeIfAbsent(keyValuePath, path -> vaultTemplate.opsForKeyValue(path, KV_1)); .computeIfAbsent(keyValuePath, path -> vaultTemplate.opsForKeyValue(path, keyValueBackend));
keyValueOperations.put(secretKey, new SecretData(value)); keyValueOperations.put(secretKey, new SecretData(value));
} }
@ -109,7 +110,7 @@ public class StandardHashiCorpVaultCommunicationService implements HashiCorpVaul
Objects.requireNonNull(keyValuePath, "Vault K/V path must be specified"); Objects.requireNonNull(keyValuePath, "Vault K/V path must be specified");
Objects.requireNonNull(secretKey, "Secret secretKey must be specified"); Objects.requireNonNull(secretKey, "Secret secretKey must be specified");
final VaultKeyValueOperations keyValueOperations = keyValueOperationsMap final VaultKeyValueOperations keyValueOperations = keyValueOperationsMap
.computeIfAbsent(keyValuePath, path -> vaultTemplate.opsForKeyValue(path, KV_1)); .computeIfAbsent(keyValuePath, path -> vaultTemplate.opsForKeyValue(path, keyValueBackend));
final VaultResponseSupport<SecretData> response = keyValueOperations.get(secretKey, SecretData.class); final VaultResponseSupport<SecretData> response = keyValueOperations.get(secretKey, SecretData.class);
return response == null ? Optional.empty() : Optional.ofNullable(response.getRequiredData().getValue()); return response == null ? Optional.empty() : Optional.ofNullable(response.getRequiredData().getValue());
} }
@ -123,14 +124,14 @@ public class StandardHashiCorpVaultCommunicationService implements HashiCorpVaul
return; return;
} }
final VaultKeyValueOperations keyValueOperations = keyValueOperationsMap final VaultKeyValueOperations keyValueOperations = keyValueOperationsMap
.computeIfAbsent(keyValuePath, path -> vaultTemplate.opsForKeyValue(path, KV_1)); .computeIfAbsent(keyValuePath, path -> vaultTemplate.opsForKeyValue(path, keyValueBackend));
keyValueOperations.put(secretKey, keyValues); keyValueOperations.put(secretKey, keyValues);
} }
@Override @Override
public Map<String, String> readKeyValueSecretMap(final String keyValuePath, final String key) { public Map<String, String> readKeyValueSecretMap(final String keyValuePath, final String key) {
final VaultKeyValueOperations keyValueOperations = keyValueOperationsMap final VaultKeyValueOperations keyValueOperations = keyValueOperationsMap
.computeIfAbsent(keyValuePath, path -> vaultTemplate.opsForKeyValue(path, KV_1)); .computeIfAbsent(keyValuePath, path -> vaultTemplate.opsForKeyValue(path, keyValueBackend));
final VaultResponseSupport<Map> response = keyValueOperations.get(key, Map.class); final VaultResponseSupport<Map> response = keyValueOperations.get(key, Map.class);
return response == null ? Collections.emptyMap() : (Map<String, String>) response.getRequiredData(); return response == null ? Collections.emptyMap() : (Map<String, String>) response.getRequiredData();
} }

View File

@ -25,6 +25,7 @@ import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.support.ResourcePropertySource; import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.vault.client.RestTemplateFactory; import org.springframework.vault.client.RestTemplateFactory;
import org.springframework.vault.config.EnvironmentVaultConfiguration; import org.springframework.vault.config.EnvironmentVaultConfiguration;
import org.springframework.vault.core.VaultKeyValueOperationsSupport.KeyValueBackend;
import org.springframework.vault.support.ClientOptions; import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration; import org.springframework.vault.support.SslConfiguration;
@ -37,10 +38,14 @@ import java.util.concurrent.TimeUnit;
* A Vault configuration that uses the NiFiVaultEnvironment. * A Vault configuration that uses the NiFiVaultEnvironment.
*/ */
public class HashiCorpVaultConfiguration extends EnvironmentVaultConfiguration { public class HashiCorpVaultConfiguration extends EnvironmentVaultConfiguration {
private static final int KV_V1 = 1;
private static final int KV_V2 = 2;
public enum VaultConfigurationKey { public enum VaultConfigurationKey {
AUTHENTICATION_PROPERTIES_FILE("vault.authentication.properties.file"), AUTHENTICATION_PROPERTIES_FILE("vault.authentication.properties.file"),
READ_TIMEOUT("vault.read.timeout"), READ_TIMEOUT("vault.read.timeout"),
CONNECTION_TIMEOUT("vault.connection.timeout"), CONNECTION_TIMEOUT("vault.connection.timeout"),
KV_VERSION("vault.kv.version"),
URI("vault.uri"); URI("vault.uri");
private final String key; private final String key;
@ -62,6 +67,7 @@ public class HashiCorpVaultConfiguration extends EnvironmentVaultConfiguration {
private final SslConfiguration sslConfiguration; private final SslConfiguration sslConfiguration;
private final ClientOptions clientOptions; private final ClientOptions clientOptions;
private final KeyValueBackend keyValueBackend;
/** /**
* Creates a HashiCorpVaultConfiguration from property sources * Creates a HashiCorpVaultConfiguration from property sources
@ -84,6 +90,22 @@ public class HashiCorpVaultConfiguration extends EnvironmentVaultConfiguration {
} }
} }
KeyValueBackend keyValueBackend = KeyValueBackend.KV_1;
if (env.containsProperty(VaultConfigurationKey.KV_VERSION.key)) {
final String kvVersion = env.getProperty(VaultConfigurationKey.KV_VERSION.key);
try {
int kvVersionNumber = Integer.parseInt(kvVersion);
if (kvVersionNumber == KV_V2) {
keyValueBackend = KeyValueBackend.KV_2;
} else if (kvVersionNumber != KV_V1) {
throw new IllegalArgumentException("K/V v" + kvVersion + " is not recognized");
}
} catch (final IllegalArgumentException e) {
throw new HashiCorpVaultConfigurationException("Unrecognized " + VaultConfigurationKey.KV_VERSION.key + ": " + kvVersion, e);
}
}
this.keyValueBackend = keyValueBackend;
this.setApplicationContext(new HashiCorpVaultApplicationContext(env)); this.setApplicationContext(new HashiCorpVaultApplicationContext(env));
sslConfiguration = env.getProperty(VaultConfigurationKey.URI.key).contains(HTTPS) sslConfiguration = env.getProperty(VaultConfigurationKey.URI.key).contains(HTTPS)
@ -92,6 +114,10 @@ public class HashiCorpVaultConfiguration extends EnvironmentVaultConfiguration {
clientOptions = getClientOptions(); clientOptions = getClientOptions();
} }
public KeyValueBackend getKeyValueBackend() {
return keyValueBackend;
}
/** /**
* A convenience method to create a PropertySource from a file on disk. * A convenience method to create a PropertySource from a file on disk.
* @param filename The properties filename. * @param filename The properties filename.

View File

@ -38,26 +38,27 @@ public class HashiCorpVaultProperties {
private final HashiCorpVaultSslProperties ssl; private final HashiCorpVaultSslProperties ssl;
private final Optional<String> connectionTimeout; private final Optional<String> connectionTimeout;
private final Optional<String> readTimeout; private final Optional<String> readTimeout;
private final int kvVersion;
private HashiCorpVaultProperties(final String uri, String keyStore, final String keyStoreType, final String keyStorePassword, final String trustStore, private HashiCorpVaultProperties(final HashiCorpVaultPropertiesBuilder builder) {
final String trustStoreType, final String trustStorePassword, final String authPropertiesFilename, this.uri = Objects.requireNonNull(builder.uri, "Vault URI is required");;
final String enabledTlsCipherSuites, final String enabledTlsProtocols, final String connectionTimeout, final String readTimeout) { this.authPropertiesFilename = Objects.requireNonNull(builder.authPropertiesFilename, "Vault auth properties filename is required");
Objects.requireNonNull(uri, "Vault URI is required"); this.ssl = new HashiCorpVaultSslProperties(builder.keyStore, builder.keyStoreType, builder.keyStorePassword,
Objects.requireNonNull(authPropertiesFilename, "Vault auth properties filename is required"); builder.trustStore, builder.trustStoreType, builder.trustStorePassword,builder.enabledTlsCipherSuites, builder.enabledTlsProtocols);
this.uri = uri; this.connectionTimeout = builder.connectionTimeout == null ? Optional.empty() : Optional.of(builder.connectionTimeout);
this.authPropertiesFilename = authPropertiesFilename; this.readTimeout = builder.readTimeout == null ? Optional.empty() : Optional.of(builder.readTimeout);
this.ssl = new HashiCorpVaultSslProperties(keyStore, keyStoreType, keyStorePassword, trustStore, trustStoreType, trustStorePassword, this.kvVersion = builder.kvVersion;
enabledTlsCipherSuites, enabledTlsProtocols); if (kvVersion != 1 && kvVersion != 2) {
this.connectionTimeout = connectionTimeout == null ? Optional.empty() : Optional.of(connectionTimeout); throw new HashiCorpVaultConfigurationException("Key/Value version " + kvVersion + " is not supported");
this.readTimeout = readTimeout == null ? Optional.empty() : Optional.of(readTimeout); }
if (uri.startsWith(HTTPS)) { if (uri.startsWith(HTTPS)) {
Objects.requireNonNull(keyStore, "KeyStore is required with an https URI"); Objects.requireNonNull(builder.keyStore, "KeyStore is required with an https URI");
Objects.requireNonNull(keyStorePassword, "KeyStore password is required with an https URI"); Objects.requireNonNull(builder.keyStorePassword, "KeyStore password is required with an https URI");
Objects.requireNonNull(keyStoreType, "KeyStore type is required with an https URI"); Objects.requireNonNull(builder.keyStoreType, "KeyStore type is required with an https URI");
Objects.requireNonNull(trustStore, "TrustStore is required with an https URI"); Objects.requireNonNull(builder.trustStore, "TrustStore is required with an https URI");
Objects.requireNonNull(trustStorePassword, "TrustStore password is required with an https URI"); Objects.requireNonNull(builder.trustStorePassword, "TrustStore password is required with an https URI");
Objects.requireNonNull(trustStoreType, "TrustStore type is required with an https URI"); Objects.requireNonNull(builder.trustStoreType, "TrustStore type is required with an https URI");
} }
validateAuthProperties(); validateAuthProperties();
} }
@ -79,6 +80,11 @@ public class HashiCorpVaultProperties {
return ssl; return ssl;
} }
@HashiCorpVaultProperty(key = "kv.version")
public int getKvVersion() {
return kvVersion;
}
@HashiCorpVaultProperty(key = "authentication.properties.file") @HashiCorpVaultProperty(key = "authentication.properties.file")
public String getAuthPropertiesFilename() { public String getAuthPropertiesFilename() {
return authPropertiesFilename; return authPropertiesFilename;
@ -108,6 +114,7 @@ public class HashiCorpVaultProperties {
private String enabledTlsProtocols; private String enabledTlsProtocols;
private String connectionTimeout; private String connectionTimeout;
private String readTimeout; private String readTimeout;
private int kvVersion = 1;
/** /**
* Set the Vault URI (e.g., http://localhost:8200). If using https protocol, the KeyStore and TrustStore * Set the Vault URI (e.g., http://localhost:8200). If using https protocol, the KeyStore and TrustStore
@ -120,6 +127,16 @@ public class HashiCorpVaultProperties {
return this; return this;
} }
/**
* Sets the Key/Value secrets engine version (1 or 2).
* @param kvVersion The Key/Value engine version
* @return Builder
*/
public HashiCorpVaultPropertiesBuilder setKvVersion(int kvVersion) {
this.kvVersion = kvVersion;
return this;
}
/** /**
* Sets the path to the keyStore. * Sets the path to the keyStore.
* @param keyStore Path to the keyStore * @param keyStore Path to the keyStore
@ -240,8 +257,7 @@ public class HashiCorpVaultProperties {
* @return Builder * @return Builder
*/ */
public HashiCorpVaultProperties build() { public HashiCorpVaultProperties build() {
return new HashiCorpVaultProperties(uri, keyStore, keyStoreType, keyStorePassword, trustStore, trustStoreType, return new HashiCorpVaultProperties(this);
trustStorePassword, authPropertiesFilename, enabledTlsCipherSuites, enabledTlsProtocols, connectionTimeout, readTimeout);
} }
} }
} }

View File

@ -44,11 +44,16 @@ public class BeanPropertyLookup extends PropertyLookup {
.filter(pd -> pd.getReadMethod().getAnnotation(HashiCorpVaultProperty.class) != null) .filter(pd -> pd.getReadMethod().getAnnotation(HashiCorpVaultProperty.class) != null)
.collect(Collectors.toMap( .collect(Collectors.toMap(
pd -> getPropertyKey(prefix, pd), pd -> getPropertyKey(prefix, pd),
pd -> pd.getReadMethod().getReturnType().equals(String.class) ? new ValuePropertyLookup(pd) pd -> isValueProperty(pd) ? new ValuePropertyLookup(pd)
: new BeanPropertyLookup(getPropertyKey(prefix, pd), pd.getReadMethod().getReturnType(), pd) : new BeanPropertyLookup(getPropertyKey(prefix, pd), pd.getReadMethod().getReturnType(), pd)
)); ));
} }
private boolean isValueProperty(final PropertyDescriptor propertyDescriptor) {
final Class<?> returnType = propertyDescriptor.getReadMethod().getReturnType();
return returnType.equals(String.class) || returnType.isPrimitive();
}
private static String getPropertyKey(final String prefix, final PropertyDescriptor propertyDescriptor) { private static String getPropertyKey(final String prefix, final PropertyDescriptor propertyDescriptor) {
final HashiCorpVaultProperty propertyAnnotation = propertyDescriptor.getReadMethod().getAnnotation(HashiCorpVaultProperty.class); final HashiCorpVaultProperty propertyAnnotation = propertyDescriptor.getReadMethod().getAnnotation(HashiCorpVaultProperty.class);
final String unqualifiedPropertyKey = !propertyAnnotation.key().isEmpty() ? propertyAnnotation.key() : propertyDescriptor.getDisplayName(); final String unqualifiedPropertyKey = !propertyAnnotation.key().isEmpty() ? propertyAnnotation.key() : propertyDescriptor.getDisplayName();

View File

@ -32,6 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
* vault server -dev * vault server -dev
* vault secrets enable transit * vault secrets enable transit
* vault secrets enable kv * vault secrets enable kv
* vault secrets enable kv-v2
* vault write -f transit/keys/nifi * vault write -f transit/keys/nifi
* *
* Make note of the Root Token and create a properties file with the contents: * Make note of the Root Token and create a properties file with the contents:
@ -48,10 +49,13 @@ public class StandardHashiCorpVaultCommunicationServiceIT {
@BeforeEach @BeforeEach
public void init() { public void init() {
vcs = new StandardHashiCorpVaultCommunicationService(new HashiCorpVaultProperties.HashiCorpVaultPropertiesBuilder() vcs = new StandardHashiCorpVaultCommunicationService(defaultServiceBuilder().build());
}
private HashiCorpVaultProperties.HashiCorpVaultPropertiesBuilder defaultServiceBuilder() {
return new HashiCorpVaultProperties.HashiCorpVaultPropertiesBuilder()
.setAuthPropertiesFilename(System.getProperty("vault.auth.properties")) .setAuthPropertiesFilename(System.getProperty("vault.auth.properties"))
.setUri("http://127.0.0.1:8200") .setUri("http://127.0.0.1:8200");
.build());
} }
@Test @Test
@ -83,6 +87,22 @@ public class StandardHashiCorpVaultCommunicationServiceIT {
assertEquals(value, resultValue); assertEquals(value, resultValue);
} }
/**
* Run <code>vault kv get kv_v2/key</code> to see the secret
*/
@Test
public void testReadWriteSecret_kv_v2() {
final String key = "key";
final String value = "value";
vcs = new StandardHashiCorpVaultCommunicationService(defaultServiceBuilder().setKvVersion(2).build());
vcs.writeKeyValueSecret("kv-v2", key, value);
final String resultValue = vcs.readKeyValueSecret("kv-v2", key).orElseThrow(() -> new NullPointerException("Missing secret for kv/key"));
assertEquals(value, resultValue);
}
/** /**
* Run <code>vault kv get kv/secret</code> to see the secret * Run <code>vault kv get kv/secret</code> to see the secret
*/ */

View File

@ -25,6 +25,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.springframework.vault.authentication.ClientAuthentication; import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.client.VaultEndpoint; import org.springframework.vault.client.VaultEndpoint;
import org.springframework.vault.core.VaultKeyValueOperationsSupport;
import org.springframework.vault.support.SslConfiguration; import org.springframework.vault.support.SslConfiguration;
import java.io.File; import java.io.File;
@ -134,6 +135,29 @@ public class TestHashiCorpVaultConfiguration {
this.runTest("http"); this.runTest("http");
} }
@Test
public void testKvVersion() {
config = new HashiCorpVaultConfiguration(new HashiCorpVaultPropertySource(propertiesBuilder.build()));
assertEquals(VaultKeyValueOperationsSupport.KeyValueBackend.KV_1, config.getKeyValueBackend());
propertiesBuilder.setKvVersion(2);
config = new HashiCorpVaultConfiguration(new HashiCorpVaultPropertySource(propertiesBuilder.build()));
assertEquals(VaultKeyValueOperationsSupport.KeyValueBackend.KV_2, config.getKeyValueBackend());
propertiesBuilder.setKvVersion(1);
config = new HashiCorpVaultConfiguration(new HashiCorpVaultPropertySource(propertiesBuilder.build()));
assertEquals(VaultKeyValueOperationsSupport.KeyValueBackend.KV_1, config.getKeyValueBackend());
}
@Test
public void testKvVersionInvalid() {
propertiesBuilder.setKvVersion(0);
assertThrows(HashiCorpVaultConfigurationException.class, () -> new HashiCorpVaultConfiguration(new HashiCorpVaultPropertySource(propertiesBuilder.build())));
propertiesBuilder.setKvVersion(3);
assertThrows(HashiCorpVaultConfigurationException.class, () -> new HashiCorpVaultConfiguration(new HashiCorpVaultPropertySource(propertiesBuilder.build())));
}
@Test @Test
public void testTlsProperties() throws IOException { public void testTlsProperties() throws IOException {
propertiesBuilder.setKeyStore(keystoreFile.toFile().getAbsolutePath()); propertiesBuilder.setKeyStore(keystoreFile.toFile().getAbsolutePath());

View File

@ -32,6 +32,8 @@ import java.util.Arrays;
import java.util.Optional; import java.util.Optional;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.mockito.Mockito.when;
public class TestStandardHashiCorpVaultCommunicationService { public class TestStandardHashiCorpVaultCommunicationService {
public static final String URI_VALUE = "http://127.0.0.1:8200"; public static final String URI_VALUE = "http://127.0.0.1:8200";
public static final String CIPHER_SUITE_VALUE = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384"; public static final String CIPHER_SUITE_VALUE = "TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384";
@ -47,9 +49,10 @@ public class TestStandardHashiCorpVaultCommunicationService {
properties = Mockito.mock(HashiCorpVaultProperties.class); properties = Mockito.mock(HashiCorpVaultProperties.class);
sslProperties = Mockito.mock(HashiCorpVaultSslProperties.class); sslProperties = Mockito.mock(HashiCorpVaultSslProperties.class);
Mockito.when(properties.getUri()).thenReturn(URI_VALUE); when(properties.getUri()).thenReturn(URI_VALUE);
Mockito.when(properties.getAuthPropertiesFilename()).thenReturn(authProps.getAbsolutePath()); when(properties.getAuthPropertiesFilename()).thenReturn(authProps.getAbsolutePath());
Mockito.when(properties.getSsl()).thenReturn(sslProperties); when(properties.getSsl()).thenReturn(sslProperties);
when(properties.getKvVersion()).thenReturn(1);
} }
@AfterEach @AfterEach
@ -88,8 +91,8 @@ public class TestStandardHashiCorpVaultCommunicationService {
@Test @Test
public void testTimeouts() { public void testTimeouts() {
Mockito.when(properties.getConnectionTimeout()).thenReturn(Optional.of("20 secs")); when(properties.getConnectionTimeout()).thenReturn(Optional.of("20 secs"));
Mockito.when(properties.getReadTimeout()).thenReturn(Optional.of("40 secs")); when(properties.getReadTimeout()).thenReturn(Optional.of("40 secs"));
this.configureService(); this.configureService();
} }
@ -97,17 +100,17 @@ public class TestStandardHashiCorpVaultCommunicationService {
public void testTLS() { public void testTLS() {
TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build(); TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
Mockito.when(sslProperties.getKeyStore()).thenReturn(tlsConfiguration.getKeystorePath()); when(sslProperties.getKeyStore()).thenReturn(tlsConfiguration.getKeystorePath());
Mockito.when(sslProperties.getKeyStorePassword()).thenReturn(tlsConfiguration.getKeystorePassword()); when(sslProperties.getKeyStorePassword()).thenReturn(tlsConfiguration.getKeystorePassword());
Mockito.when(sslProperties.getKeyStoreType()).thenReturn(tlsConfiguration.getKeystoreType().getType()); when(sslProperties.getKeyStoreType()).thenReturn(tlsConfiguration.getKeystoreType().getType());
Mockito.when(sslProperties.getTrustStore()).thenReturn(tlsConfiguration.getTruststorePath()); when(sslProperties.getTrustStore()).thenReturn(tlsConfiguration.getTruststorePath());
Mockito.when(sslProperties.getTrustStorePassword()).thenReturn(tlsConfiguration.getTruststorePassword()); when(sslProperties.getTrustStorePassword()).thenReturn(tlsConfiguration.getTruststorePassword());
Mockito.when(sslProperties.getTrustStoreType()).thenReturn(tlsConfiguration.getTruststoreType().getType()); when(sslProperties.getTrustStoreType()).thenReturn(tlsConfiguration.getTruststoreType().getType());
Mockito.when(sslProperties.getEnabledProtocols()).thenReturn(Arrays.stream(tlsConfiguration.getEnabledProtocols()) when(sslProperties.getEnabledProtocols()).thenReturn(Arrays.stream(tlsConfiguration.getEnabledProtocols())
.collect(Collectors.joining(","))); .collect(Collectors.joining(",")));
Mockito.when(sslProperties.getEnabledCipherSuites()).thenReturn(CIPHER_SUITE_VALUE); when(sslProperties.getEnabledCipherSuites()).thenReturn(CIPHER_SUITE_VALUE);
Mockito.when(properties.getUri()).thenReturn(URI_VALUE.replace("http", "https")); when(properties.getUri()).thenReturn(URI_VALUE.replace("http", "https"));
this.configureService(); this.configureService();
this.ensureTlsPropertiesAccessed(1); this.ensureTlsPropertiesAccessed(1);

View File

@ -1852,6 +1852,7 @@ Following are the configuration properties available inside the `bootstrap-hashi
[options="header,footer"] [options="header,footer"]
|=== |===
|Property Name|Description|Default |Property Name|Description|Default
|`vault.kv.version`|The Key/Value Secrets Engine version: `1` for unversioned, and `2` for versioned. This must match the versioned enabled in Vault.|`1`
|`vault.connection.timeout`|The connection timeout of the Vault client|`5 secs` |`vault.connection.timeout`|The connection timeout of the Vault client|`5 secs`
|`vault.read.timeout`|The read timeout of the Vault client|`15 secs` |`vault.read.timeout`|The read timeout of the Vault client|`15 secs`
|`vault.ssl.enabledCipherSuites`|A comma-separated list of the enabled TLS cipher suites|_none_ |`vault.ssl.enabledCipherSuites`|A comma-separated list of the enabled TLS cipher suites|_none_

View File

@ -23,6 +23,8 @@ vault.transit.path=
# Key/Value Path is required to enable the Sensitive Properties Provider Protection Scheme 'hashicorp/vault/kv/{path}' # Key/Value Path is required to enable the Sensitive Properties Provider Protection Scheme 'hashicorp/vault/kv/{path}'
vault.kv.path= vault.kv.path=
# Key/Value Secrets Engine version may be 1 or 2, and defaults to 1
# vault.kv.version=1
# Token Authentication example properties # Token Authentication example properties
# vault.authentication=TOKEN # vault.authentication=TOKEN

View File

@ -23,6 +23,8 @@ vault.transit.path=
# Key/Value Path is required to enable the Sensitive Properties Provider Protection Scheme 'hashicorp/vault/kv/{path}' # Key/Value Path is required to enable the Sensitive Properties Provider Protection Scheme 'hashicorp/vault/kv/{path}'
vault.kv.path= vault.kv.path=
# Key/Value Secrets Engine version may be 1 or 2, and defaults to 1
# vault.kv.version=1
# Token Authentication example properties # Token Authentication example properties
# vault.authentication=TOKEN # vault.authentication=TOKEN