This is an automated email from the ASF dual-hosted git repository.
bbende pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new 48de70a568 NIFI-10071: Adding support for HashiCorp Vault K/V version
2 Secrets Engine (#6087)
48de70a568 is described below
commit 48de70a56883c69a7bc51545c7cd410563b995ec
Author: Joe Gresock <[email protected]>
AuthorDate: Wed Jun 1 15:09:01 2022 -0400
NIFI-10071: Adding support for HashiCorp Vault K/V version 2 Secrets Engine
(#6087)
---
.../HashiCorpVaultCommunicationService.java | 24 +++++-----
...StandardHashiCorpVaultCommunicationService.java | 13 +++---
.../config/HashiCorpVaultConfiguration.java | 26 +++++++++++
.../hashicorp/config/HashiCorpVaultProperties.java | 54 ++++++++++++++--------
.../config/lookup/BeanPropertyLookup.java | 7 ++-
...andardHashiCorpVaultCommunicationServiceIT.java | 26 +++++++++--
.../hashicorp/TestHashiCorpVaultConfiguration.java | 24 ++++++++++
...StandardHashiCorpVaultCommunicationService.java | 31 +++++++------
.../src/main/asciidoc/administration-guide.adoc | 1 +
.../resources/conf/bootstrap-hashicorp-vault.conf | 2 +
.../resources/conf/bootstrap-hashicorp-vault.conf | 2 +
11 files changed, 155 insertions(+), 55 deletions(-)
diff --git
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
b/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
index 8e9f8c5594..840db72ffa 100644
---
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
+++
b/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultCommunicationService.java
@@ -46,39 +46,39 @@ public interface HashiCorpVaultCommunicationService {
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>
- * @param keyValuePath The Vault path to use for the configured Key/Value
v1 Secrets Engine
+ * @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
Secrets Engine
* @param secretKey The secret key
* @param value The secret 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>
- * @param keyValuePath The Vault path to use for the configured Key/Value
v1 Secrets Engine
+ * @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
Secrets Engine
* @param secretKey The secret key
* @return The secret value, or empty if not found
*/
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>
- * @param keyValuePath The Vault path to use for the configured Key/Value
v1 Secrets Engine
+ * @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
Secrets Engine
* @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);
/**
- * 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>
- * @param keyValuePath The Vault path to use for the configured Key/Value
v1 Secrets Engine
+ * @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
Secrets Engine
* @param secretKey The secret key
* @return A map from key to value from the secret key/values, or an empty
map if not found
*/
diff --git
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
b/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
index 34508436d3..a407b85eed 100644
---
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
+++
b/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
@@ -25,6 +25,7 @@ import org.springframework.core.env.PropertySource;
import org.springframework.vault.authentication.SimpleSessionManager;
import org.springframework.vault.client.ClientHttpRequestFactoryFactory;
import org.springframework.vault.core.VaultKeyValueOperations;
+import
org.springframework.vault.core.VaultKeyValueOperationsSupport.KeyValueBackend;
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultTransitOperations;
import org.springframework.vault.support.Ciphertext;
@@ -37,8 +38,6 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
-import static
org.springframework.vault.core.VaultKeyValueOperationsSupport.KeyValueBackend.KV_1;
-
/**
* Implements the VaultCommunicationService using Spring Vault
*/
@@ -46,6 +45,7 @@ public class StandardHashiCorpVaultCommunicationService
implements HashiCorpVaul
private final VaultTemplate vaultTemplate;
private final VaultTransitOperations transitOperations;
private final Map<String, VaultKeyValueOperations> keyValueOperationsMap;
+ private final KeyValueBackend keyValueBackend;
/**
* Creates a VaultCommunicationService that uses Spring Vault.
@@ -60,6 +60,7 @@ public class StandardHashiCorpVaultCommunicationService
implements HashiCorpVaul
new
SimpleSessionManager(vaultConfiguration.clientAuthentication()));
transitOperations = vaultTemplate.opsForTransit();
+ keyValueBackend = vaultConfiguration.getKeyValueBackend();
keyValueOperationsMap = new HashMap<>();
}
@@ -94,7 +95,7 @@ public class StandardHashiCorpVaultCommunicationService
implements HashiCorpVaul
Objects.requireNonNull(secretKey, "Secret secretKey must be
specified");
Objects.requireNonNull(value, "Secret value must be specified");
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));
}
@@ -109,7 +110,7 @@ public class StandardHashiCorpVaultCommunicationService
implements HashiCorpVaul
Objects.requireNonNull(keyValuePath, "Vault K/V path must be
specified");
Objects.requireNonNull(secretKey, "Secret secretKey must be
specified");
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);
return response == null ? Optional.empty() :
Optional.ofNullable(response.getRequiredData().getValue());
}
@@ -123,14 +124,14 @@ public class StandardHashiCorpVaultCommunicationService
implements HashiCorpVaul
return;
}
final VaultKeyValueOperations keyValueOperations =
keyValueOperationsMap
- .computeIfAbsent(keyValuePath, path ->
vaultTemplate.opsForKeyValue(path, KV_1));
+ .computeIfAbsent(keyValuePath, path ->
vaultTemplate.opsForKeyValue(path, keyValueBackend));
keyValueOperations.put(secretKey, keyValues);
}
@Override
public Map<String, String> readKeyValueSecretMap(final String
keyValuePath, final String key) {
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);
return response == null ? Collections.emptyMap() : (Map<String,
String>) response.getRequiredData();
}
diff --git
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
b/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
index 34ce6c68a9..873477c097 100644
---
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
+++
b/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
@@ -25,6 +25,7 @@ import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.support.ResourcePropertySource;
import org.springframework.vault.client.RestTemplateFactory;
import org.springframework.vault.config.EnvironmentVaultConfiguration;
+import
org.springframework.vault.core.VaultKeyValueOperationsSupport.KeyValueBackend;
import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.SslConfiguration;
@@ -37,10 +38,14 @@ import java.util.concurrent.TimeUnit;
* A Vault configuration that uses the NiFiVaultEnvironment.
*/
public class HashiCorpVaultConfiguration extends EnvironmentVaultConfiguration
{
+ private static final int KV_V1 = 1;
+ private static final int KV_V2 = 2;
+
public enum VaultConfigurationKey {
AUTHENTICATION_PROPERTIES_FILE("vault.authentication.properties.file"),
READ_TIMEOUT("vault.read.timeout"),
CONNECTION_TIMEOUT("vault.connection.timeout"),
+ KV_VERSION("vault.kv.version"),
URI("vault.uri");
private final String key;
@@ -62,6 +67,7 @@ public class HashiCorpVaultConfiguration extends
EnvironmentVaultConfiguration {
private final SslConfiguration sslConfiguration;
private final ClientOptions clientOptions;
+ private final KeyValueBackend keyValueBackend;
/**
* 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));
sslConfiguration =
env.getProperty(VaultConfigurationKey.URI.key).contains(HTTPS)
@@ -92,6 +114,10 @@ public class HashiCorpVaultConfiguration extends
EnvironmentVaultConfiguration {
clientOptions = getClientOptions();
}
+ public KeyValueBackend getKeyValueBackend() {
+ return keyValueBackend;
+ }
+
/**
* A convenience method to create a PropertySource from a file on disk.
* @param filename The properties filename.
diff --git
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultProperties.java
b/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultProperties.java
index 99b6ff13a9..5042dd3593 100644
---
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultProperties.java
+++
b/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultProperties.java
@@ -38,26 +38,27 @@ public class HashiCorpVaultProperties {
private final HashiCorpVaultSslProperties ssl;
private final Optional<String> connectionTimeout;
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,
- final String trustStoreType, final String
trustStorePassword, final String authPropertiesFilename,
- final String enabledTlsCipherSuites,
final String enabledTlsProtocols, final String connectionTimeout, final String
readTimeout) {
- Objects.requireNonNull(uri, "Vault URI is required");
- Objects.requireNonNull(authPropertiesFilename, "Vault auth properties
filename is required");
- this.uri = uri;
- this.authPropertiesFilename = authPropertiesFilename;
- this.ssl = new HashiCorpVaultSslProperties(keyStore, keyStoreType,
keyStorePassword, trustStore, trustStoreType, trustStorePassword,
- enabledTlsCipherSuites, enabledTlsProtocols);
- this.connectionTimeout = connectionTimeout == null ? Optional.empty()
: Optional.of(connectionTimeout);
- this.readTimeout = readTimeout == null ? Optional.empty() :
Optional.of(readTimeout);
+ private HashiCorpVaultProperties(final HashiCorpVaultPropertiesBuilder
builder) {
+ this.uri = Objects.requireNonNull(builder.uri, "Vault URI is
required");;
+ this.authPropertiesFilename =
Objects.requireNonNull(builder.authPropertiesFilename, "Vault auth properties
filename is required");
+ this.ssl = new HashiCorpVaultSslProperties(builder.keyStore,
builder.keyStoreType, builder.keyStorePassword,
+ builder.trustStore, builder.trustStoreType,
builder.trustStorePassword,builder.enabledTlsCipherSuites,
builder.enabledTlsProtocols);
+ this.connectionTimeout = builder.connectionTimeout == null ?
Optional.empty() : Optional.of(builder.connectionTimeout);
+ this.readTimeout = builder.readTimeout == null ? Optional.empty() :
Optional.of(builder.readTimeout);
+ this.kvVersion = builder.kvVersion;
+ if (kvVersion != 1 && kvVersion != 2) {
+ throw new HashiCorpVaultConfigurationException("Key/Value version
" + kvVersion + " is not supported");
+ }
if (uri.startsWith(HTTPS)) {
- Objects.requireNonNull(keyStore, "KeyStore is required with an
https URI");
- Objects.requireNonNull(keyStorePassword, "KeyStore password is
required with an https URI");
- Objects.requireNonNull(keyStoreType, "KeyStore type is required
with an https URI");
- Objects.requireNonNull(trustStore, "TrustStore is required with an
https URI");
- Objects.requireNonNull(trustStorePassword, "TrustStore password is
required with an https URI");
- Objects.requireNonNull(trustStoreType, "TrustStore type is
required with an https URI");
+ Objects.requireNonNull(builder.keyStore, "KeyStore is required
with an https URI");
+ Objects.requireNonNull(builder.keyStorePassword, "KeyStore
password is required with an https URI");
+ Objects.requireNonNull(builder.keyStoreType, "KeyStore type is
required with an https URI");
+ Objects.requireNonNull(builder.trustStore, "TrustStore is required
with an https URI");
+ Objects.requireNonNull(builder.trustStorePassword, "TrustStore
password is required with an https URI");
+ Objects.requireNonNull(builder.trustStoreType, "TrustStore type is
required with an https URI");
}
validateAuthProperties();
}
@@ -79,6 +80,11 @@ public class HashiCorpVaultProperties {
return ssl;
}
+ @HashiCorpVaultProperty(key = "kv.version")
+ public int getKvVersion() {
+ return kvVersion;
+ }
+
@HashiCorpVaultProperty(key = "authentication.properties.file")
public String getAuthPropertiesFilename() {
return authPropertiesFilename;
@@ -108,6 +114,7 @@ public class HashiCorpVaultProperties {
private String enabledTlsProtocols;
private String connectionTimeout;
private String readTimeout;
+ private int kvVersion = 1;
/**
* 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;
}
+ /**
+ * 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.
* @param keyStore Path to the keyStore
@@ -240,8 +257,7 @@ public class HashiCorpVaultProperties {
* @return Builder
*/
public HashiCorpVaultProperties build() {
- return new HashiCorpVaultProperties(uri, keyStore, keyStoreType,
keyStorePassword, trustStore, trustStoreType,
- trustStorePassword, authPropertiesFilename,
enabledTlsCipherSuites, enabledTlsProtocols, connectionTimeout, readTimeout);
+ return new HashiCorpVaultProperties(this);
}
}
}
diff --git
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/BeanPropertyLookup.java
b/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/BeanPropertyLookup.java
index da873afa24..1b7bab2a33 100644
---
a/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/BeanPropertyLookup.java
+++
b/nifi-commons/nifi-vault-utils/src/main/java/org/apache/nifi/vault/hashicorp/config/lookup/BeanPropertyLookup.java
@@ -44,11 +44,16 @@ public class BeanPropertyLookup extends PropertyLookup {
.filter(pd ->
pd.getReadMethod().getAnnotation(HashiCorpVaultProperty.class) != null)
.collect(Collectors.toMap(
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)
));
}
+ 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) {
final HashiCorpVaultProperty propertyAnnotation =
propertyDescriptor.getReadMethod().getAnnotation(HashiCorpVaultProperty.class);
final String unqualifiedPropertyKey =
!propertyAnnotation.key().isEmpty() ? propertyAnnotation.key() :
propertyDescriptor.getDisplayName();
diff --git
a/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationServiceIT.java
b/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationServiceIT.java
index 6261376dcd..2080e147a8 100644
---
a/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationServiceIT.java
+++
b/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationServiceIT.java
@@ -32,6 +32,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
* vault server -dev
* vault secrets enable transit
* vault secrets enable kv
+ * vault secrets enable kv-v2
* vault write -f transit/keys/nifi
*
* Make note of the Root Token and create a properties file with the contents:
@@ -48,10 +49,13 @@ public class StandardHashiCorpVaultCommunicationServiceIT {
@BeforeEach
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"))
- .setUri("http://127.0.0.1:8200")
- .build());
+ .setUri("http://127.0.0.1:8200");
}
@Test
@@ -83,6 +87,22 @@ public class StandardHashiCorpVaultCommunicationServiceIT {
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
*/
diff --git
a/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultConfiguration.java
b/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultConfiguration.java
index 9dd75f9b6f..190b9aa6ce 100644
---
a/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultConfiguration.java
+++
b/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/TestHashiCorpVaultConfiguration.java
@@ -25,6 +25,7 @@ import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.vault.authentication.ClientAuthentication;
import org.springframework.vault.client.VaultEndpoint;
+import org.springframework.vault.core.VaultKeyValueOperationsSupport;
import org.springframework.vault.support.SslConfiguration;
import java.io.File;
@@ -134,6 +135,29 @@ public class TestHashiCorpVaultConfiguration {
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
public void testTlsProperties() throws IOException {
propertiesBuilder.setKeyStore(keystoreFile.toFile().getAbsolutePath());
diff --git
a/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
b/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
index 4eb14eb79d..a24f13b28d 100644
---
a/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
+++
b/nifi-commons/nifi-vault-utils/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
@@ -32,6 +32,8 @@ import java.util.Arrays;
import java.util.Optional;
import java.util.stream.Collectors;
+import static org.mockito.Mockito.when;
+
public class TestStandardHashiCorpVaultCommunicationService {
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";
@@ -47,9 +49,10 @@ public class TestStandardHashiCorpVaultCommunicationService {
properties = Mockito.mock(HashiCorpVaultProperties.class);
sslProperties = Mockito.mock(HashiCorpVaultSslProperties.class);
- Mockito.when(properties.getUri()).thenReturn(URI_VALUE);
-
Mockito.when(properties.getAuthPropertiesFilename()).thenReturn(authProps.getAbsolutePath());
- Mockito.when(properties.getSsl()).thenReturn(sslProperties);
+ when(properties.getUri()).thenReturn(URI_VALUE);
+
when(properties.getAuthPropertiesFilename()).thenReturn(authProps.getAbsolutePath());
+ when(properties.getSsl()).thenReturn(sslProperties);
+ when(properties.getKvVersion()).thenReturn(1);
}
@AfterEach
@@ -88,8 +91,8 @@ public class TestStandardHashiCorpVaultCommunicationService {
@Test
public void testTimeouts() {
-
Mockito.when(properties.getConnectionTimeout()).thenReturn(Optional.of("20
secs"));
- Mockito.when(properties.getReadTimeout()).thenReturn(Optional.of("40
secs"));
+ when(properties.getConnectionTimeout()).thenReturn(Optional.of("20
secs"));
+ when(properties.getReadTimeout()).thenReturn(Optional.of("40 secs"));
this.configureService();
}
@@ -97,17 +100,17 @@ public class
TestStandardHashiCorpVaultCommunicationService {
public void testTLS() {
TlsConfiguration tlsConfiguration = new
TemporaryKeyStoreBuilder().build();
-
Mockito.when(sslProperties.getKeyStore()).thenReturn(tlsConfiguration.getKeystorePath());
-
Mockito.when(sslProperties.getKeyStorePassword()).thenReturn(tlsConfiguration.getKeystorePassword());
-
Mockito.when(sslProperties.getKeyStoreType()).thenReturn(tlsConfiguration.getKeystoreType().getType());
-
Mockito.when(sslProperties.getTrustStore()).thenReturn(tlsConfiguration.getTruststorePath());
-
Mockito.when(sslProperties.getTrustStorePassword()).thenReturn(tlsConfiguration.getTruststorePassword());
-
Mockito.when(sslProperties.getTrustStoreType()).thenReturn(tlsConfiguration.getTruststoreType().getType());
-
Mockito.when(sslProperties.getEnabledProtocols()).thenReturn(Arrays.stream(tlsConfiguration.getEnabledProtocols())
+
when(sslProperties.getKeyStore()).thenReturn(tlsConfiguration.getKeystorePath());
+
when(sslProperties.getKeyStorePassword()).thenReturn(tlsConfiguration.getKeystorePassword());
+
when(sslProperties.getKeyStoreType()).thenReturn(tlsConfiguration.getKeystoreType().getType());
+
when(sslProperties.getTrustStore()).thenReturn(tlsConfiguration.getTruststorePath());
+
when(sslProperties.getTrustStorePassword()).thenReturn(tlsConfiguration.getTruststorePassword());
+
when(sslProperties.getTrustStoreType()).thenReturn(tlsConfiguration.getTruststoreType().getType());
+
when(sslProperties.getEnabledProtocols()).thenReturn(Arrays.stream(tlsConfiguration.getEnabledProtocols())
.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.ensureTlsPropertiesAccessed(1);
diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc
b/nifi-docs/src/main/asciidoc/administration-guide.adoc
index dda5ef3e84..d61b2134ef 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -1852,6 +1852,7 @@ Following are the configuration properties available
inside the `bootstrap-hashi
[options="header,footer"]
|===
|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.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_
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/bootstrap-hashicorp-vault.conf
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/bootstrap-hashicorp-vault.conf
index bbf54f57cf..bf6975b962 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/bootstrap-hashicorp-vault.conf
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/bootstrap-hashicorp-vault.conf
@@ -23,6 +23,8 @@ vault.transit.path=
# Key/Value Path is required to enable the Sensitive Properties Provider
Protection Scheme 'hashicorp/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
# vault.authentication=TOKEN
diff --git
a/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/bootstrap-hashicorp-vault.conf
b/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/bootstrap-hashicorp-vault.conf
index bbf54f57cf..bf6975b962 100644
---
a/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/bootstrap-hashicorp-vault.conf
+++
b/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/bootstrap-hashicorp-vault.conf
@@ -23,6 +23,8 @@ vault.transit.path=
# Key/Value Path is required to enable the Sensitive Properties Provider
Protection Scheme 'hashicorp/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
# vault.authentication=TOKEN