This is an automated email from the ASF dual-hosted git repository.
pvillard31 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 ee33ae84e9e NIFI-15948 Switched HashiCorp Vault Client Service to
SSLContextProvider (#11257)
ee33ae84e9e is described below
commit ee33ae84e9e24a218fa2090e13e71e7441c8c2df
Author: David Handermann <[email protected]>
AuthorDate: Sat May 16 04:57:05 2026 -0500
NIFI-15948 Switched HashiCorp Vault Client Service to SSLContextProvider
(#11257)
---
...StandardHashiCorpVaultCommunicationService.java | 35 +++++++++++++++++--
.../config/HashiCorpVaultConfiguration.java | 25 --------------
...StandardHashiCorpVaultCommunicationService.java | 8 ++---
.../hashicorp/HashiCorpVaultClientService.java | 7 ++--
.../StandardHashiCorpVaultClientService.java | 39 +++++-----------------
5 files changed, 48 insertions(+), 66 deletions(-)
diff --git
a/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
index 48c5cb1135a..fc8cccab810 100644
---
a/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
+++
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultCommunicationService.java
@@ -23,6 +23,7 @@ import
org.apache.nifi.vault.hashicorp.config.HashiCorpVaultProperties;
import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.http.client.ClientHttpRequestFactory;
+import org.springframework.http.client.JdkClientHttpRequestFactory;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.vault.authentication.LifecycleAwareSessionManager;
import org.springframework.vault.client.ClientHttpRequestFactoryFactory;
@@ -32,12 +33,15 @@ import
org.springframework.vault.core.VaultKeyValueOperationsSupport.KeyValueBac
import org.springframework.vault.core.VaultTemplate;
import org.springframework.vault.core.VaultTransitOperations;
import org.springframework.vault.support.Ciphertext;
+import org.springframework.vault.support.ClientOptions;
import org.springframework.vault.support.Plaintext;
import org.springframework.vault.support.VaultResponseSupport;
import org.springframework.web.client.RestOperations;
import java.io.Closeable;
import java.io.IOException;
+import java.net.http.HttpClient;
+import java.security.GeneralSecurityException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
@@ -45,11 +49,14 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
+import javax.net.ssl.SSLContext;
/**
* Implements the VaultCommunicationService using Spring Vault
*/
public class StandardHashiCorpVaultCommunicationService implements
HashiCorpVaultCommunicationService, Closeable {
+ private static final String SSL_CONTEXT_PROPERTY =
SSLContext.class.getName();
+
private final LifecycleAwareSessionManager sessionManager;
private final ThreadPoolTaskScheduler taskScheduler;
private final VaultTemplate vaultTemplate;
@@ -70,7 +77,31 @@ public class StandardHashiCorpVaultCommunicationService
implements HashiCorpVaul
taskScheduler.setDaemon(true);
taskScheduler.afterPropertiesSet();
- final ClientHttpRequestFactory clientHttpRequestFactory =
ClientHttpRequestFactoryFactory.create(vaultConfiguration.clientOptions(),
vaultConfiguration.sslConfiguration());
+ final ClientHttpRequestFactory clientHttpRequestFactory;
+
+ final ClientOptions clientOptions = vaultConfiguration.clientOptions();
+ final PropertySource<?> propertySource = propertySources[0];
+ final Object sslContextProperty =
propertySource.getProperty(SSL_CONTEXT_PROPERTY);
+ if (sslContextProperty instanceof SSLContext sslContext) {
+ // Customize HttpClient construction with configured SSLContext
+ final HttpClient.Builder httpClientBuilder =
HttpClient.newBuilder();
+
httpClientBuilder.connectTimeout(clientOptions.getConnectionTimeout());
+ httpClientBuilder.followRedirects(HttpClient.Redirect.ALWAYS);
+ httpClientBuilder.sslContext(sslContext);
+
+ final HttpClient httpClient = httpClientBuilder.build();
+ final JdkClientHttpRequestFactory jdkClientHttpRequestFactory =
new JdkClientHttpRequestFactory(httpClient);
+
jdkClientHttpRequestFactory.setReadTimeout(clientOptions.getReadTimeout());
+ clientHttpRequestFactory = jdkClientHttpRequestFactory;
+ } else {
+ // Build with standard Spring Vault methods
+ try {
+ clientHttpRequestFactory =
ClientHttpRequestFactoryFactory.JdkHttpClient.usingJdkHttpClient(clientOptions,
vaultConfiguration.sslConfiguration());
+ } catch (final GeneralSecurityException | IOException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
+
final RestOperations restOperations =
VaultClients.createRestTemplate(vaultConfiguration.vaultEndpoint(),
clientHttpRequestFactory);
sessionManager = new
LifecycleAwareSessionManager(vaultConfiguration.clientAuthentication(),
taskScheduler, restOperations);
@@ -96,7 +127,7 @@ public class StandardHashiCorpVaultCommunicationService
implements HashiCorpVaul
}
@Override
- public void close() throws IOException {
+ public void close() {
sessionManager.destroy();
taskScheduler.destroy();
}
diff --git
a/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
index 46c406fb6f8..3188733c3bf 100644
---
a/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
+++
b/nifi-commons/nifi-hashicorp-vault/src/main/java/org/apache/nifi/vault/hashicorp/config/HashiCorpVaultConfiguration.java
@@ -36,7 +36,6 @@ import org.springframework.vault.support.SslConfiguration;
import java.io.IOException;
import java.nio.file.Paths;
import java.time.Duration;
-import java.util.Objects;
import java.util.concurrent.TimeUnit;
/**
@@ -123,7 +122,6 @@ public class HashiCorpVaultConfiguration extends
EnvironmentVaultConfiguration {
}
}
this.keyValueBackend = keyValueBackend;
- validateProperties(env);
this.setApplicationContext(new HashiCorpVaultApplicationContext(env));
@@ -133,29 +131,6 @@ public class HashiCorpVaultConfiguration extends
EnvironmentVaultConfiguration {
clientOptions = getClientOptions();
}
- private void validateProperties(final ConfigurableEnvironment environment)
{
- try {
- final String vaultUri =
Objects.requireNonNull(environment.getProperty(VaultConfigurationKey.URI.key),
- "Missing required property " +
VaultConfigurationKey.URI.key);
- if (vaultUri.startsWith(HTTPS)) {
- requireSslProperty("vault.ssl.key-store", environment);
- requireSslProperty("vault.ssl.key-store-password",
environment);
- requireSslProperty("vault.ssl.key-store-type", environment);
- requireSslProperty("vault.ssl.trust-store", environment);
- requireSslProperty("vault.ssl.trust-store-password",
environment);
- requireSslProperty("vault.ssl.trust-store-type", environment);
- }
- } catch (final NullPointerException e) {
- // Rethrow as IllegalArgumentException
- throw new IllegalArgumentException(e.getMessage(), e);
- }
-
- }
-
- private void requireSslProperty(final String propertyName, final
ConfigurableEnvironment environment) {
- Objects.requireNonNull(environment.getProperty(propertyName),
propertyName + " is required with an https URI");
- }
-
public KeyValueBackend getKeyValueBackend() {
return keyValueBackend;
}
diff --git
a/nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
b/nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
index e013449a986..8960bff1494 100644
---
a/nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
+++
b/nifi-commons/nifi-hashicorp-vault/src/test/java/org/apache/nifi/vault/hashicorp/TestStandardHashiCorpVaultCommunicationService.java
@@ -63,10 +63,10 @@ public class TestStandardHashiCorpVaultCommunicationService
{
}
@Test
- public void testBasicConfiguration() throws Exception {
+ public void testBasicConfiguration() {
try (StandardHashiCorpVaultCommunicationService ignored =
this.configureService()) {
- // Once to check if the URI is https, once by VaultTemplate, once
by RestTemplate, and once to validate
- Mockito.verify(properties, Mockito.times(4)).getUri();
+ // Once to check if the URI is https, once by VaultTemplate, once
by RestTemplate
+ Mockito.verify(properties, Mockito.times(3)).getUri();
// Once to check if the property is set, and once to retrieve the
value
Mockito.verify(properties,
Mockito.times(2)).getAuthPropertiesFilename();
@@ -74,7 +74,7 @@ public class TestStandardHashiCorpVaultCommunicationService {
}
@Test
- public void testTimeouts() throws Exception {
+ public void testTimeouts() {
when(properties.getConnectionTimeout()).thenReturn(Optional.of("20
secs"));
when(properties.getReadTimeout()).thenReturn(Optional.of("40 secs"));
try (StandardHashiCorpVaultCommunicationService ignored =
this.configureService()) {
diff --git
a/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultClientService.java
b/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultClientService.java
index 02290a3d368..6b9b1b74466 100644
---
a/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultClientService.java
+++
b/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service-api/src/main/java/org/apache/nifi/vault/hashicorp/HashiCorpVaultClientService.java
@@ -25,7 +25,7 @@ import org.apache.nifi.controller.VerifiableControllerService;
import org.apache.nifi.expression.ExpressionLanguageScope;
import org.apache.nifi.migration.PropertyConfiguration;
import org.apache.nifi.processor.util.StandardValidators;
-import org.apache.nifi.ssl.SSLContextService;
+import org.apache.nifi.ssl.SSLContextProvider;
/**
* Provides a HashiCorpVaultCommunicationService.
@@ -70,10 +70,9 @@ public interface HashiCorpVaultClientService extends
ControllerService, Verifiab
PropertyDescriptor SSL_CONTEXT_SERVICE = new PropertyDescriptor.Builder()
.name("vault.ssl.context.service")
.displayName("SSL Context Service")
- .description("The SSL Context Service used to provide client
certificate information for TLS/SSL connections to the " +
- "HashiCorp Vault server.")
+ .description("SSL Context Provider for TLS encrypted communication
with HashiCorp Vault Server")
.required(false)
- .identifiesControllerService(SSLContextService.class)
+ .identifiesControllerService(SSLContextProvider.class)
.dependsOn(CONFIGURATION_STRATEGY, DIRECT_PROPERTIES)
.build();
diff --git
a/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultClientService.java
b/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultClientService.java
index bdf2d3cdba2..073691fb1e1 100644
---
a/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultClientService.java
+++
b/nifi-extension-bundles/nifi-hashicorp-vault-bundle/nifi-hashicorp-vault-client-service/src/main/java/org/apache/nifi/vault/hashicorp/StandardHashiCorpVaultClientService.java
@@ -32,7 +32,7 @@ import org.apache.nifi.expression.ExpressionLanguageScope;
import org.apache.nifi.logging.ComponentLog;
import org.apache.nifi.processor.util.StandardValidators;
import org.apache.nifi.reporting.InitializationException;
-import org.apache.nifi.ssl.SSLContextService;
+import org.apache.nifi.ssl.SSLContextProvider;
import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultConfiguration;
import org.springframework.core.env.PropertySource;
@@ -40,6 +40,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
+import javax.net.ssl.SSLContext;
@Tags({"hashicorp", "vault", "client"})
@CapabilityDescription("A controller service for interacting with HashiCorp
Vault.")
@@ -47,9 +48,7 @@ import java.util.Map;
@DynamicProperties(
@DynamicProperty(name = "A Spring Vault configuration property name",
value = "The property value",
- description = "Allows any Spring Vault property keys to be
specified, as described in " +
-
"(https://docs.spring.io/spring-vault/docs/2.3.x/reference/html/#vault.core.environment-vault-configuration).
" +
- "See Additional Details for more information.",
+ description = "Allows any Spring Vault property keys to be
specified. See Additional Details for more information.",
expressionLanguageScope = ExpressionLanguageScope.ENVIRONMENT
)
)
@@ -171,7 +170,7 @@ public class StandardHashiCorpVaultClientService extends
AbstractControllerServi
static class DirectPropertySource extends
PropertySource<ConfigurationContext> {
- private static final String VAULT_SSL_KEY_PATTERN =
"vault.ssl.(key.*|trust.*|enabledProtocols)";
+ private static final String SSL_CONTEXT_PROPERTY =
SSLContext.class.getName();
public DirectPropertySource(final String name, final
ConfigurationContext source) {
super(name, source);
@@ -179,35 +178,13 @@ public class StandardHashiCorpVaultClientService extends
AbstractControllerServi
@Override
public Object getProperty(final String name) {
- if (name.matches(VAULT_SSL_KEY_PATTERN)) {
- return getSslProperty(name);
+ if (SSL_CONTEXT_PROPERTY.equals(name)) {
+ // Create SSLContext for property matching class
+ final SSLContextProvider sslContextProvider =
getSource().getProperty(SSL_CONTEXT_SERVICE).asControllerService(SSLContextProvider.class);
+ return sslContextProvider == null ? null :
sslContextProvider.createContext();
}
return getSource().getAllProperties().get(name);
}
-
- private String getSslProperty(final String name) {
- if (getSource().getProperty(SSL_CONTEXT_SERVICE).isSet()) {
- final SSLContextService sslContextService =
getSource().getProperty(SSL_CONTEXT_SERVICE).asControllerService(SSLContextService.class);
- switch (name) {
- case "vault.ssl.key-store":
- return sslContextService.getKeyStoreFile();
- case "vault.ssl.key-store-password":
- return sslContextService.getKeyStorePassword();
- case "vault.ssl.key-store-type":
- return sslContextService.getKeyStoreType();
- case "vault.ssl.trust-store":
- return sslContextService.getTrustStoreFile();
- case "vault.ssl.trust-store-password":
- return sslContextService.getTrustStorePassword();
- case "vault.ssl.trust-store-type":
- return sslContextService.getTrustStoreType();
- case "vault.ssl.enabledProtocols":
- return sslContextService.getSslAlgorithm();
- }
- }
-
- return null;
- }
}
}