mirror of https://github.com/apache/nifi.git
NIFI-13874 Refactored KeyStore and SSLContext Creation for Tests
This closes #9392 - Added EphemeralKeyStoreBuilder to nifi-security-ssl - Removed nifi-security-utils - Moved StandardTlsConfiguration to nifi-ssl-context-service - Refactored tests to use EphemeralKeyStoreBuilder and nifi-security-cert-builder for TLS Signed-off-by: Joseph Witt <joewitt@apache.org>
This commit is contained in:
parent
ea2cfb102f
commit
1b37d78403
|
@ -51,11 +51,6 @@ limitations under the License.
|
|||
<artifactId>nifi-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-ssl</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>c2-protocol-api</artifactId>
|
||||
|
@ -125,12 +120,6 @@ limitations under the License.
|
|||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
|
|
|
@ -1,121 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.apache.nifi.minifi.bootstrap.configuration.ingestors;
|
||||
|
||||
import static org.apache.nifi.minifi.bootstrap.configuration.ingestors.PullHttpChangeIngestor.PULL_HTTP_BASE_KEY;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.security.KeyStore;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import okhttp3.OkHttpClient;
|
||||
import org.apache.nifi.minifi.bootstrap.ConfigurationFileHolder;
|
||||
import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeListener;
|
||||
import org.apache.nifi.minifi.bootstrap.configuration.ConfigurationChangeNotifier;
|
||||
import org.apache.nifi.minifi.bootstrap.configuration.ListenerHandleResult;
|
||||
import org.apache.nifi.minifi.properties.BootstrapProperties;
|
||||
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardTrustManagerBuilder;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
public class RestChangeIngestorSSLTest extends RestChangeIngestorCommonTest {
|
||||
|
||||
@BeforeAll
|
||||
public static void setUpHttps() throws IOException, InterruptedException {
|
||||
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().trustStoreType("JKS").build();
|
||||
|
||||
Map<String, String> bootstrapProperties = new HashMap<>();
|
||||
bootstrapProperties.put(RestChangeIngestor.TRUSTSTORE_LOCATION_KEY, tlsConfiguration.getTruststorePath());
|
||||
bootstrapProperties.put(RestChangeIngestor.TRUSTSTORE_PASSWORD_KEY, tlsConfiguration.getTruststorePassword());
|
||||
bootstrapProperties.put(RestChangeIngestor.TRUSTSTORE_TYPE_KEY, tlsConfiguration.getTruststoreType().getType());
|
||||
bootstrapProperties.put(RestChangeIngestor.KEYSTORE_LOCATION_KEY, tlsConfiguration.getKeystorePath());
|
||||
bootstrapProperties.put(RestChangeIngestor.KEYSTORE_PASSWORD_KEY, tlsConfiguration.getKeystorePassword());
|
||||
bootstrapProperties.put(RestChangeIngestor.KEYSTORE_TYPE_KEY, tlsConfiguration.getKeystoreType().getType());
|
||||
bootstrapProperties.put(RestChangeIngestor.NEED_CLIENT_AUTH_KEY, "false");
|
||||
bootstrapProperties.put(PullHttpChangeIngestor.OVERRIDE_SECURITY, "true");
|
||||
bootstrapProperties.put(PULL_HTTP_BASE_KEY + ".override.core", "true");
|
||||
BootstrapProperties properties = new BootstrapProperties(bootstrapProperties);
|
||||
|
||||
restChangeIngestor = new RestChangeIngestor();
|
||||
|
||||
testNotifier = Mockito.mock(ConfigurationChangeNotifier.class);
|
||||
ConfigurationChangeListener testListener = Mockito.mock(ConfigurationChangeListener.class);
|
||||
when(testListener.getDescriptor()).thenReturn("MockChangeListener");
|
||||
when(testNotifier.notifyListeners(Mockito.any())).thenReturn(Collections.singleton(new ListenerHandleResult(testListener)));
|
||||
|
||||
ConfigurationFileHolder configurationFileHolder = Mockito.mock(ConfigurationFileHolder.class);
|
||||
when(configurationFileHolder.getConfigFileReference()).thenReturn(new AtomicReference<>(ByteBuffer.wrap(new byte[0])));
|
||||
|
||||
restChangeIngestor.initialize(properties, configurationFileHolder, testNotifier);
|
||||
restChangeIngestor.setDifferentiator(mockDifferentiator);
|
||||
restChangeIngestor.start();
|
||||
|
||||
OkHttpClient.Builder clientBuilder = new OkHttpClient.Builder();
|
||||
|
||||
final KeyStore keyStore;
|
||||
try (final FileInputStream keyStoreStream = new FileInputStream(tlsConfiguration.getKeystorePath())) {
|
||||
keyStore = new StandardKeyStoreBuilder()
|
||||
.type(tlsConfiguration.getKeystoreType().getType())
|
||||
.inputStream(keyStoreStream)
|
||||
.password(tlsConfiguration.getKeystorePassword().toCharArray())
|
||||
.build();
|
||||
}
|
||||
|
||||
final KeyStore truststore;
|
||||
try (final FileInputStream trustStoreStream = new FileInputStream(tlsConfiguration.getTruststorePath())) {
|
||||
truststore = new StandardKeyStoreBuilder()
|
||||
.type(tlsConfiguration.getTruststoreType().getType())
|
||||
.inputStream(trustStoreStream)
|
||||
.password(tlsConfiguration.getTruststorePassword().toCharArray())
|
||||
.build();
|
||||
}
|
||||
|
||||
final X509TrustManager trustManager = new StandardTrustManagerBuilder().trustStore(truststore).build();
|
||||
final SSLContext sslContext = new StandardSslContextBuilder()
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(tlsConfiguration.getKeyPassword().toCharArray())
|
||||
.trustStore(truststore)
|
||||
.build();
|
||||
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||
|
||||
clientBuilder.sslSocketFactory(sslSocketFactory, trustManager);
|
||||
|
||||
Thread.sleep(1000);
|
||||
url = restChangeIngestor.getURI().toURL().toString();
|
||||
client = clientBuilder.build();
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
public static void stop() throws Exception {
|
||||
restChangeIngestor.close();
|
||||
client = null;
|
||||
}
|
||||
}
|
|
@ -59,11 +59,6 @@ limitations under the License.
|
|||
<artifactId>nifi-framework-nar-loading-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils-api</artifactId>
|
||||
|
|
|
@ -352,11 +352,6 @@
|
|||
<artifactId>nifi-security-ssl</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils-api</artifactId>
|
||||
|
|
|
@ -65,12 +65,6 @@
|
|||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
|
|
|
@ -16,10 +16,7 @@
|
|||
*/
|
||||
package org.apache.nifi.vault.hashicorp;
|
||||
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultProperties;
|
||||
import org.apache.nifi.vault.hashicorp.config.HashiCorpVaultSslProperties;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -28,18 +25,14 @@ import org.mockito.Mockito;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
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";
|
||||
|
||||
private HashiCorpVaultProperties properties;
|
||||
private HashiCorpVaultSslProperties sslProperties;
|
||||
private File authProps;
|
||||
|
||||
@BeforeEach
|
||||
|
@ -47,11 +40,9 @@ public class TestStandardHashiCorpVaultCommunicationService {
|
|||
authProps = TestHashiCorpVaultConfiguration.writeBasicVaultAuthProperties();
|
||||
|
||||
properties = Mockito.mock(HashiCorpVaultProperties.class);
|
||||
sslProperties = Mockito.mock(HashiCorpVaultSslProperties.class);
|
||||
|
||||
when(properties.getUri()).thenReturn(URI_VALUE);
|
||||
when(properties.getAuthPropertiesFilename()).thenReturn(authProps.getAbsolutePath());
|
||||
when(properties.getSsl()).thenReturn(sslProperties);
|
||||
when(properties.getKvVersion()).thenReturn(1);
|
||||
}
|
||||
|
||||
|
@ -73,18 +64,6 @@ public class TestStandardHashiCorpVaultCommunicationService {
|
|||
|
||||
// Once to check if the property is set, and once to retrieve the value
|
||||
Mockito.verify(properties, Mockito.times(2)).getAuthPropertiesFilename();
|
||||
|
||||
// These should not be called because TLS is not configured
|
||||
this.ensureTlsPropertiesAccessed(0);
|
||||
}
|
||||
|
||||
private void ensureTlsPropertiesAccessed(int numberOfTimes) {
|
||||
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getKeyStore();
|
||||
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getKeyStoreType();
|
||||
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getKeyStorePassword();
|
||||
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getTrustStore();
|
||||
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getTrustStoreType();
|
||||
Mockito.verify(sslProperties, Mockito.times(numberOfTimes)).getTrustStorePassword();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -93,26 +72,4 @@ public class TestStandardHashiCorpVaultCommunicationService {
|
|||
when(properties.getReadTimeout()).thenReturn(Optional.of("40 secs"));
|
||||
this.configureService();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTLS() {
|
||||
TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
|
||||
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(",")));
|
||||
when(sslProperties.getEnabledCipherSuites()).thenReturn(CIPHER_SUITE_VALUE);
|
||||
|
||||
when(properties.getUri()).thenReturn(URI_VALUE.replace("http", "https"));
|
||||
this.configureService();
|
||||
|
||||
this.ensureTlsPropertiesAccessed(2);
|
||||
Mockito.verify(sslProperties, Mockito.times(1)).getEnabledProtocols();
|
||||
Mockito.verify(sslProperties, Mockito.times(1)).getEnabledCipherSuites();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.security.ssl;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.ListIterator;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Ephemeral implementation of KeyStore Builder that creates a KeyStore in memory without persistent entries
|
||||
*/
|
||||
public class EphemeralKeyStoreBuilder implements KeyStoreBuilder {
|
||||
|
||||
private static final String CERTIFICATE_ALIAS = "certificate-%d";
|
||||
|
||||
private static final String PRIVATE_KEY_ALIAS = "private-key-%d";
|
||||
|
||||
private final List<X509Certificate> certificates = new ArrayList<>();
|
||||
|
||||
private final List<KeyStore.PrivateKeyEntry> privateKeyEntries = new ArrayList<>();
|
||||
|
||||
private char[] keyPassword = null;
|
||||
|
||||
/**
|
||||
* Add X.509 Certificate to list of trusted certificates
|
||||
*
|
||||
* @param certificate Certificate to be added as a trusted entry
|
||||
* @return Builder
|
||||
*/
|
||||
public EphemeralKeyStoreBuilder addCertificate(final X509Certificate certificate) {
|
||||
Objects.requireNonNull(certificate, "Certificate required");
|
||||
certificates.add(certificate);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add Private Key Entry containing one or more associated X.509 Certificates
|
||||
*
|
||||
* @param privateKeyEntry Private Key Entry to be added
|
||||
* @return Builder
|
||||
*/
|
||||
public EphemeralKeyStoreBuilder addPrivateKeyEntry(final KeyStore.PrivateKeyEntry privateKeyEntry) {
|
||||
Objects.requireNonNull(privateKeyEntry, "Private Key Entry required");
|
||||
privateKeyEntries.add(privateKeyEntry);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Key Password for Private Key Entries
|
||||
*
|
||||
* @param keyPassword Key password array of characters
|
||||
* @return Builder
|
||||
*/
|
||||
public EphemeralKeyStoreBuilder keyPassword(final char[] keyPassword) {
|
||||
this.keyPassword = Objects.requireNonNull(keyPassword, "Key Password required").clone();
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Key Store with provided Certificates and Private Key Entries
|
||||
*
|
||||
* @return Key Store
|
||||
*/
|
||||
@Override
|
||||
public KeyStore build() {
|
||||
final KeyStore keyStore = getInitializedKeyStore();
|
||||
|
||||
final ListIterator<X509Certificate> certificateEntries = certificates.listIterator();
|
||||
while (certificateEntries.hasNext()) {
|
||||
final String alias = CERTIFICATE_ALIAS.formatted(certificateEntries.nextIndex());
|
||||
final X509Certificate certificate = certificateEntries.next();
|
||||
|
||||
try {
|
||||
keyStore.setCertificateEntry(alias, certificate);
|
||||
} catch (final KeyStoreException e) {
|
||||
final String message = String.format("Set certificate entry [%s] failed", alias);
|
||||
throw new BuilderConfigurationException(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
final ListIterator<KeyStore.PrivateKeyEntry> privateKeys = privateKeyEntries.listIterator();
|
||||
while (privateKeys.hasNext()) {
|
||||
final String alias = PRIVATE_KEY_ALIAS.formatted(privateKeys.nextIndex());
|
||||
final KeyStore.PrivateKeyEntry privateKeyEntry = privateKeys.next();
|
||||
|
||||
final PrivateKey privateKey = privateKeyEntry.getPrivateKey();
|
||||
final Certificate[] certificateChain = privateKeyEntry.getCertificateChain();
|
||||
|
||||
try {
|
||||
keyStore.setKeyEntry(alias, privateKey, keyPassword, certificateChain);
|
||||
} catch (final KeyStoreException e) {
|
||||
final String message = String.format("Set key entry [%s] failed", alias);
|
||||
throw new BuilderConfigurationException(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
private KeyStore getInitializedKeyStore() {
|
||||
final String keyStoreType = KeyStore.getDefaultType();
|
||||
try {
|
||||
final KeyStore keyStore = KeyStore.getInstance(keyStoreType);
|
||||
keyStore.load(null);
|
||||
return keyStore;
|
||||
} catch (final Exception e) {
|
||||
final String message = String.format("Key Store Type [%s] initialization failed", keyStoreType);
|
||||
throw new BuilderConfigurationException(message, e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.security.ssl;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import java.security.Key;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Enumeration;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class EphemeralKeyStoreBuilderTest {
|
||||
|
||||
private static final String KEY_ALGORITHM = "RSA";
|
||||
|
||||
private static final char[] KEY_PASSWORD = EphemeralKeyStoreBuilderTest.class.getSimpleName().toCharArray();;
|
||||
|
||||
private static KeyPair keyPair;
|
||||
|
||||
@Mock
|
||||
private X509Certificate certificate;
|
||||
|
||||
@BeforeAll
|
||||
static void setKeyPair() throws NoSuchAlgorithmException {
|
||||
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_ALGORITHM);
|
||||
keyPair = keyPairGenerator.generateKeyPair();
|
||||
}
|
||||
|
||||
@Test
|
||||
void testBuild() {
|
||||
final EphemeralKeyStoreBuilder builder = new EphemeralKeyStoreBuilder();
|
||||
|
||||
final KeyStore keyStore = builder.build();
|
||||
|
||||
assertNotNull(keyStore);
|
||||
|
||||
final String expectedKeyStoreType = KeyStore.getDefaultType();
|
||||
assertEquals(expectedKeyStoreType, keyStore.getType());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddCertificateBuild() throws KeyStoreException {
|
||||
final EphemeralKeyStoreBuilder builder = new EphemeralKeyStoreBuilder();
|
||||
|
||||
final KeyStore keyStore = builder.addCertificate(certificate).build();
|
||||
|
||||
assertNotNull(keyStore);
|
||||
|
||||
final String expectedKeyStoreType = KeyStore.getDefaultType();
|
||||
assertEquals(expectedKeyStoreType, keyStore.getType());
|
||||
|
||||
final Enumeration<String> aliases = keyStore.aliases();
|
||||
assertTrue(aliases.hasMoreElements());
|
||||
final String alias = aliases.nextElement();
|
||||
assertNotNull(alias);
|
||||
assertFalse(aliases.hasMoreElements());
|
||||
}
|
||||
|
||||
@Test
|
||||
void testAddPrivateKeyEntryBuild() throws Exception {
|
||||
final EphemeralKeyStoreBuilder builder = new EphemeralKeyStoreBuilder();
|
||||
|
||||
final PrivateKey privateKey = keyPair.getPrivate();
|
||||
final PublicKey publicKey = keyPair.getPublic();
|
||||
when(certificate.getPublicKey()).thenReturn(publicKey);
|
||||
|
||||
final KeyStore.PrivateKeyEntry privateKeyEntry = new KeyStore.PrivateKeyEntry(privateKey, new Certificate[]{certificate});
|
||||
|
||||
final KeyStore keyStore = builder.addPrivateKeyEntry(privateKeyEntry).keyPassword(KEY_PASSWORD).build();
|
||||
|
||||
assertNotNull(keyStore);
|
||||
|
||||
final String expectedKeyStoreType = KeyStore.getDefaultType();
|
||||
assertEquals(expectedKeyStoreType, keyStore.getType());
|
||||
|
||||
final Enumeration<String> aliases = keyStore.aliases();
|
||||
assertTrue(aliases.hasMoreElements());
|
||||
final String alias = aliases.nextElement();
|
||||
assertNotNull(alias);
|
||||
|
||||
final Key key = keyStore.getKey(alias, KEY_PASSWORD);
|
||||
assertEquals(privateKey, key);
|
||||
|
||||
assertFalse(aliases.hasMoreElements());
|
||||
}
|
||||
}
|
|
@ -1,230 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.security.util;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import javax.net.ssl.KeyManager;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import org.apache.nifi.security.ssl.StandardKeyManagerFactoryBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardTrustManagerFactoryBuilder;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
/**
|
||||
* A factory for creating SSL contexts using the application's security properties. By requiring callers to bundle
|
||||
* the properties in a {@link TlsConfiguration} container object, much better validation and property matching can
|
||||
* occur. The {@code public} methods are designed for easy use, while the {@code protected} methods provide more
|
||||
* granular (but less common) access to intermediate objects if required.
|
||||
*/
|
||||
public final class SslContextFactory {
|
||||
private static final Logger logger = LoggerFactory.getLogger(SslContextFactory.class);
|
||||
|
||||
/**
|
||||
* Create and initialize a {@link SSLContext} from the provided TLS configuration.
|
||||
*
|
||||
* @param tlsConfiguration the TLS configuration container object
|
||||
* @return {@link SSLContext} initialized from TLS Configuration or null when TLS Configuration is empty
|
||||
* @throws TlsException if there is a problem configuring the SSLContext
|
||||
*/
|
||||
public static SSLContext createSslContext(final TlsConfiguration tlsConfiguration) throws TlsException {
|
||||
if (TlsConfiguration.isEmpty(tlsConfiguration)) {
|
||||
logger.debug("Cannot create SSLContext from empty TLS configuration; returning null");
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the keystore properties are present, truststore properties are required to be present as well
|
||||
if (tlsConfiguration.isKeystorePopulated() && !tlsConfiguration.isTruststorePopulated()) {
|
||||
logger.error("The TLS config keystore properties were populated but the truststore properties were not");
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Provided TLS config: {}", tlsConfiguration);
|
||||
}
|
||||
throw new TlsException("Truststore properties are required if keystore properties are present");
|
||||
}
|
||||
final TrustManager[] trustManagers = getTrustManagers(tlsConfiguration);
|
||||
|
||||
return createSslContext(tlsConfiguration, trustManagers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create and initialize a {@link SSLContext} from the provided TLS configuration and Trust Managers.
|
||||
*
|
||||
* @param tlsConfiguration the TLS configuration container object
|
||||
* @param trustManagers Trust Managers can be null to use platform default Trust Managers
|
||||
* @return {@link SSLContext} initialized from TLS Configuration or null when TLS Configuration is empty
|
||||
* @throws TlsException if there is a problem configuring the SSLContext
|
||||
*/
|
||||
public static SSLContext createSslContext(final TlsConfiguration tlsConfiguration, final TrustManager[] trustManagers) throws TlsException {
|
||||
if (TlsConfiguration.isEmpty(tlsConfiguration)) {
|
||||
logger.debug("Cannot create SSLContext from empty TLS configuration; returning null");
|
||||
return null;
|
||||
}
|
||||
|
||||
final KeyManager[] keyManagers = getKeyManagers(tlsConfiguration);
|
||||
return initializeSSLContext(tlsConfiguration, keyManagers, trustManagers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a configured {@link X509TrustManager} for the provided configuration. Useful for
|
||||
* constructing HTTP clients which require their own trust management rather than an
|
||||
* {@link SSLContext}. Filters and removes any trust managers that are not
|
||||
* {@link javax.net.ssl.X509TrustManager} implementations, and returns the <em>first</em>
|
||||
* X.509 trust manager.
|
||||
*
|
||||
* @param tlsConfiguration the TLS configuration container object
|
||||
* @return an X.509 TrustManager (can be {@code null})
|
||||
* @throws TlsException if there is a problem reading the truststore to create the trust managers
|
||||
*/
|
||||
public static X509TrustManager getX509TrustManager(TlsConfiguration tlsConfiguration) throws TlsException {
|
||||
TrustManager[] trustManagers = getTrustManagers(tlsConfiguration);
|
||||
if (trustManagers == null) {
|
||||
return null;
|
||||
}
|
||||
Optional<X509TrustManager> x509TrustManager = Arrays.stream(trustManagers)
|
||||
.filter(tm -> tm instanceof X509TrustManager)
|
||||
.map(tm -> (X509TrustManager) tm)
|
||||
.findFirst();
|
||||
return x509TrustManager.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convenience method to return the {@link SSLSocketFactory} from the created {@link SSLContext}
|
||||
*
|
||||
* @param tlsConfiguration the TLS configuration container object
|
||||
* @return the configured SSLSocketFactory (can be {@code null})
|
||||
* @throws TlsException if there is a problem creating the SSLContext or SSLSocketFactory
|
||||
*/
|
||||
public static SSLSocketFactory createSSLSocketFactory(final TlsConfiguration tlsConfiguration) throws TlsException {
|
||||
SSLContext sslContext = createSslContext(tlsConfiguration);
|
||||
if (sslContext == null) {
|
||||
// Only display an error in the log if the provided config wasn't empty
|
||||
if (!TlsConfiguration.isEmpty(tlsConfiguration)) {
|
||||
logger.error("The SSLContext could not be formed from the provided TLS configuration. Check the provided keystore and truststore properties");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return sslContext.getSocketFactory();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of {@link KeyManager}s for the provided configuration. Useful for constructing
|
||||
* HTTP clients which require their own key management rather than an {@link SSLContext}. The result can be
|
||||
* {@code null} or empty. If an empty configuration is provided, {@code null} is returned. However, if a partially-populated
|
||||
* but invalid configuration is provided, a {@link TlsException} is thrown.
|
||||
*
|
||||
* @param tlsConfiguration the TLS configuration container object with keystore properties
|
||||
* @return an array of KeyManagers (can be {@code null})
|
||||
* @throws TlsException if there is a problem reading the keystore to create the key managers
|
||||
*/
|
||||
protected static KeyManager[] getKeyManagers(final TlsConfiguration tlsConfiguration) throws TlsException {
|
||||
KeyManager[] keyManagers = null;
|
||||
|
||||
final String keystorePath = tlsConfiguration.getKeystorePath();
|
||||
final KeystoreType keystoreType = tlsConfiguration.getKeystoreType();
|
||||
|
||||
if (tlsConfiguration.isKeystorePopulated()) {
|
||||
try (InputStream inputStream = new FileInputStream(keystorePath)) {
|
||||
final KeyStore keyStore = new StandardKeyStoreBuilder()
|
||||
.inputStream(inputStream)
|
||||
.type(keystoreType.getType())
|
||||
.password(tlsConfiguration.getKeystorePassword().toCharArray())
|
||||
.build();
|
||||
|
||||
keyManagers = new StandardKeyManagerFactoryBuilder()
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(tlsConfiguration.getFunctionalKeyPassword().toCharArray())
|
||||
.build()
|
||||
.getKeyManagers();
|
||||
} catch (final IOException e) {
|
||||
final String message = "Key Store [%s] Type [%s] loading failed".formatted(keystorePath, keystoreType);
|
||||
throw new TlsException(message, e);
|
||||
}
|
||||
} else if (tlsConfiguration.isAnyKeystorePopulated()) {
|
||||
throw new TlsException("Key Store properties missing: Set Path [%s] Type [%s]".formatted(keystorePath, keystoreType));
|
||||
}
|
||||
|
||||
return keyManagers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of {@link TrustManager} implementations based on the provided truststore configurations. The result can be
|
||||
* {@code null} or empty. If an empty configuration is provided, {@code null} is returned. However, if a partially-populated
|
||||
* but invalid configuration is provided, a {@link TlsException} is thrown.
|
||||
* <p>
|
||||
*
|
||||
* @param tlsConfiguration the TLS configuration container object with truststore properties
|
||||
* @return the loaded trust managers
|
||||
* @throws TlsException if there is a problem reading from the truststore
|
||||
*/
|
||||
public static TrustManager[] getTrustManagers(final TlsConfiguration tlsConfiguration) throws TlsException {
|
||||
Objects.requireNonNull(tlsConfiguration, "TLS Configuration required");
|
||||
|
||||
final String truststorePath = tlsConfiguration.getTruststorePath();
|
||||
final KeystoreType truststoreType = tlsConfiguration.getTruststoreType();
|
||||
|
||||
TrustManager[] trustManagers = null;
|
||||
if (tlsConfiguration.isTruststorePopulated()) {
|
||||
try (InputStream inputStream = new FileInputStream(truststorePath)) {
|
||||
final StandardKeyStoreBuilder keyStoreBuilder = new StandardKeyStoreBuilder()
|
||||
.inputStream(inputStream)
|
||||
.type(truststoreType.getType());
|
||||
|
||||
final String truststorePassword = tlsConfiguration.getTruststorePassword();
|
||||
if (truststorePassword != null) {
|
||||
keyStoreBuilder.password(truststorePassword.toCharArray());
|
||||
}
|
||||
final KeyStore trustStore = keyStoreBuilder.build();
|
||||
|
||||
trustManagers = new StandardTrustManagerFactoryBuilder()
|
||||
.trustStore(trustStore)
|
||||
.build()
|
||||
.getTrustManagers();
|
||||
} catch (final IOException e) {
|
||||
final String message = "Trust Store [%s] Type [%s] loading failed".formatted(truststorePath, truststoreType);
|
||||
throw new TlsException(message, e);
|
||||
}
|
||||
} else if (tlsConfiguration.isAnyTruststorePopulated()) {
|
||||
throw new TlsException("Trust Store properties missing: Set Path [%s] Type [%s]".formatted(truststorePath, truststoreType));
|
||||
}
|
||||
|
||||
return trustManagers;
|
||||
}
|
||||
|
||||
private static SSLContext initializeSSLContext(final TlsConfiguration tlsConfiguration, final KeyManager[] keyManagers, final TrustManager[] trustManagers) throws TlsException {
|
||||
try {
|
||||
final SSLContext sslContext = SSLContext.getInstance(tlsConfiguration.getProtocol());
|
||||
sslContext.init(keyManagers, trustManagers, new SecureRandom());
|
||||
return sslContext;
|
||||
} catch (final NoSuchAlgorithmException | KeyManagementException e) {
|
||||
logger.error("Encountered an error creating SSLContext from TLS configuration ({}): {}", tlsConfiguration, e.getLocalizedMessage());
|
||||
throw new TlsException("Error creating SSL context", e);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,210 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.security.util;
|
||||
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* KeyStore Factory for creating temporary files primarily used for testing
|
||||
*/
|
||||
public class TemporaryKeyStoreBuilder {
|
||||
private static final String KEY_PAIR_ALGORITHM = "RSA";
|
||||
|
||||
private static final int KEY_SIZE = 2048;
|
||||
|
||||
private static final int RANDOM_BYTES_LENGTH = 16;
|
||||
|
||||
private static final Base64.Encoder ENCODER = Base64.getEncoder().withoutPadding();
|
||||
|
||||
private static final String DISTINGUISHED_NAME_FORMAT = "CN=%s";
|
||||
|
||||
private static final int CERTIFICATE_VALID_DAYS = 1;
|
||||
|
||||
private static final KeystoreType KEYSTORE_TYPE = KeystoreType.PKCS12;
|
||||
|
||||
private static final String KEY_STORE_EXTENSION = ".p12";
|
||||
|
||||
private static final String KEY_STORE_PREFIX = "TemporaryKeyStore-";
|
||||
|
||||
private static final String DEFAULT_HOSTNAME = "localhost";
|
||||
|
||||
private String hostname = DEFAULT_HOSTNAME;
|
||||
|
||||
private String trustStorePassword = generateSecureRandomPassword();
|
||||
|
||||
private String trustStoreType = KEYSTORE_TYPE.getType();
|
||||
|
||||
/**
|
||||
* Set Hostname used for Certificate Common Name and DNS Subject Alternative Names
|
||||
*
|
||||
* @param hostname Hostname is required
|
||||
* @return Builder
|
||||
*/
|
||||
public TemporaryKeyStoreBuilder hostname(final String hostname) {
|
||||
this.hostname = Objects.requireNonNull(hostname, "Hostname required");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Trust Store Password used for protected generated Trust Store file
|
||||
*
|
||||
* @param trustStorePassword Trust Store Password is required
|
||||
* @return Builder
|
||||
*/
|
||||
public TemporaryKeyStoreBuilder trustStorePassword(final String trustStorePassword) {
|
||||
this.trustStorePassword = Objects.requireNonNull(trustStorePassword, "TrustStore Password required");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Trust Store Type used for storing Trust Store files
|
||||
*
|
||||
* @param trustStoreType Trust Store type must be a supported value for KeyStore.getInstance()
|
||||
* @return Builder
|
||||
*/
|
||||
public TemporaryKeyStoreBuilder trustStoreType(final String trustStoreType) {
|
||||
this.trustStoreType = Objects.requireNonNull(trustStoreType, "TrustStore Type required");
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build Temporary KeyStore and TrustStore with configured values and set files with File.deleteOnExit()
|
||||
*
|
||||
* @return TLS Configuration with KeyStore and TrustStore properties
|
||||
*/
|
||||
public TlsConfiguration build() {
|
||||
final KeyPair keyPair = generateKeyPair();
|
||||
final X509Certificate certificate = generateCertificate(hostname, keyPair);
|
||||
|
||||
final KeyStoreConfiguration keyStoreConfiguration = setKeyStore(keyPair.getPrivate(), certificate);
|
||||
final KeyStoreConfiguration trustStoreConfiguration = setTrustStore(certificate);
|
||||
|
||||
return new StandardTlsConfiguration(
|
||||
keyStoreConfiguration.location(),
|
||||
keyStoreConfiguration.password(),
|
||||
keyStoreConfiguration.password(),
|
||||
keyStoreConfiguration.keyStoreType(),
|
||||
trustStoreConfiguration.location(),
|
||||
trustStoreConfiguration.password(),
|
||||
trustStoreConfiguration.keyStoreType(),
|
||||
TlsPlatform.getLatestProtocol()
|
||||
);
|
||||
}
|
||||
|
||||
private KeyStoreConfiguration setKeyStore(final PrivateKey privateKey, final X509Certificate certificate) {
|
||||
final KeyStore keyStore = getNewKeyStore(KEYSTORE_TYPE.getType());
|
||||
|
||||
final String password = generateSecureRandomPassword();
|
||||
final String alias = UUID.randomUUID().toString();
|
||||
try {
|
||||
keyStore.setKeyEntry(alias, privateKey, password.toCharArray(), new Certificate[]{certificate});
|
||||
} catch (final KeyStoreException e) {
|
||||
throw new RuntimeException("Set Key Entry Failed", e);
|
||||
}
|
||||
|
||||
final File keyStoreFile = storeKeyStore(keyStore, password.toCharArray());
|
||||
return new KeyStoreConfiguration(keyStoreFile.getAbsolutePath(), password, KEYSTORE_TYPE.getType());
|
||||
}
|
||||
|
||||
private KeyStoreConfiguration setTrustStore(final X509Certificate certificate) {
|
||||
final KeyStore keyStore = getNewKeyStore(trustStoreType);
|
||||
|
||||
final String alias = UUID.randomUUID().toString();
|
||||
try {
|
||||
keyStore.setCertificateEntry(alias, certificate);
|
||||
} catch (final KeyStoreException e) {
|
||||
throw new RuntimeException("Set Certificate Entry Failed", e);
|
||||
}
|
||||
|
||||
final File trustStoreFile = storeKeyStore(keyStore, trustStorePassword.toCharArray());
|
||||
return new KeyStoreConfiguration(trustStoreFile.getAbsolutePath(), trustStorePassword, trustStoreType);
|
||||
}
|
||||
|
||||
private File storeKeyStore(final KeyStore keyStore, final char[] password) {
|
||||
try {
|
||||
final File keyStoreFile = File.createTempFile(KEY_STORE_PREFIX, KEY_STORE_EXTENSION);
|
||||
keyStoreFile.deleteOnExit();
|
||||
try (final OutputStream outputStream = new FileOutputStream(keyStoreFile)) {
|
||||
keyStore.store(outputStream, password);
|
||||
}
|
||||
return keyStoreFile;
|
||||
} catch (final IOException | KeyStoreException | NoSuchAlgorithmException | CertificateException e) {
|
||||
throw new RuntimeException("Store KeyStore Failed", e);
|
||||
}
|
||||
}
|
||||
|
||||
private KeyStore getNewKeyStore(final String newKeyStoreType) {
|
||||
try {
|
||||
final KeyStore keyStore = KeyStore.getInstance(newKeyStoreType);
|
||||
keyStore.load(null);
|
||||
return keyStore;
|
||||
} catch (final KeyStoreException | IOException | NoSuchAlgorithmException | CertificateException e) {
|
||||
throw new RuntimeException(String.format("Create KeyStore [%s] Failed", KEYSTORE_TYPE), e);
|
||||
}
|
||||
}
|
||||
|
||||
private X509Certificate generateCertificate(final String hostname, final KeyPair keyPair) {
|
||||
final X500Principal distinguishedName = new X500Principal(String.format(DISTINGUISHED_NAME_FORMAT, hostname));
|
||||
final List<String> dnsNames = Collections.singletonList(hostname);
|
||||
return new StandardCertificateBuilder(keyPair, distinguishedName, Duration.ofDays(CERTIFICATE_VALID_DAYS))
|
||||
.setDnsSubjectAlternativeNames(dnsNames)
|
||||
.build();
|
||||
}
|
||||
|
||||
private KeyPair generateKeyPair() {
|
||||
try {
|
||||
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(KEY_PAIR_ALGORITHM);
|
||||
keyPairGenerator.initialize(KEY_SIZE);
|
||||
return keyPairGenerator.generateKeyPair();
|
||||
} catch (final NoSuchAlgorithmException e) {
|
||||
throw new IllegalArgumentException(String.format("[%s] Algorithm not found", KEY_PAIR_ALGORITHM), e);
|
||||
}
|
||||
}
|
||||
|
||||
private String generateSecureRandomPassword() {
|
||||
final SecureRandom secureRandom = new SecureRandom();
|
||||
final byte[] randomBytes = new byte[RANDOM_BYTES_LENGTH];
|
||||
secureRandom.nextBytes(randomBytes);
|
||||
return ENCODER.encodeToString(randomBytes);
|
||||
}
|
||||
|
||||
private record KeyStoreConfiguration(String location, String password, String keyStoreType) {
|
||||
|
||||
}
|
||||
}
|
|
@ -1,106 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.security.util;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
public class SslSocketFactoryTest {
|
||||
private static final String EMPTY = "";
|
||||
|
||||
private static TlsConfiguration tlsConfiguration;
|
||||
|
||||
@BeforeAll
|
||||
public static void setTlsConfiguration() {
|
||||
tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSslContextNullTlsConfiguration() throws TlsException {
|
||||
final SSLContext sslContext = SslContextFactory.createSslContext(null);
|
||||
assertNull(sslContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSslContextEmptyTlsConfiguration() throws TlsException {
|
||||
final SSLContext sslContext = SslContextFactory.createSslContext(new StandardTlsConfiguration());
|
||||
assertNull(sslContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSslContextEmptyKeyPassword() throws TlsException {
|
||||
final TlsConfiguration customTlsConfiguration = new StandardTlsConfiguration(
|
||||
tlsConfiguration.getKeystorePath(),
|
||||
tlsConfiguration.getKeystorePassword(),
|
||||
EMPTY,
|
||||
tlsConfiguration.getKeystoreType(),
|
||||
tlsConfiguration.getTruststorePath(),
|
||||
tlsConfiguration.getTruststorePassword(),
|
||||
tlsConfiguration.getTruststoreType(),
|
||||
tlsConfiguration.getProtocol()
|
||||
);
|
||||
final SSLContext sslContext = SslContextFactory.createSslContext(customTlsConfiguration);
|
||||
assertNotNull(sslContext);
|
||||
assertEquals(customTlsConfiguration.getProtocol(), sslContext.getProtocol());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSslContextEmptyTrustStorePasswordJks() throws TlsException {
|
||||
final TlsConfiguration customTlsConfiguration = new TemporaryKeyStoreBuilder()
|
||||
.trustStorePassword(EMPTY)
|
||||
.trustStoreType(KeystoreType.JKS.getType())
|
||||
.build();
|
||||
final SSLContext sslContext = SslContextFactory.createSslContext(customTlsConfiguration);
|
||||
assertNotNull(sslContext);
|
||||
assertEquals(customTlsConfiguration.getProtocol(), sslContext.getProtocol());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateSslContext() throws TlsException {
|
||||
final SSLContext sslContext = SslContextFactory.createSslContext(tlsConfiguration);
|
||||
assertNotNull(sslContext);
|
||||
assertEquals(tlsConfiguration.getProtocol(), sslContext.getProtocol());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTrustManager() throws TlsException {
|
||||
final X509TrustManager trustManager = SslContextFactory.getX509TrustManager(tlsConfiguration);
|
||||
assertNotNull(trustManager);
|
||||
assertEquals(1, trustManager.getAcceptedIssuers().length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTrustManagers() throws TlsException {
|
||||
final TrustManager[] trustManagers = SslContextFactory.getTrustManagers(tlsConfiguration);
|
||||
assertNotNull(trustManagers);
|
||||
assertEquals(1, trustManagers.length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTrustManagersEmptyTlsConfiguration() throws TlsException {
|
||||
final TrustManager[] trustManagers = SslContextFactory.getTrustManagers(new StandardTlsConfiguration());
|
||||
assertNull(trustManagers);
|
||||
}
|
||||
}
|
|
@ -1,205 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.security.util;
|
||||
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class StandardTlsConfigurationTest {
|
||||
private static final String KEY_STORE_PATH = UUID.randomUUID().toString();
|
||||
|
||||
private static final String KEY_STORE_PASSWORD = UUID.randomUUID().toString();
|
||||
|
||||
private static final String KEY_PASSWORD = UUID.randomUUID().toString();
|
||||
|
||||
private static final String KEY_STORE_TYPE = KeystoreType.PKCS12.getType();
|
||||
|
||||
private static final String TRUST_STORE_PATH = UUID.randomUUID().toString();
|
||||
|
||||
private static final String TRUST_STORE_PASSWORD = UUID.randomUUID().toString();
|
||||
|
||||
private static final String TRUST_STORE_TYPE = KeystoreType.JKS.getType();
|
||||
|
||||
private static final String PROTOCOL = TlsPlatform.getLatestProtocol();
|
||||
|
||||
private static TlsConfiguration tlsConfiguration;
|
||||
|
||||
@BeforeAll
|
||||
public static void setTlsConfiguration() {
|
||||
tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsKeyStoreValid() {
|
||||
assertTrue(tlsConfiguration.isKeystoreValid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsTrustStoreValid() {
|
||||
assertTrue(tlsConfiguration.isTruststoreValid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualsNullProperties() {
|
||||
final StandardTlsConfiguration configuration = new StandardTlsConfiguration();
|
||||
assertEquals(new StandardTlsConfiguration(), configuration);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHashCodeNullProperties() {
|
||||
final StandardTlsConfiguration configuration = new StandardTlsConfiguration();
|
||||
assertEquals(new StandardTlsConfiguration().hashCode(), configuration.hashCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFunctionalKeyPasswordFromKeyStorePassword() {
|
||||
final TlsConfiguration configuration = new StandardTlsConfiguration(
|
||||
KEY_STORE_PATH,
|
||||
KEY_STORE_PASSWORD,
|
||||
null,
|
||||
KEY_STORE_TYPE,
|
||||
TRUST_STORE_PATH,
|
||||
TRUST_STORE_PASSWORD,
|
||||
TRUST_STORE_TYPE,
|
||||
PROTOCOL
|
||||
);
|
||||
assertEquals(KEY_STORE_PASSWORD, configuration.getFunctionalKeyPassword());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsKeyStorePopulated() {
|
||||
final TlsConfiguration configuration = new StandardTlsConfiguration(
|
||||
KEY_STORE_PATH,
|
||||
KEY_STORE_PASSWORD,
|
||||
KEY_PASSWORD,
|
||||
KEY_STORE_TYPE,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
PROTOCOL
|
||||
);
|
||||
assertTrue(configuration.isKeystorePopulated());
|
||||
assertTrue(configuration.isAnyKeystorePopulated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsKeyStorePopulatedMissingProperties() {
|
||||
final TlsConfiguration configuration = new StandardTlsConfiguration(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
TRUST_STORE_PATH,
|
||||
TRUST_STORE_PASSWORD,
|
||||
TRUST_STORE_TYPE,
|
||||
PROTOCOL
|
||||
);
|
||||
assertFalse(configuration.isKeystorePopulated());
|
||||
assertFalse(configuration.isAnyKeystorePopulated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsTrustStorePopulated() {
|
||||
final TlsConfiguration configuration = new StandardTlsConfiguration(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
TRUST_STORE_PATH,
|
||||
TRUST_STORE_PASSWORD,
|
||||
TRUST_STORE_TYPE,
|
||||
PROTOCOL
|
||||
);
|
||||
assertTrue(configuration.isTruststorePopulated());
|
||||
assertTrue(configuration.isAnyTruststorePopulated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIsTrustStorePopulatedMissingProperties() {
|
||||
final TlsConfiguration configuration = new StandardTlsConfiguration(
|
||||
KEY_STORE_PATH,
|
||||
KEY_STORE_PASSWORD,
|
||||
KEY_PASSWORD,
|
||||
KEY_STORE_TYPE,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
PROTOCOL
|
||||
);
|
||||
assertFalse(configuration.isTruststorePopulated());
|
||||
assertFalse(configuration.isAnyTruststorePopulated());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEnabledProtocolsVersionMatched() {
|
||||
final TlsConfiguration configuration = new StandardTlsConfiguration(
|
||||
KEY_STORE_PATH,
|
||||
KEY_STORE_PASSWORD,
|
||||
KEY_PASSWORD,
|
||||
KEY_STORE_TYPE,
|
||||
TRUST_STORE_PATH,
|
||||
TRUST_STORE_PASSWORD,
|
||||
TRUST_STORE_TYPE,
|
||||
PROTOCOL
|
||||
);
|
||||
|
||||
final String[] enabledProtocols = configuration.getEnabledProtocols();
|
||||
assertArrayEquals(new String[]{PROTOCOL}, enabledProtocols);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEnabledProtocolsTlsProtocol() {
|
||||
final TlsConfiguration configuration = new StandardTlsConfiguration(
|
||||
KEY_STORE_PATH,
|
||||
KEY_STORE_PASSWORD,
|
||||
KEY_PASSWORD,
|
||||
KEY_STORE_TYPE,
|
||||
TRUST_STORE_PATH,
|
||||
TRUST_STORE_PASSWORD,
|
||||
TRUST_STORE_TYPE,
|
||||
StandardTlsConfiguration.TLS_PROTOCOL
|
||||
);
|
||||
|
||||
final String[] enabledProtocols = configuration.getEnabledProtocols();
|
||||
assertArrayEquals(TlsPlatform.getPreferredProtocols().toArray(new String[0]), enabledProtocols);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetEnabledProtocolsSslProtocol() {
|
||||
final TlsConfiguration configuration = new StandardTlsConfiguration(
|
||||
KEY_STORE_PATH,
|
||||
KEY_STORE_PASSWORD,
|
||||
KEY_PASSWORD,
|
||||
KEY_STORE_TYPE,
|
||||
TRUST_STORE_PATH,
|
||||
TRUST_STORE_PASSWORD,
|
||||
TRUST_STORE_TYPE,
|
||||
StandardTlsConfiguration.SSL_PROTOCOL
|
||||
);
|
||||
|
||||
final String[] enabledProtocols = configuration.getEnabledProtocols();
|
||||
|
||||
assertArrayEquals(TlsPlatform.getSupportedProtocols().toArray(new String[0]), enabledProtocols);
|
||||
}
|
||||
}
|
|
@ -53,7 +53,6 @@
|
|||
<module>nifi-security-kerberos</module>
|
||||
<module>nifi-security-ssl</module>
|
||||
<module>nifi-security-utils-api</module>
|
||||
<module>nifi-security-utils</module>
|
||||
<module>nifi-single-user-utils</module>
|
||||
<module>nifi-site-to-site-client</module>
|
||||
<module>nifi-swagger-integration</module>
|
||||
|
|
|
@ -34,12 +34,6 @@ language governing permissions and limitations under the License. -->
|
|||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-ssl-context-service-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-ssl-context-service</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-utils</artifactId>
|
||||
|
@ -56,6 +50,11 @@ language governing permissions and limitations under the License. -->
|
|||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils-api</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -96,12 +96,6 @@
|
|||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.icegreen</groupId>
|
||||
<artifactId>greenmail</artifactId>
|
||||
|
|
|
@ -51,7 +51,13 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<artifactId>nifi-security-ssl</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-cert-builder</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
|
|
@ -26,10 +26,10 @@ import org.apache.nifi.event.transport.configuration.ShutdownTimeout;
|
|||
import org.apache.nifi.event.transport.configuration.TransportProtocol;
|
||||
import org.apache.nifi.event.transport.message.ByteArrayMessage;
|
||||
import org.apache.nifi.logging.ComponentLog;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.security.util.ClientAuth;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
@ -38,12 +38,18 @@ import org.mockito.Mock;
|
|||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.net.InetAddress;
|
||||
import java.net.SocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
@ -199,7 +205,15 @@ public class StringNettyEventSenderFactoryTest {
|
|||
}
|
||||
|
||||
private SSLContext getSslContext() throws GeneralSecurityException {
|
||||
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
return SslContextFactory.createSslContext(tlsConfiguration);
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.build();
|
||||
return new StandardSslContextBuilder()
|
||||
.trustStore(keyStore)
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(new char[]{})
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,12 +22,6 @@
|
|||
<artifactId>nifi-flow-registry-client-services</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils-api</artifactId>
|
||||
|
|
|
@ -36,6 +36,10 @@
|
|||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-ssl-context-service-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-kerberos-user-service-api</artifactId>
|
||||
|
@ -68,7 +72,13 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-ssl-context-service</artifactId>
|
||||
<artifactId>nifi-security-cert-builder</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-ssl</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
|
|
@ -35,10 +35,9 @@ import org.apache.nifi.kafka.service.api.producer.RecordSummary;
|
|||
import org.apache.nifi.kafka.service.api.record.ByteRecord;
|
||||
import org.apache.nifi.kafka.service.api.record.KafkaRecord;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.nifi.ssl.StandardRestrictedSSLContextService;
|
||||
import org.apache.nifi.util.MockConfigurationContext;
|
||||
import org.apache.nifi.util.NoOpProcessor;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
|
@ -50,11 +49,22 @@ import org.junit.jupiter.api.BeforeEach;
|
|||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.testcontainers.containers.KafkaContainer;
|
||||
import org.testcontainers.utility.DockerImageName;
|
||||
import org.testcontainers.utility.MountableFile;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
|
@ -74,6 +84,8 @@ import static org.junit.jupiter.api.Assertions.assertEquals;
|
|||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@TestInstance(Lifecycle.PER_CLASS)
|
||||
public class Kafka3ConnectionServiceBaseIT {
|
||||
|
@ -100,13 +112,25 @@ public class Kafka3ConnectionServiceBaseIT {
|
|||
|
||||
private static final int POLLING_ATTEMPTS = 3;
|
||||
|
||||
private static final Set<String> fileLocationNames = Set.of(
|
||||
"KAFKA_SSL_KEYSTORE_LOCATION", "KAFKA_SSL_TRUSTSTORE_LOCATION");
|
||||
private static final Set<String> fileLocationNames = Set.of("KAFKA_SSL_KEYSTORE_LOCATION", "KAFKA_SSL_TRUSTSTORE_LOCATION");
|
||||
|
||||
protected static final String TEST_USERNAME = "nifi";
|
||||
protected static final String TEST_PASSWORD = UUID.randomUUID().toString();
|
||||
|
||||
protected TlsConfiguration tlsConfiguration;
|
||||
private static final String KEY_STORE_EXTENSION = ".p12";
|
||||
|
||||
protected static final String KEY_PASSWORD = Kafka3ConnectionServiceBaseIT.class.getSimpleName();
|
||||
|
||||
protected static final String KEY_STORE_PASSWORD = KEY_PASSWORD;
|
||||
|
||||
@TempDir
|
||||
private static Path keyStoreDirectory;
|
||||
|
||||
protected static Path keyStorePath;
|
||||
|
||||
protected static String keyStoreType;
|
||||
|
||||
protected static Path trustStorePath;
|
||||
|
||||
protected TestRunner runner;
|
||||
|
||||
|
@ -115,8 +139,28 @@ public class Kafka3ConnectionServiceBaseIT {
|
|||
private Kafka3ConnectionService service;
|
||||
|
||||
@BeforeAll
|
||||
void startContainer() {
|
||||
tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
void startContainer() throws Exception {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.keyPassword(KEY_PASSWORD.toCharArray())
|
||||
.build();
|
||||
|
||||
keyStorePath = File.createTempFile("keyStore", KEY_STORE_EXTENSION, keyStoreDirectory.toFile()).toPath();
|
||||
try (OutputStream outputStream = Files.newOutputStream(keyStorePath)) {
|
||||
keyStore.store(outputStream, KEY_STORE_PASSWORD.toCharArray());
|
||||
}
|
||||
keyStoreType = keyStore.getType().toUpperCase();
|
||||
|
||||
final KeyStore trustStore = new EphemeralKeyStoreBuilder()
|
||||
.addCertificate(certificate)
|
||||
.build();
|
||||
trustStorePath = File.createTempFile("trustStore", KEY_STORE_EXTENSION, keyStoreDirectory.toFile()).toPath();
|
||||
try (OutputStream outputStream = Files.newOutputStream(trustStorePath)) {
|
||||
trustStore.store(outputStream, KEY_STORE_PASSWORD.toCharArray());
|
||||
}
|
||||
|
||||
kafkaContainer = new KafkaContainer(DockerImageName.parse(IMAGE_NAME));
|
||||
initializeContainer();
|
||||
kafkaContainer.start();
|
||||
|
@ -238,7 +282,7 @@ public class Kafka3ConnectionServiceBaseIT {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testVerifySuccessful() throws InitializationException {
|
||||
void testVerifySuccessful() {
|
||||
final Map<PropertyDescriptor, String> properties = new LinkedHashMap<>();
|
||||
properties.put(Kafka3ConnectionService.BOOTSTRAP_SERVERS, kafkaContainer.getBootstrapServers());
|
||||
final MockConfigurationContext configurationContext = new MockConfigurationContext(properties, null, null);
|
||||
|
@ -311,18 +355,18 @@ public class Kafka3ConnectionServiceBaseIT {
|
|||
|
||||
protected String addSSLContextService(final TestRunner runner) throws InitializationException {
|
||||
final String identifier = SSLContextService.class.getSimpleName();
|
||||
final SSLContextService service = new StandardRestrictedSSLContextService();
|
||||
final SSLContextService service = mock(SSLContextService.class);
|
||||
when(service.getIdentifier()).thenReturn(identifier);
|
||||
runner.addControllerService(identifier, service);
|
||||
|
||||
// TemporaryKeyStoreBuilder sets keystorePassword and keyPassword to the same value.
|
||||
// The SSL Context Service uses specified Keystore Password as the Key Password.
|
||||
//runner.setProperty(service, StandardRestrictedSSLContextService.KEY_PASSWORD, tlsConfiguration.getKeyPassword());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.KEYSTORE, tlsConfiguration.getKeystorePath());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.KEYSTORE_PASSWORD, tlsConfiguration.getKeystorePassword());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.TRUSTSTORE, tlsConfiguration.getTruststorePath());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.TRUSTSTORE_PASSWORD, tlsConfiguration.getTruststorePassword());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
|
||||
when(service.isKeyStoreConfigured()).thenReturn(true);
|
||||
when(service.getKeyStoreFile()).thenReturn(keyStorePath.toString());
|
||||
when(service.getKeyStoreType()).thenReturn(keyStoreType);
|
||||
when(service.getKeyStorePassword()).thenReturn(KEY_STORE_PASSWORD);
|
||||
when(service.isTrustStoreConfigured()).thenReturn(true);
|
||||
when(service.getTrustStoreFile()).thenReturn(trustStorePath.toString());
|
||||
when(service.getTrustStoreType()).thenReturn(keyStoreType);
|
||||
when(service.getTrustStorePassword()).thenReturn(KEY_STORE_PASSWORD);
|
||||
|
||||
runner.enableControllerService(service);
|
||||
return identifier;
|
||||
|
|
|
@ -29,13 +29,13 @@ public class Kafka3ConnectionServiceSSLIT extends Kafka3ConnectionServiceBaseIT
|
|||
protected Map<String, String> getKafkaContainerConfigProperties() {
|
||||
final Map<String, String> properties = new LinkedHashMap<>(super.getKafkaContainerConfigProperties());
|
||||
properties.put("KAFKA_LISTENER_SECURITY_PROTOCOL_MAP", "BROKER:SSL,PLAINTEXT:SSL");
|
||||
properties.put("KAFKA_SSL_KEYSTORE_LOCATION", tlsConfiguration.getKeystorePath());
|
||||
properties.put("KAFKA_SSL_KEYSTORE_TYPE", tlsConfiguration.getKeystoreType().getType());
|
||||
properties.put("KAFKA_SSL_KEYSTORE_PASSWORD", tlsConfiguration.getKeystorePassword());
|
||||
properties.put("KAFKA_SSL_KEY_PASSWORD", tlsConfiguration.getKeyPassword());
|
||||
properties.put("KAFKA_SSL_TRUSTSTORE_LOCATION", tlsConfiguration.getTruststorePath());
|
||||
properties.put("KAFKA_SSL_TRUSTSTORE_TYPE", tlsConfiguration.getTruststoreType().getType());
|
||||
properties.put("KAFKA_SSL_TRUSTSTORE_PASSWORD", tlsConfiguration.getTruststorePassword());
|
||||
properties.put("KAFKA_SSL_KEYSTORE_LOCATION", keyStorePath.toString());
|
||||
properties.put("KAFKA_SSL_KEYSTORE_TYPE", keyStoreType);
|
||||
properties.put("KAFKA_SSL_KEYSTORE_PASSWORD", KEY_STORE_PASSWORD);
|
||||
properties.put("KAFKA_SSL_KEY_PASSWORD", KEY_PASSWORD);
|
||||
properties.put("KAFKA_SSL_TRUSTSTORE_LOCATION", trustStorePath.toString());
|
||||
properties.put("KAFKA_SSL_TRUSTSTORE_TYPE", keyStoreType);
|
||||
properties.put("KAFKA_SSL_TRUSTSTORE_PASSWORD", KEY_STORE_PASSWORD);
|
||||
properties.put("KAFKA_ALLOW_EVERYONE_IF_NO_ACL_FOUND", "false");
|
||||
properties.put("KAFKA_SSL_CLIENT_AUTH", "required");
|
||||
properties.put("KAFKA_SSL_ENDPOINT_IDENTIFICATION_ALGORITHM", " ");
|
||||
|
@ -54,13 +54,13 @@ public class Kafka3ConnectionServiceSSLIT extends Kafka3ConnectionServiceBaseIT
|
|||
protected Map<String, String> getAdminClientConfigProperties() {
|
||||
final Map<String, String> properties = new LinkedHashMap<>(super.getAdminClientConfigProperties());
|
||||
properties.put(AdminClientConfig.SECURITY_PROTOCOL_CONFIG, SecurityProtocol.SSL.name());
|
||||
properties.put(KafkaClientProperty.SSL_KEY_PASSWORD.getProperty(), tlsConfiguration.getKeystorePassword());
|
||||
properties.put(KafkaClientProperty.SSL_KEYSTORE_LOCATION.getProperty(), tlsConfiguration.getKeystorePath());
|
||||
properties.put(KafkaClientProperty.SSL_KEYSTORE_TYPE.getProperty(), tlsConfiguration.getKeystoreType().getType());
|
||||
properties.put(KafkaClientProperty.SSL_KEYSTORE_PASSWORD.getProperty(), tlsConfiguration.getKeystorePassword());
|
||||
properties.put(KafkaClientProperty.SSL_TRUSTSTORE_LOCATION.getProperty(), tlsConfiguration.getTruststorePath());
|
||||
properties.put(KafkaClientProperty.SSL_TRUSTSTORE_TYPE.getProperty(), tlsConfiguration.getTruststoreType().getType());
|
||||
properties.put(KafkaClientProperty.SSL_TRUSTSTORE_PASSWORD.getProperty(), tlsConfiguration.getTruststorePassword());
|
||||
properties.put(KafkaClientProperty.SSL_KEY_PASSWORD.getProperty(), KEY_PASSWORD);
|
||||
properties.put(KafkaClientProperty.SSL_KEYSTORE_LOCATION.getProperty(), keyStorePath.toString());
|
||||
properties.put(KafkaClientProperty.SSL_KEYSTORE_TYPE.getProperty(), keyStoreType);
|
||||
properties.put(KafkaClientProperty.SSL_KEYSTORE_PASSWORD.getProperty(), KEY_STORE_PASSWORD);
|
||||
properties.put(KafkaClientProperty.SSL_TRUSTSTORE_LOCATION.getProperty(), trustStorePath.toString());
|
||||
properties.put(KafkaClientProperty.SSL_TRUSTSTORE_TYPE.getProperty(), keyStoreType);
|
||||
properties.put(KafkaClientProperty.SSL_TRUSTSTORE_PASSWORD.getProperty(), KEY_STORE_PASSWORD);
|
||||
return properties;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-ssl-context-service</artifactId>
|
||||
<artifactId>nifi-security-utils-api</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
|
|
@ -291,11 +291,6 @@
|
|||
<artifactId>commons-codec</artifactId>
|
||||
<version>${org.apache.commons.codec.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-cert</artifactId>
|
||||
|
@ -542,6 +537,18 @@
|
|||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-ssl</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-cert-builder</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>mockwebserver</artifactId>
|
||||
|
|
|
@ -81,8 +81,6 @@ import org.apache.nifi.processors.standard.util.ProxyAuthenticator;
|
|||
import org.apache.nifi.processors.standard.util.SoftLimitBoundedByteArrayOutputStream;
|
||||
import org.apache.nifi.proxy.ProxyConfiguration;
|
||||
import org.apache.nifi.proxy.ProxySpec;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsException;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.nifi.stream.io.StreamUtils;
|
||||
|
@ -114,7 +112,6 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
@ -770,8 +767,7 @@ public class InvokeHTTP extends AbstractProcessor {
|
|||
if (sslService != null) {
|
||||
final SSLContext sslContext = sslService.createContext();
|
||||
final SSLSocketFactory socketFactory = sslContext.getSocketFactory();
|
||||
final TlsConfiguration tlsConfiguration = sslService.createTlsConfiguration();
|
||||
final X509TrustManager trustManager = Objects.requireNonNull(SslContextFactory.getX509TrustManager(tlsConfiguration), "Trust Manager not found");
|
||||
final X509TrustManager trustManager = sslService.createTrustManager();;
|
||||
okHttpClientBuilder.sslSocketFactory(socketFactory, trustManager);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,16 +36,15 @@ import org.apache.nifi.provenance.ProvenanceEventType;
|
|||
import org.apache.nifi.proxy.ProxyConfiguration;
|
||||
import org.apache.nifi.proxy.ProxyConfigurationService;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.security.util.StandardTlsConfiguration;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsException;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardTrustManagerBuilder;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.nifi.util.LogMessage;
|
||||
import org.apache.nifi.util.MockFlowFile;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
import org.apache.nifi.util.TestRunners;
|
||||
import org.apache.nifi.web.util.ssl.SslContextUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
|
@ -57,11 +56,18 @@ import org.mockito.Answers;
|
|||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.IOException;
|
||||
import java.net.Proxy;
|
||||
import java.net.URI;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.HashMap;
|
||||
|
@ -137,25 +143,36 @@ public class InvokeHTTPTest {
|
|||
|
||||
private static final String TLS_CONNECTION_TIMEOUT = "60 s";
|
||||
|
||||
private static TlsConfiguration generatedTlsConfiguration;
|
||||
private static SSLContext sslContext;
|
||||
|
||||
private static TlsConfiguration truststoreTlsConfiguration;
|
||||
private static SSLContext trustStoreSslContext;
|
||||
|
||||
private static X509ExtendedTrustManager trustManager;
|
||||
|
||||
private MockWebServer mockWebServer;
|
||||
|
||||
private TestRunner runner;
|
||||
|
||||
@BeforeAll
|
||||
public static void setStores() {
|
||||
generatedTlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
truststoreTlsConfiguration = new StandardTlsConfiguration(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
generatedTlsConfiguration.getTruststorePath(),
|
||||
generatedTlsConfiguration.getTruststorePassword(),
|
||||
generatedTlsConfiguration.getTruststoreType()
|
||||
);
|
||||
public static void setStores() throws Exception {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.build();
|
||||
final char[] protectionParameter = new char[]{};
|
||||
|
||||
sslContext = new StandardSslContextBuilder()
|
||||
.trustStore(keyStore)
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(protectionParameter)
|
||||
.build();
|
||||
|
||||
trustStoreSslContext = new StandardSslContextBuilder()
|
||||
.trustStore(keyStore)
|
||||
.build();
|
||||
|
||||
trustManager = new StandardTrustManagerBuilder().trustStore(keyStore).build();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
|
@ -511,19 +528,19 @@ public class InvokeHTTPTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testRunGetHttp200SuccessSslContextServiceServerTrusted() throws InitializationException, GeneralSecurityException {
|
||||
assertResponseSuccessSslContextConfigured(generatedTlsConfiguration, truststoreTlsConfiguration);
|
||||
public void testRunGetHttp200SuccessSslContextServiceServerTrusted() throws InitializationException {
|
||||
assertResponseSuccessSslContextConfigured(trustStoreSslContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRunGetHttp200SuccessSslContextServiceMutualTrusted() throws InitializationException, GeneralSecurityException {
|
||||
assertResponseSuccessSslContextConfigured(generatedTlsConfiguration, generatedTlsConfiguration);
|
||||
public void testRunGetHttp200SuccessSslContextServiceMutualTrusted() throws InitializationException {
|
||||
assertResponseSuccessSslContextConfigured(sslContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRunGetSslContextServiceMutualTrustedClientCertificateMissing() throws InitializationException, GeneralSecurityException {
|
||||
public void testRunGetSslContextServiceMutualTrustedClientCertificateMissing() throws InitializationException {
|
||||
runner.setProperty(InvokeHTTP.HTTP2_DISABLED, StringUtils.capitalize(Boolean.TRUE.toString()));
|
||||
setSslContextConfiguration(generatedTlsConfiguration, truststoreTlsConfiguration);
|
||||
setSslContextConfiguration(trustStoreSslContext);
|
||||
mockWebServer.requireClientAuth();
|
||||
|
||||
setUrlProperty();
|
||||
|
@ -992,8 +1009,8 @@ public class InvokeHTTPTest {
|
|||
runner.assertTransferCount(InvokeHTTP.FAILURE, 0);
|
||||
}
|
||||
|
||||
private void assertResponseSuccessSslContextConfigured(final TlsConfiguration serverTlsConfiguration, final TlsConfiguration clientTlsConfiguration) throws InitializationException, TlsException {
|
||||
setSslContextConfiguration(serverTlsConfiguration, clientTlsConfiguration);
|
||||
private void assertResponseSuccessSslContextConfigured(final SSLContext clientSslContext) throws InitializationException {
|
||||
setSslContextConfiguration(clientSslContext);
|
||||
enqueueResponseCodeAndRun(HTTP_OK);
|
||||
|
||||
assertResponseSuccessRelationships();
|
||||
|
@ -1003,14 +1020,12 @@ public class InvokeHTTPTest {
|
|||
flowFile.assertAttributeExists(InvokeHTTP.REMOTE_DN);
|
||||
}
|
||||
|
||||
private void setSslContextConfiguration(final TlsConfiguration serverTlsConfiguration, final TlsConfiguration clientTlsConfiguration) throws InitializationException, TlsException {
|
||||
final SSLContextService sslContextService = setSslContextService();
|
||||
final SSLContext serverSslContext = SslContextUtils.createSslContext(serverTlsConfiguration);
|
||||
setMockWebServerSslSocketFactory(serverSslContext);
|
||||
private void setSslContextConfiguration(final SSLContext clientSslContext) throws InitializationException {
|
||||
setMockWebServerSslSocketFactory();
|
||||
|
||||
final SSLContext clientSslContext = SslContextUtils.createSslContext(clientTlsConfiguration);
|
||||
final SSLContextService sslContextService = setSslContextService();
|
||||
when(sslContextService.createContext()).thenReturn(clientSslContext);
|
||||
when(sslContextService.createTlsConfiguration()).thenReturn(clientTlsConfiguration);
|
||||
when(sslContextService.createTrustManager()).thenReturn(trustManager);
|
||||
}
|
||||
|
||||
private SSLContextService setSslContextService() throws InitializationException {
|
||||
|
@ -1026,7 +1041,7 @@ public class InvokeHTTPTest {
|
|||
return sslContextService;
|
||||
}
|
||||
|
||||
private void setMockWebServerSslSocketFactory(final SSLContext sslContext) {
|
||||
private void setMockWebServerSslSocketFactory() {
|
||||
final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
|
||||
if (sslSocketFactory == null) {
|
||||
throw new IllegalArgumentException("Socket Factory not found");
|
||||
|
|
|
@ -28,9 +28,10 @@ import okio.Okio;
|
|||
import org.apache.nifi.processors.standard.http.ContentEncodingStrategy;
|
||||
import org.apache.nifi.processors.standard.http.HttpProtocolStrategy;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.StandardTlsConfiguration;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardTrustManagerBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsPlatform;
|
||||
import org.apache.nifi.serialization.record.MockRecordParser;
|
||||
|
@ -41,7 +42,6 @@ import org.apache.nifi.ssl.SSLContextService;
|
|||
import org.apache.nifi.util.MockFlowFile;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
import org.apache.nifi.util.TestRunners;
|
||||
import org.apache.nifi.web.util.ssl.SslContextUtils;
|
||||
import org.eclipse.jetty.server.NetworkConnector;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.util.thread.ThreadPool;
|
||||
|
@ -49,16 +49,14 @@ import org.junit.jupiter.api.AfterEach;
|
|||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.condition.EnabledIf;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLHandshakeException;
|
||||
import javax.net.ssl.SSLSession;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.TrustManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
|
@ -70,6 +68,11 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
@ -82,6 +85,8 @@ import static org.apache.nifi.processors.standard.ListenHTTP.RELATIONSHIP_SUCCES
|
|||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class TestListenHTTP {
|
||||
|
||||
|
@ -97,8 +102,6 @@ public class TestListenHTTP {
|
|||
private final static String HTTP_SERVER_BASEPATH_EL = "${" + BASEPATH_VARIABLE + "}";
|
||||
private static final String MULTIPART_ATTRIBUTE = "http.multipart.name";
|
||||
|
||||
private static final String TLS_1_3 = "TLSv1.3";
|
||||
private static final String TLS_1_2 = "TLSv1.2";
|
||||
private static final String LOCALHOST = "localhost";
|
||||
|
||||
private static final int SOCKET_CONNECT_TIMEOUT = 100;
|
||||
|
@ -111,10 +114,6 @@ public class TestListenHTTP {
|
|||
private static final Duration CLIENT_CALL_TIMEOUT = Duration.ofSeconds(10);
|
||||
private static final String LOCALHOST_DN = "CN=localhost";
|
||||
|
||||
private static TlsConfiguration serverConfiguration;
|
||||
private static TlsConfiguration serverTls_1_3_Configuration;
|
||||
private static TlsConfiguration serverNoTruststoreConfiguration;
|
||||
private static SSLContext serverKeyStoreSslContext;
|
||||
private static SSLContext serverKeyStoreNoTrustStoreSslContext;
|
||||
private static SSLContext keyStoreSslContext;
|
||||
private static SSLContext trustStoreSslContext;
|
||||
|
@ -123,67 +122,29 @@ public class TestListenHTTP {
|
|||
private ListenHTTP proc;
|
||||
private TestRunner runner;
|
||||
|
||||
|
||||
static boolean isTls13Supported() {
|
||||
return TLS_1_3.equals(TlsPlatform.getLatestProtocol());
|
||||
}
|
||||
|
||||
@BeforeAll
|
||||
public static void setUpSuite() throws GeneralSecurityException {
|
||||
// generate new keystore and truststore
|
||||
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.build();
|
||||
final char[] protectionParameter = new char[]{};
|
||||
|
||||
serverConfiguration = new StandardTlsConfiguration(
|
||||
tlsConfiguration.getKeystorePath(),
|
||||
tlsConfiguration.getKeystorePassword(),
|
||||
tlsConfiguration.getKeyPassword(),
|
||||
tlsConfiguration.getKeystoreType(),
|
||||
tlsConfiguration.getTruststorePath(),
|
||||
tlsConfiguration.getTruststorePassword(),
|
||||
tlsConfiguration.getTruststoreType(),
|
||||
TLS_1_2
|
||||
);
|
||||
serverTls_1_3_Configuration = new StandardTlsConfiguration(
|
||||
tlsConfiguration.getKeystorePath(),
|
||||
tlsConfiguration.getKeystorePassword(),
|
||||
tlsConfiguration.getKeyPassword(),
|
||||
tlsConfiguration.getKeystoreType(),
|
||||
tlsConfiguration.getTruststorePath(),
|
||||
tlsConfiguration.getTruststorePassword(),
|
||||
tlsConfiguration.getTruststoreType(),
|
||||
TLS_1_3
|
||||
);
|
||||
serverNoTruststoreConfiguration = new StandardTlsConfiguration(
|
||||
tlsConfiguration.getKeystorePath(),
|
||||
tlsConfiguration.getKeystorePassword(),
|
||||
tlsConfiguration.getKeyPassword(),
|
||||
tlsConfiguration.getKeystoreType(),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
TLS_1_2
|
||||
);
|
||||
trustManager = new StandardTrustManagerBuilder().trustStore(keyStore).build();
|
||||
serverKeyStoreNoTrustStoreSslContext = new StandardSslContextBuilder()
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(protectionParameter)
|
||||
.build();
|
||||
|
||||
serverKeyStoreSslContext = SslContextUtils.createSslContext(serverConfiguration);
|
||||
trustManager = SslContextFactory.getX509TrustManager(serverConfiguration);
|
||||
serverKeyStoreNoTrustStoreSslContext = SslContextFactory.createSslContext(serverNoTruststoreConfiguration, new TrustManager[]{trustManager});
|
||||
|
||||
keyStoreSslContext = SslContextUtils.createSslContext(new StandardTlsConfiguration(
|
||||
tlsConfiguration.getKeystorePath(),
|
||||
tlsConfiguration.getKeystorePassword(),
|
||||
tlsConfiguration.getKeystoreType(),
|
||||
tlsConfiguration.getTruststorePath(),
|
||||
tlsConfiguration.getTruststorePassword(),
|
||||
tlsConfiguration.getTruststoreType())
|
||||
);
|
||||
trustStoreSslContext = SslContextUtils.createSslContext(new StandardTlsConfiguration(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
tlsConfiguration.getTruststorePath(),
|
||||
tlsConfiguration.getTruststorePassword(),
|
||||
tlsConfiguration.getTruststoreType())
|
||||
);
|
||||
keyStoreSslContext = new StandardSslContextBuilder()
|
||||
.trustStore(keyStore)
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(protectionParameter)
|
||||
.build();
|
||||
trustStoreSslContext = new StandardSslContextBuilder()
|
||||
.trustStore(keyStore)
|
||||
.build();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
|
@ -238,7 +199,7 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testSecurePOSTRequestsReceivedWithoutELHttp2AndHttp1() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO);
|
||||
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
runner.setProperty(ListenHTTP.HTTP_PROTOCOL_STRATEGY, HttpProtocolStrategy.H2_HTTP_1_1);
|
||||
|
@ -249,7 +210,7 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testSecurePOSTRequestsReturnCodeReceivedWithoutELHttp2() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO);
|
||||
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
runner.setProperty(ListenHTTP.RETURN_CODE, Integer.toString(HttpServletResponse.SC_NO_CONTENT));
|
||||
|
@ -261,7 +222,7 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testSecurePOSTRequestsReceivedWithEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, HTTP_SERVER_PORT_EL);
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_SERVER_BASEPATH_EL);
|
||||
|
@ -272,7 +233,7 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testSecurePOSTRequestsReturnCodeReceivedWithEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO);
|
||||
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
runner.setProperty(ListenHTTP.RETURN_CODE, Integer.toString(HttpServletResponse.SC_NO_CONTENT));
|
||||
|
@ -283,7 +244,7 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testSecureTwoWaySslPOSTRequestsReceivedWithoutEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, serverConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED);
|
||||
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
runner.assertValid();
|
||||
|
@ -293,7 +254,7 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testSecureTwoWaySslPOSTRequestsReceivedWithUnauthorizedSubjectDn() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, serverConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED);
|
||||
|
||||
runner.setProperty(ListenHTTP.AUTHORIZED_DN_PATTERN, "CN=other");
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
|
@ -304,7 +265,7 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testSecureTwoWaySslPOSTRequestsReceivedWithAuthorizedIssuerDn() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, serverConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED);
|
||||
|
||||
runner.setProperty(ListenHTTP.AUTHORIZED_DN_PATTERN, LOCALHOST_DN);
|
||||
runner.setProperty(ListenHTTP.AUTHORIZED_ISSUER_DN_PATTERN, LOCALHOST_DN);
|
||||
|
@ -316,7 +277,7 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testSecureTwoWaySslPOSTRequestsReceivedWithUnauthorizedIssuerDn() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, serverConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED);
|
||||
|
||||
runner.setProperty(ListenHTTP.AUTHORIZED_DN_PATTERN, LOCALHOST_DN); // Although subject is authorized, issuer is not
|
||||
runner.setProperty(ListenHTTP.AUTHORIZED_ISSUER_DN_PATTERN, "CN=other");
|
||||
|
@ -328,7 +289,7 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testSecureTwoWaySslPOSTRequestsReturnCodeReceivedWithoutEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, serverConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED);
|
||||
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
runner.setProperty(ListenHTTP.RETURN_CODE, Integer.toString(HttpServletResponse.SC_NO_CONTENT));
|
||||
|
@ -339,7 +300,7 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testSecureTwoWaySslPOSTRequestsReceivedWithEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, serverConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED);
|
||||
|
||||
runner.setProperty(ListenHTTP.PORT, HTTP_SERVER_PORT_EL);
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_SERVER_BASEPATH_EL);
|
||||
|
@ -350,7 +311,7 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testSecureTwoWaySslPOSTRequestsReturnCodeReceivedWithEL() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, serverConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED);
|
||||
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
runner.setProperty(ListenHTTP.RETURN_CODE, Integer.toString(HttpServletResponse.SC_NO_CONTENT));
|
||||
|
@ -361,12 +322,12 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testSecureServerSupportsCurrentTlsProtocolVersion() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO);
|
||||
final int listeningPort = startSecureServer();
|
||||
|
||||
final SSLSocketFactory sslSocketFactory = trustStoreSslContext.getSocketFactory();
|
||||
try (final SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(LOCALHOST, listeningPort)) {
|
||||
final String currentProtocol = serverNoTruststoreConfiguration.getProtocol();
|
||||
final String currentProtocol = TlsPlatform.getLatestProtocol();
|
||||
sslSocket.setEnabledProtocols(new String[]{currentProtocol});
|
||||
|
||||
sslSocket.startHandshake();
|
||||
|
@ -377,37 +338,19 @@ public class TestListenHTTP {
|
|||
|
||||
@Test
|
||||
public void testSecureServerTrustStoreConfiguredClientAuthenticationRequired() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED, serverConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.REQUIRED);
|
||||
final int port = startSecureServer();
|
||||
assertThrows(IOException.class, () -> sendMessage(null, true, port, false, HTTP_POST));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSecureServerTrustStoreNotConfiguredClientAuthenticationNotRequired() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverNoTruststoreConfiguration);
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO);
|
||||
final int port = startSecureServer();
|
||||
final int responseCode = sendMessage(null, true, port, true, HTTP_POST);
|
||||
assertEquals(HttpServletResponse.SC_NO_CONTENT, responseCode);
|
||||
}
|
||||
|
||||
@EnabledIf(value = "isTls13Supported", disabledReason = "TLSv1.3 is not supported")
|
||||
@Test
|
||||
public void testSecureServerRejectsUnsupportedTlsProtocolVersion() throws Exception {
|
||||
configureProcessorSslContextService(ListenHTTP.ClientAuthentication.AUTO, serverTls_1_3_Configuration);
|
||||
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
runner.setProperty(ListenHTTP.RETURN_CODE, Integer.toString(HttpServletResponse.SC_NO_CONTENT));
|
||||
runner.assertValid();
|
||||
|
||||
final int listeningPort = startWebServer();
|
||||
final SSLSocketFactory sslSocketFactory = trustStoreSslContext.getSocketFactory();
|
||||
try (final SSLSocket sslSocket = (SSLSocket) sslSocketFactory.createSocket(LOCALHOST, listeningPort)) {
|
||||
sslSocket.setEnabledProtocols(new String[]{TLS_1_2});
|
||||
|
||||
assertThrows(SSLHandshakeException.class, sslSocket::startHandshake);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMaxThreadPoolSizeTooLow() {
|
||||
runner.setProperty(ListenHTTP.BASE_PATH, HTTP_BASE_PATH);
|
||||
|
@ -471,16 +414,18 @@ public class TestListenHTTP {
|
|||
}
|
||||
|
||||
final String expectedMessage =
|
||||
"\"1\",\"rec1\",\"101\"\n" +
|
||||
"\"2\",\"rec2\",\"102\"\n" +
|
||||
"\"3\",\"rec3\",\"103\"\n" +
|
||||
"\"4\",\"rec4\",\"104\"\n";
|
||||
"""
|
||||
"1","rec1","101"
|
||||
"2","rec2","102"
|
||||
"3","rec3","103"
|
||||
"4","rec4","104"
|
||||
""";
|
||||
|
||||
startWebServerAndSendMessages(Collections.singletonList(""), HttpServletResponse.SC_OK, false, false, HTTP_POST);
|
||||
List<MockFlowFile> mockFlowFiles = runner.getFlowFilesForRelationship(RELATIONSHIP_SUCCESS);
|
||||
|
||||
runner.assertTransferCount(RELATIONSHIP_SUCCESS, 1);
|
||||
mockFlowFiles.get(0).assertContentEquals(expectedMessage);
|
||||
mockFlowFiles.getFirst().assertContentEquals(expectedMessage);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -533,7 +478,7 @@ public class TestListenHTTP {
|
|||
assertTrue(response.isSuccessful());
|
||||
|
||||
runner.assertTransferCount(RELATIONSHIP_SUCCESS, 1);
|
||||
final MockFlowFile flowFile = runner.getFlowFilesForRelationship(RELATIONSHIP_SUCCESS).iterator().next();
|
||||
final MockFlowFile flowFile = runner.getFlowFilesForRelationship(RELATIONSHIP_SUCCESS).getFirst();
|
||||
flowFile.assertContentEquals(message);
|
||||
}
|
||||
}
|
||||
|
@ -631,8 +576,8 @@ public class TestListenHTTP {
|
|||
mockFlowFiles.get(3).assertContentEquals("payload 2");
|
||||
|
||||
if (twoWaySsl) {
|
||||
mockFlowFiles.get(0).assertAttributeEquals("restlistener.remote.user.dn", LOCALHOST_DN);
|
||||
mockFlowFiles.get(0).assertAttributeEquals("restlistener.remote.issuer.dn", LOCALHOST_DN);
|
||||
mockFlowFiles.getFirst().assertAttributeEquals("restlistener.remote.user.dn", LOCALHOST_DN);
|
||||
mockFlowFiles.getFirst().assertAttributeEquals("restlistener.remote.issuer.dn", LOCALHOST_DN);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -678,16 +623,18 @@ public class TestListenHTTP {
|
|||
}
|
||||
}
|
||||
|
||||
private void configureProcessorSslContextService(final ListenHTTP.ClientAuthentication clientAuthentication,
|
||||
final TlsConfiguration tlsConfiguration) throws InitializationException {
|
||||
final RestrictedSSLContextService sslContextService = Mockito.mock(RestrictedSSLContextService.class);
|
||||
Mockito.when(sslContextService.getIdentifier()).thenReturn(SSL_CONTEXT_SERVICE_IDENTIFIER);
|
||||
Mockito.when(sslContextService.createTlsConfiguration()).thenReturn(tlsConfiguration);
|
||||
private void configureProcessorSslContextService(final ListenHTTP.ClientAuthentication clientAuthentication) throws InitializationException {
|
||||
final RestrictedSSLContextService sslContextService = mock(RestrictedSSLContextService.class);
|
||||
when(sslContextService.getIdentifier()).thenReturn(SSL_CONTEXT_SERVICE_IDENTIFIER);
|
||||
|
||||
final TlsConfiguration tlsConfiguration = mock(TlsConfiguration.class);
|
||||
when(tlsConfiguration.getEnabledProtocols()).thenReturn(TlsPlatform.getPreferredProtocols().toArray(new String[0]));
|
||||
when(sslContextService.createTlsConfiguration()).thenReturn(tlsConfiguration);
|
||||
|
||||
if (ListenHTTP.ClientAuthentication.REQUIRED.equals(clientAuthentication)) {
|
||||
Mockito.when(sslContextService.createContext()).thenReturn(serverKeyStoreSslContext);
|
||||
when(sslContextService.createContext()).thenReturn(keyStoreSslContext);
|
||||
} else {
|
||||
Mockito.when(sslContextService.createContext()).thenReturn(serverKeyStoreNoTrustStoreSslContext);
|
||||
when(sslContextService.createContext()).thenReturn(serverKeyStoreNoTrustStoreSslContext);
|
||||
}
|
||||
runner.addControllerService(SSL_CONTEXT_SERVICE_IDENTIFIER, sslContextService);
|
||||
|
||||
|
|
|
@ -24,21 +24,28 @@ import org.apache.nifi.event.transport.configuration.TransportProtocol;
|
|||
import org.apache.nifi.event.transport.netty.ByteArrayNettyEventSenderFactory;
|
||||
import org.apache.nifi.processor.util.listen.ListenerProperties;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.security.util.ClientAuth;
|
||||
import org.apache.nifi.security.util.TlsException;
|
||||
import org.apache.nifi.ssl.RestrictedSSLContextService;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.nifi.util.MockFlowFile;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
import org.apache.nifi.util.TestRunners;
|
||||
import org.apache.nifi.web.util.ssl.SslContextUtils;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -58,9 +65,23 @@ public class TestListenTCP {
|
|||
private TestRunner runner;
|
||||
|
||||
@BeforeAll
|
||||
public static void configureServices() throws TlsException {
|
||||
keyStoreSslContext = SslContextUtils.createKeyStoreSslContext();
|
||||
trustStoreSslContext = SslContextUtils.createTrustStoreSslContext();
|
||||
public static void configureServices() throws Exception {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.build();
|
||||
|
||||
keyStoreSslContext = new StandardSslContextBuilder()
|
||||
.trustStore(keyStore)
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(new char[]{})
|
||||
.build();
|
||||
|
||||
trustStoreSslContext = new StandardSslContextBuilder()
|
||||
.trustStore(keyStore)
|
||||
.keyPassword(new char[]{})
|
||||
.build();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
|
|
|
@ -27,8 +27,9 @@ import org.apache.nifi.flowfile.FlowFile;
|
|||
import org.apache.nifi.processors.standard.property.TransmissionStrategy;
|
||||
import org.apache.nifi.provenance.ProvenanceEventRecord;
|
||||
import org.apache.nifi.provenance.ProvenanceEventType;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.serialization.RecordReader;
|
||||
import org.apache.nifi.serialization.RecordReaderFactory;
|
||||
import org.apache.nifi.serialization.RecordSetWriter;
|
||||
|
@ -40,7 +41,6 @@ import org.apache.nifi.ssl.SSLContextService;
|
|||
import org.apache.nifi.util.MockFlowFile;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
import org.apache.nifi.util.TestRunners;
|
||||
import org.apache.nifi.web.util.ssl.SslContextUtils;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -52,11 +52,18 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
|||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
|
@ -138,10 +145,7 @@ public class TestPutTCP {
|
|||
|
||||
@Test
|
||||
public void testRunSuccessSslContextService() throws Exception {
|
||||
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
|
||||
final SSLContext sslContext = SslContextUtils.createSslContext(tlsConfiguration);
|
||||
assertNotNull(sslContext, "SSLContext not found");
|
||||
final SSLContext sslContext = getSslContext();
|
||||
final String identifier = SSLContextService.class.getName();
|
||||
final SSLContextService sslContextService = Mockito.mock(SSLContextService.class);
|
||||
Mockito.when(sslContextService.getIdentifier()).thenReturn(identifier);
|
||||
|
@ -367,6 +371,19 @@ public class TestPutTCP {
|
|||
return new String[] {new String(content)};
|
||||
}
|
||||
|
||||
private SSLContext getSslContext() throws GeneralSecurityException {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.build();
|
||||
return new StandardSslContextBuilder()
|
||||
.trustStore(keyStore)
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(new char[]{})
|
||||
.build();
|
||||
}
|
||||
|
||||
private static class MockRecordSetWriter implements RecordSetWriter {
|
||||
private final OutputStream outputStream;
|
||||
|
||||
|
|
|
@ -1,110 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.web.util.ssl;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.StandardTlsConfiguration;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsException;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.io.File;
|
||||
|
||||
public class SslContextUtils {
|
||||
private static final TlsConfiguration TLS_CONFIGURATION;
|
||||
|
||||
private static final TlsConfiguration KEYSTORE_TLS_CONFIGURATION;
|
||||
|
||||
private static final TlsConfiguration TRUSTSTORE_TLS_CONFIGURATION;
|
||||
|
||||
static {
|
||||
try {
|
||||
TLS_CONFIGURATION = new TemporaryKeyStoreBuilder().build();
|
||||
|
||||
KEYSTORE_TLS_CONFIGURATION = new StandardTlsConfiguration(
|
||||
TLS_CONFIGURATION.getKeystorePath(),
|
||||
TLS_CONFIGURATION.getKeystorePassword(),
|
||||
TLS_CONFIGURATION.getKeyPassword(),
|
||||
TLS_CONFIGURATION.getKeystoreType().getType(),
|
||||
TLS_CONFIGURATION.getTruststorePath(),
|
||||
TLS_CONFIGURATION.getTruststorePassword(),
|
||||
TLS_CONFIGURATION.getTruststoreType().getType()
|
||||
);
|
||||
|
||||
TRUSTSTORE_TLS_CONFIGURATION = new StandardTlsConfiguration(
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
TLS_CONFIGURATION.getTruststorePath(),
|
||||
TLS_CONFIGURATION.getTruststorePassword(),
|
||||
TLS_CONFIGURATION.getTruststoreType().getType()
|
||||
);
|
||||
} catch (final Exception e) {
|
||||
throw new IllegalStateException("Failed to create TLS configuration for testing", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create SSLContext with Key Store and Trust Store configured
|
||||
*
|
||||
* @return SSLContext configured with Key Store and Trust Store
|
||||
* @throws TlsException Thrown on SslContextFactory.createSslContext()
|
||||
*/
|
||||
public static SSLContext createKeyStoreSslContext() throws TlsException {
|
||||
return SslContextFactory.createSslContext(KEYSTORE_TLS_CONFIGURATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create SSLContext with Trust Store configured
|
||||
*
|
||||
* @return SSLContext configured with Trust Store
|
||||
* @throws TlsException Thrown on SslContextFactory.createSslContext()
|
||||
*/
|
||||
public static SSLContext createTrustStoreSslContext() throws TlsException {
|
||||
return SslContextFactory.createSslContext(TRUSTSTORE_TLS_CONFIGURATION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create SSLContext using Keystore and Truststore with deleteOnExit() for files
|
||||
*
|
||||
* @param tlsConfiguration TLS Configuration
|
||||
* @return SSLContext configured with generated Keystore and Truststore
|
||||
* @throws TlsException Thrown on SslContextFactory.createSslContext()
|
||||
*/
|
||||
public static SSLContext createSslContext(final TlsConfiguration tlsConfiguration) throws TlsException {
|
||||
final String keystorePath = tlsConfiguration.getKeystorePath();
|
||||
if (StringUtils.isNotBlank(keystorePath)) {
|
||||
final File keystoreFile = new File(keystorePath);
|
||||
keystoreFile.deleteOnExit();
|
||||
}
|
||||
|
||||
final String truststorePath = tlsConfiguration.getTruststorePath();
|
||||
if (StringUtils.isNotBlank(truststorePath)) {
|
||||
final File truststoreFile = new File(truststorePath);
|
||||
truststoreFile.deleteOnExit();
|
||||
}
|
||||
|
||||
final SSLContext sslContext = SslContextFactory.createSslContext(tlsConfiguration);
|
||||
if (sslContext == null) {
|
||||
throw new TlsException(String.format("Failed to create SSLContext from Configuration %s", tlsConfiguration));
|
||||
}
|
||||
return sslContext;
|
||||
}
|
||||
}
|
|
@ -42,7 +42,13 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<artifactId>nifi-security-ssl</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-cert-builder</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
|
|
@ -22,9 +22,9 @@ import org.apache.nifi.distributed.cache.client.DistributedMapCacheClientService
|
|||
import org.apache.nifi.distributed.cache.client.Serializer;
|
||||
import org.apache.nifi.distributed.cache.client.exception.DeserializationException;
|
||||
import org.apache.nifi.processor.Processor;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
import org.apache.nifi.util.TestRunners;
|
||||
|
@ -34,14 +34,22 @@ import org.junit.jupiter.api.Test;
|
|||
import org.mockito.Mockito;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class DistributedMapCacheTlsTest {
|
||||
|
||||
|
@ -98,19 +106,21 @@ public class DistributedMapCacheTlsTest {
|
|||
assertFalse(client.containsKey(key, serializer));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a fresh {@link SSLContext} in order to test mutual TLS authentication aspect of the
|
||||
* distributed cache protocol.
|
||||
*
|
||||
* @return a NiFi {@link SSLContextService}, to be used to secure the distributed cache comms
|
||||
* @throws GeneralSecurityException on SSLContext generation failure
|
||||
*/
|
||||
private static SSLContextService createSslContextService() throws GeneralSecurityException {
|
||||
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
final SSLContext sslContext = SslContextFactory.createSslContext(tlsConfiguration);
|
||||
private static SSLContextService createSslContextService() throws NoSuchAlgorithmException {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.build();
|
||||
final SSLContext sslContext = new StandardSslContextBuilder()
|
||||
.trustStore(keyStore)
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(new char[]{})
|
||||
.build();
|
||||
|
||||
final SSLContextService sslContextService = Mockito.mock(SSLContextService.class);
|
||||
Mockito.when(sslContextService.getIdentifier()).thenReturn(sslContextService.getClass().getName());
|
||||
Mockito.when(sslContextService.createContext()).thenReturn(sslContext);
|
||||
when(sslContextService.getIdentifier()).thenReturn(sslContextService.getClass().getName());
|
||||
when(sslContextService.createContext()).thenReturn(sslContext);
|
||||
return sslContextService;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,12 +28,18 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<artifactId>nifi-security-utils-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-ssl</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils-api</artifactId>
|
||||
<artifactId>nifi-security-cert-builder</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -34,11 +34,11 @@ import org.apache.nifi.expression.ExpressionLanguageScope;
|
|||
import org.apache.nifi.processor.exception.ProcessException;
|
||||
import org.apache.nifi.processor.util.StandardValidators;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardTrustManagerBuilder;
|
||||
import org.apache.nifi.security.util.KeystoreType;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.StandardTlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsException;
|
||||
import org.apache.nifi.security.util.TlsPlatform;
|
||||
import org.apache.nifi.util.StringUtils;
|
||||
|
||||
|
@ -49,6 +49,9 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyStore;
|
||||
import java.util.ArrayList;
|
||||
|
@ -242,16 +245,46 @@ public class StandardSSLContextService extends AbstractControllerService impleme
|
|||
*/
|
||||
@Override
|
||||
public SSLContext createContext() {
|
||||
final TlsConfiguration tlsConfiguration = createTlsConfiguration();
|
||||
if (!tlsConfiguration.isTruststorePopulated()) {
|
||||
getLogger().warn("Trust Store properties not found: using platform default Certificate Authorities");
|
||||
}
|
||||
|
||||
try {
|
||||
final TrustManager[] trustManagers = SslContextFactory.getTrustManagers(tlsConfiguration);
|
||||
return SslContextFactory.createSslContext(tlsConfiguration, trustManagers);
|
||||
} catch (final TlsException e) {
|
||||
getLogger().error("Unable to create SSLContext: {}", e.getLocalizedMessage());
|
||||
final String protocol = getSslAlgorithm();
|
||||
final StandardSslContextBuilder sslContextBuilder = new StandardSslContextBuilder().protocol(protocol);
|
||||
|
||||
final TrustManager trustManager;
|
||||
final String trustStoreFile = getKeyStoreFile();
|
||||
if (trustStoreFile == null || trustStoreFile.isBlank()) {
|
||||
getLogger().debug("Trust Store File not configured");
|
||||
} else {
|
||||
trustManager = createTrustManager();
|
||||
sslContextBuilder.trustManager(trustManager);
|
||||
}
|
||||
|
||||
final String keyStoreFile = getKeyStoreFile();
|
||||
if (keyStoreFile == null || keyStoreFile.isBlank()) {
|
||||
getLogger().debug("Key Store File not configured");
|
||||
} else {
|
||||
final StandardKeyStoreBuilder keyStoreBuilder = new StandardKeyStoreBuilder();
|
||||
keyStoreBuilder.type(getKeyStoreType());
|
||||
keyStoreBuilder.password(getKeyStorePassword().toCharArray());
|
||||
|
||||
final Path keyStorePath = Paths.get(keyStoreFile);
|
||||
try (InputStream keyStoreInputStream = Files.newInputStream(keyStorePath)) {
|
||||
keyStoreBuilder.inputStream(keyStoreInputStream);
|
||||
final KeyStore keyStore = keyStoreBuilder.build();
|
||||
sslContextBuilder.keyStore(keyStore);
|
||||
}
|
||||
|
||||
final char[] keyProtectionPassword;
|
||||
final String keyPassword = getKeyPassword();
|
||||
if (keyPassword == null) {
|
||||
keyProtectionPassword = getKeyStorePassword().toCharArray();
|
||||
} else {
|
||||
keyProtectionPassword = keyPassword.toCharArray();
|
||||
}
|
||||
sslContextBuilder.keyPassword(keyProtectionPassword);
|
||||
}
|
||||
|
||||
return sslContextBuilder.build();
|
||||
} catch (final Exception e) {
|
||||
throw new ProcessException("Unable to create SSLContext", e);
|
||||
}
|
||||
}
|
||||
|
@ -264,12 +297,28 @@ public class StandardSSLContextService extends AbstractControllerService impleme
|
|||
@Override
|
||||
public X509TrustManager createTrustManager() {
|
||||
try {
|
||||
final X509TrustManager trustManager = SslContextFactory.getX509TrustManager(createTlsConfiguration());
|
||||
if (trustManager == null) {
|
||||
throw new ProcessException("X.509 Trust Manager not found using configured properties");
|
||||
final char[] password;
|
||||
final String trustStorePassword = getTrustStorePassword();
|
||||
if (trustStorePassword == null || trustStorePassword.isBlank()) {
|
||||
password = null;
|
||||
} else {
|
||||
password = trustStorePassword.toCharArray();
|
||||
}
|
||||
return trustManager;
|
||||
} catch (final TlsException e) {
|
||||
|
||||
final StandardKeyStoreBuilder builder = new StandardKeyStoreBuilder().type(getTrustStoreType()).password(password);
|
||||
|
||||
final String trustStoreFile = getTrustStoreFile();
|
||||
if (trustStoreFile == null || trustStoreFile.isBlank()) {
|
||||
throw new ProcessException("Trust Store File not specified");
|
||||
}
|
||||
|
||||
final Path trustStorePath = Paths.get(trustStoreFile);
|
||||
try (InputStream trustStoreInputStream = Files.newInputStream(trustStorePath)) {
|
||||
builder.inputStream(trustStoreInputStream);
|
||||
final KeyStore trustStore = builder.build();
|
||||
return new StandardTrustManagerBuilder().trustStore(trustStore).build();
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
throw new ProcessException("Unable to create X.509 Trust Manager", e);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,9 +14,12 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.security.util;
|
||||
package org.apache.nifi.ssl;
|
||||
|
||||
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.KeystoreType;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsPlatform;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
|
@ -25,15 +28,12 @@ import java.util.List;
|
|||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This class serves as a concrete immutable domain object (acting as an internal DTO)
|
||||
* for the various keystore and truststore configuration settings necessary for
|
||||
* building {@link javax.net.ssl.SSLContext}s.
|
||||
* Standard implementation of TLS Configuration for SSL Context Services
|
||||
*/
|
||||
public class StandardTlsConfiguration implements TlsConfiguration {
|
||||
class StandardTlsConfiguration implements TlsConfiguration {
|
||||
protected static final String SSL_PROTOCOL = "SSL";
|
||||
protected static final String TLS_PROTOCOL = "TLS";
|
||||
|
||||
private static final String TLS_PROTOCOL_VERSION = TlsPlatform.getLatestProtocol();
|
||||
private static final String MASKED_PASSWORD_LOG = "********";
|
||||
private static final String NULL_LOG = "null";
|
||||
|
||||
|
@ -48,63 +48,6 @@ public class StandardTlsConfiguration implements TlsConfiguration {
|
|||
|
||||
private final String protocol;
|
||||
|
||||
/**
|
||||
* Default constructor present for testing and completeness.
|
||||
*/
|
||||
public StandardTlsConfiguration() {
|
||||
this(null, null, null, "", null, null, "", null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a container object with the given configuration values.
|
||||
*
|
||||
* @param keystorePath the keystore path
|
||||
* @param keystorePassword the keystore password
|
||||
* @param keystoreType the keystore type
|
||||
* @param truststorePath the truststore path
|
||||
* @param truststorePassword the truststore password
|
||||
* @param truststoreType the truststore type
|
||||
*/
|
||||
public StandardTlsConfiguration(String keystorePath, String keystorePassword, KeystoreType keystoreType, String truststorePath, String truststorePassword, KeystoreType truststoreType) {
|
||||
this(keystorePath, keystorePassword, keystorePassword, keystoreType, truststorePath, truststorePassword, truststoreType, TLS_PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a container object with the given configuration values.
|
||||
*
|
||||
* @param keystorePath the keystore path
|
||||
* @param keystorePassword the keystore password
|
||||
* @param keyPassword the key password
|
||||
* @param keystoreType the keystore type
|
||||
* @param truststorePath the truststore path
|
||||
* @param truststorePassword the truststore password
|
||||
* @param truststoreType the truststore type
|
||||
*/
|
||||
public StandardTlsConfiguration(String keystorePath, String keystorePassword, String keyPassword,
|
||||
KeystoreType keystoreType, String truststorePath, String truststorePassword, KeystoreType truststoreType) {
|
||||
this(keystorePath, keystorePassword, keyPassword, keystoreType, truststorePath, truststorePassword, truststoreType, TLS_PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a container object with the given configuration values.
|
||||
*
|
||||
* @param keystorePath the keystore path
|
||||
* @param keystorePassword the keystore password
|
||||
* @param keyPassword the key password
|
||||
* @param keystoreType the keystore type as a String
|
||||
* @param truststorePath the truststore path
|
||||
* @param truststorePassword the truststore password
|
||||
* @param truststoreType the truststore type as a String
|
||||
*/
|
||||
public StandardTlsConfiguration(String keystorePath, String keystorePassword, String keyPassword,
|
||||
String keystoreType, String truststorePath, String truststorePassword, String truststoreType) {
|
||||
this(keystorePath, keystorePassword, keyPassword,
|
||||
(KeystoreType.isValidKeystoreType(keystoreType) ? KeystoreType.valueOf(keystoreType.toUpperCase()) : null),
|
||||
truststorePath, truststorePassword,
|
||||
(KeystoreType.isValidKeystoreType(truststoreType) ? KeystoreType.valueOf(truststoreType.toUpperCase()) : null),
|
||||
TLS_PROTOCOL_VERSION);
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a container object with the given configuration values.
|
||||
*
|
||||
|
@ -150,22 +93,6 @@ public class StandardTlsConfiguration implements TlsConfiguration {
|
|||
this.protocol = protocol;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a container object with a deep copy of the given configuration values.
|
||||
*
|
||||
* @param other the configuration to copy
|
||||
*/
|
||||
public StandardTlsConfiguration(TlsConfiguration other) {
|
||||
this.keystorePath = other.getKeystorePath();
|
||||
this.keystorePassword = other.getKeystorePassword();
|
||||
this.keyPassword = other.getKeyPassword();
|
||||
this.keystoreType = other.getKeystoreType();
|
||||
this.truststorePath = other.getTruststorePath();
|
||||
this.truststorePassword = other.getTruststorePassword();
|
||||
this.truststoreType = other.getTruststoreType();
|
||||
this.protocol = other.getProtocol();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getKeystorePath() {
|
||||
return keystorePath;
|
|
@ -1,256 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.ssl;
|
||||
|
||||
import org.apache.nifi.components.ValidationContext;
|
||||
import org.apache.nifi.components.ValidationResult;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.util.MockProcessContext;
|
||||
import org.apache.nifi.util.MockValidationContext;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
import org.apache.nifi.util.TestRunners;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
public class SSLContextServiceTest {
|
||||
private static TlsConfiguration tlsConfiguration;
|
||||
|
||||
@BeforeAll
|
||||
public static void setTlsConfiguration() {
|
||||
tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldFailToAddControllerServiceWithNoProperties() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final SSLContextService service = new StandardSSLContextService();
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
runner.addControllerService("test-no-properties", service, properties);
|
||||
runner.assertNotValid(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldFailToAddControllerServiceWithoutKeystoreType() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final SSLContextService service = new StandardSSLContextService();
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
properties.put(StandardSSLContextService.KEYSTORE.getName(), tlsConfiguration.getKeystorePath());
|
||||
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getKeystorePassword());
|
||||
runner.addControllerService("test-no-keystore-type", service, properties);
|
||||
runner.assertNotValid(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldFailToAddControllerServiceWithOnlyTruststorePath() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final SSLContextService service = new StandardSSLContextService();
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
properties.put(StandardSSLContextService.KEYSTORE.getName(), tlsConfiguration.getKeystorePath());
|
||||
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getKeystorePassword());
|
||||
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), tlsConfiguration.getTruststorePath());
|
||||
runner.addControllerService("test-no-truststore-password-or-type", service, properties);
|
||||
runner.assertNotValid(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldFailToAddControllerServiceWithWrongPasswords() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final SSLContextService service = new StandardSSLContextService();
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
properties.put(StandardSSLContextService.KEYSTORE.getName(), tlsConfiguration.getKeystorePath());
|
||||
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), String.class.getSimpleName());
|
||||
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), tlsConfiguration.getTruststorePath());
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), String.class.getSimpleName());
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
|
||||
runner.addControllerService("test-wrong-passwords", service, properties);
|
||||
|
||||
runner.assertNotValid(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldFailToAddControllerServiceWithNonExistentFiles() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final SSLContextService service = new StandardSSLContextService();
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
properties.put(StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/DOES-NOT-EXIST.jks");
|
||||
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
|
||||
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), tlsConfiguration.getTruststorePath());
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
|
||||
runner.addControllerService("test-keystore-file-does-not-exist", service, properties);
|
||||
runner.assertNotValid(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGood() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
SSLContextService service = new StandardSSLContextService();
|
||||
runner.addControllerService("test-good1", service);
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), tlsConfiguration.getKeystorePath());
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getKeystorePassword());
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), tlsConfiguration.getTruststorePath());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
|
||||
runner.enableControllerService(service);
|
||||
|
||||
runner.setProperty("SSL Context Svc ID", "test-good1");
|
||||
runner.assertValid(service);
|
||||
service = (SSLContextService) runner.getProcessContext().getControllerServiceLookup().getControllerService("test-good1");
|
||||
assertNotNull(service);
|
||||
SSLContextService sslService = service;
|
||||
sslService.createContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGoodWithEL() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
SSLContextService service = new StandardSSLContextService();
|
||||
runner.addControllerService("test-good1", service);
|
||||
runner.setEnvironmentVariableValue("keystore", tlsConfiguration.getKeystorePath());
|
||||
runner.setEnvironmentVariableValue("truststore", tlsConfiguration.getTruststorePath());
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), "${keystore}");
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getKeystorePassword());
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), "${truststore}");
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
|
||||
runner.enableControllerService(service);
|
||||
|
||||
runner.setProperty("SSL Context Svc ID", "test-good1");
|
||||
runner.assertValid(service);
|
||||
service = (SSLContextService) runner.getProcessContext().getControllerServiceLookup().getControllerService("test-good1");
|
||||
assertNotNull(service);
|
||||
SSLContextService sslService = service;
|
||||
sslService.createContext();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithChanges() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
SSLContextService service = new StandardSSLContextService();
|
||||
runner.addControllerService("test-good1", service);
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), tlsConfiguration.getKeystorePath());
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getKeystorePassword());
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), tlsConfiguration.getTruststorePath());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
|
||||
runner.enableControllerService(service);
|
||||
|
||||
runner.setProperty("SSL Context Svc ID", "test-good1");
|
||||
runner.assertValid(service);
|
||||
|
||||
runner.disableControllerService(service);
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/DOES-NOT-EXIST.jks");
|
||||
runner.assertNotValid(service);
|
||||
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), tlsConfiguration.getKeystorePath());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), String.class.getSimpleName());
|
||||
runner.assertNotValid(service);
|
||||
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
|
||||
runner.enableControllerService(service);
|
||||
runner.assertValid(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidationResultsCacheShouldExpire() throws InitializationException, IOException {
|
||||
// Copy the keystore and truststore to a tmp directory so the originals are not modified
|
||||
File originalKeystore = new File(tlsConfiguration.getKeystorePath());
|
||||
File originalTruststore = new File(tlsConfiguration.getTruststorePath());
|
||||
|
||||
File tmpKeystore = File.createTempFile(getClass().getSimpleName(), ".keystore.p12");
|
||||
File tmpTruststore = File.createTempFile(getClass().getSimpleName(), ".truststore.p12");
|
||||
|
||||
Files.copy(originalKeystore.toPath(), tmpKeystore.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.copy(originalTruststore.toPath(), tmpTruststore.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
StandardSSLContextService service = new StandardSSLContextService();
|
||||
final String serviceIdentifier = "test-should-expire";
|
||||
runner.addControllerService(serviceIdentifier, service);
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), tmpKeystore.getAbsolutePath());
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), tlsConfiguration.getKeystorePassword());
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), tlsConfiguration.getKeystoreType().getType());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), tmpTruststore.getAbsolutePath());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
|
||||
runner.enableControllerService(service);
|
||||
|
||||
runner.setProperty("SSL Context Svc ID", serviceIdentifier);
|
||||
runner.assertValid(service);
|
||||
|
||||
// Act
|
||||
assertTrue(tmpKeystore.delete());
|
||||
assertFalse(tmpKeystore.exists());
|
||||
|
||||
// Manually validate the service (expecting cached result to be returned)
|
||||
final MockProcessContext processContext = (MockProcessContext) runner.getProcessContext();
|
||||
// This service does not use the state manager
|
||||
final ValidationContext validationContext = new MockValidationContext(processContext, null);
|
||||
|
||||
// Even though the keystore file is no longer present, because no property changed, the cached result is still valid
|
||||
Collection<ValidationResult> validationResults = service.customValidate(validationContext);
|
||||
assertTrue(validationResults.isEmpty(), "validation results is not empty");
|
||||
|
||||
// Assert
|
||||
|
||||
// Have to exhaust the cached result by checking n-1 more times
|
||||
for (int i = 2; i < service.getValidationCacheExpiration(); i++) {
|
||||
validationResults = service.customValidate(validationContext);
|
||||
assertTrue(validationResults.isEmpty(), "validation results is not empty");
|
||||
}
|
||||
|
||||
validationResults = service.customValidate(validationContext);
|
||||
assertFalse(validationResults.isEmpty(), "validation results is empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGoodTrustOnly() throws InitializationException {
|
||||
TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
SSLContextService service = new StandardSSLContextService();
|
||||
HashMap<String, String> properties = new HashMap<>();
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), tlsConfiguration.getTruststorePath());
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), tlsConfiguration.getTruststorePassword());
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), tlsConfiguration.getTruststoreType().getType());
|
||||
runner.addControllerService("test-good2", service, properties);
|
||||
runner.enableControllerService(service);
|
||||
|
||||
runner.setProperty("SSL Context Svc ID", "test-good2");
|
||||
runner.assertValid();
|
||||
assertNotNull(service);
|
||||
service.createContext();
|
||||
}
|
||||
}
|
|
@ -18,8 +18,8 @@ package org.apache.nifi.ssl;
|
|||
|
||||
import org.apache.nifi.processor.Processor;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsPlatform;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
import org.apache.nifi.util.TestRunners;
|
||||
|
@ -27,19 +27,42 @@ import org.junit.jupiter.api.BeforeAll;
|
|||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class StandardRestrictedSSLContextServiceTest {
|
||||
|
||||
private static final String ALIAS = "entry-0";
|
||||
|
||||
private static final String SERVICE_ID = StandardRestrictedSSLContextService.class.getSimpleName();
|
||||
|
||||
private static TlsConfiguration tlsConfiguration;
|
||||
private static final String KEY_STORE_EXTENSION = ".p12";
|
||||
|
||||
private static final String KEY_STORE_PASS = StandardRestrictedSSLContextServiceTest.class.getName();
|
||||
|
||||
@TempDir
|
||||
private static Path keyStoreDirectory;
|
||||
|
||||
private static Path keyStorePath;
|
||||
|
||||
private static String keyStoreType;
|
||||
|
||||
@Mock
|
||||
private Processor processor;
|
||||
|
@ -49,8 +72,18 @@ public class StandardRestrictedSSLContextServiceTest {
|
|||
private TestRunner runner;
|
||||
|
||||
@BeforeAll
|
||||
public static void setConfiguration() {
|
||||
tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
public static void setConfiguration() throws Exception {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder().build();
|
||||
keyStore.setKeyEntry(ALIAS, keyPair.getPrivate(), KEY_STORE_PASS.toCharArray(), new Certificate[]{certificate});
|
||||
|
||||
keyStorePath = Files.createTempFile(keyStoreDirectory, StandardRestrictedSSLContextServiceTest.class.getSimpleName(), KEY_STORE_EXTENSION);
|
||||
try (OutputStream outputStream = Files.newOutputStream(keyStorePath)) {
|
||||
keyStore.store(outputStream, KEY_STORE_PASS.toCharArray());
|
||||
}
|
||||
|
||||
keyStoreType = keyStore.getType().toUpperCase();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
|
@ -95,11 +128,11 @@ public class StandardRestrictedSSLContextServiceTest {
|
|||
}
|
||||
|
||||
private void setMinimumProperties() {
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.KEYSTORE, tlsConfiguration.getKeystorePath());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.KEYSTORE_PASSWORD, tlsConfiguration.getKeystorePassword());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.TRUSTSTORE, tlsConfiguration.getTruststorePath());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.TRUSTSTORE_PASSWORD, tlsConfiguration.getTruststorePassword());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.KEYSTORE, keyStorePath.toString());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.KEYSTORE_PASSWORD, KEY_STORE_PASS);
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.KEYSTORE_TYPE, keyStoreType);
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.TRUSTSTORE, keyStorePath.toString());
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.TRUSTSTORE_PASSWORD, KEY_STORE_PASS);
|
||||
runner.setProperty(service, StandardRestrictedSSLContextService.TRUSTSTORE_TYPE, keyStoreType);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,302 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.ssl;
|
||||
|
||||
import org.apache.nifi.components.ValidationContext;
|
||||
import org.apache.nifi.components.ValidationResult;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.util.MockProcessContext;
|
||||
import org.apache.nifi.util.MockValidationContext;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
import org.apache.nifi.util.TestRunners;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class StandardSSLContextServiceTest {
|
||||
private static final String SERVICE_PROPERTY = "SSL Context Svc ID";
|
||||
|
||||
private static final String SERVICE_ID = StandardSSLContextService.class.getSimpleName();
|
||||
|
||||
private static final String ALIAS = "entry-0";
|
||||
|
||||
private static final String KEY_STORE_EXTENSION = ".p12";
|
||||
|
||||
private static final String KEY_STORE_PASS = UUID.randomUUID().toString();
|
||||
|
||||
private static final String TRUST_STORE_PASS = UUID.randomUUID().toString();
|
||||
|
||||
@TempDir
|
||||
private static Path keyStoreDirectory;
|
||||
|
||||
private static String keyStoreType;
|
||||
|
||||
private static Path keyStorePath;
|
||||
|
||||
private static Path trustStorePath;
|
||||
|
||||
@BeforeAll
|
||||
public static void setConfiguration() throws Exception {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder().build();
|
||||
keyStore.setKeyEntry(ALIAS, keyPair.getPrivate(), KEY_STORE_PASS.toCharArray(), new Certificate[]{certificate});
|
||||
|
||||
keyStorePath = Files.createTempFile(keyStoreDirectory, "keyStore", KEY_STORE_EXTENSION);
|
||||
try (OutputStream outputStream = Files.newOutputStream(keyStorePath)) {
|
||||
keyStore.store(outputStream, KEY_STORE_PASS.toCharArray());
|
||||
}
|
||||
|
||||
keyStoreType = keyStore.getType().toUpperCase();
|
||||
|
||||
final KeyStore trustStore = new EphemeralKeyStoreBuilder().addCertificate(certificate).build();
|
||||
trustStorePath = Files.createTempFile(keyStoreDirectory, "trustStore", KEY_STORE_EXTENSION);
|
||||
try (OutputStream outputStream = Files.newOutputStream(trustStorePath)) {
|
||||
trustStore.store(outputStream, TRUST_STORE_PASS.toCharArray());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotValidMissingProperties() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final StandardSSLContextService service = new StandardSSLContextService();
|
||||
|
||||
runner.addControllerService(SERVICE_ID, service, Map.of());
|
||||
runner.assertNotValid(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotValidMissingKeyStoreType() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final StandardSSLContextService service = new StandardSSLContextService();
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
|
||||
properties.put(StandardSSLContextService.KEYSTORE.getName(), keyStorePath.toString());
|
||||
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEY_STORE_PASS);
|
||||
runner.addControllerService(SERVICE_ID, service, properties);
|
||||
runner.assertNotValid(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotValidMissingTrustStoreType() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final StandardSSLContextService service = new StandardSSLContextService();
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
|
||||
properties.put(StandardSSLContextService.KEYSTORE.getName(), keyStorePath.toString());
|
||||
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEY_STORE_PASS);
|
||||
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), keyStoreType);
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), trustStorePath.toString());
|
||||
runner.addControllerService(SERVICE_ID, service, properties);
|
||||
runner.assertNotValid(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNotValidIncorrectPassword() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final StandardSSLContextService service = new StandardSSLContextService();
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
|
||||
runner.addControllerService(SERVICE_ID, service, properties);
|
||||
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), keyStorePath.toString());
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), String.class.getSimpleName());
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), keyStoreType);
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), keyStorePath.toString());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), String.class.getSimpleName());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), keyStoreType);
|
||||
|
||||
runner.assertNotValid(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testShouldFailToAddControllerServiceWithNonExistentFiles() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final StandardSSLContextService service = new StandardSSLContextService();
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
|
||||
properties.put(StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/DOES-NOT-EXIST.jks");
|
||||
properties.put(StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEY_STORE_PASS);
|
||||
properties.put(StandardSSLContextService.KEYSTORE_TYPE.getName(), keyStoreType);
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), keyStorePath.toString());
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), keyStorePath.toString());
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), keyStoreType);
|
||||
runner.addControllerService(SERVICE_ID, service, properties);
|
||||
|
||||
runner.assertNotValid(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateContext() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final StandardSSLContextService service = new StandardSSLContextService();
|
||||
|
||||
runner.addControllerService(SERVICE_ID, service);
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), keyStorePath.toString());
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEY_STORE_PASS);
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), keyStoreType);
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), trustStorePath.toString());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), TRUST_STORE_PASS);
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), keyStoreType);
|
||||
runner.enableControllerService(service);
|
||||
|
||||
runner.setProperty(SERVICE_PROPERTY, SERVICE_ID);
|
||||
runner.assertValid(service);
|
||||
|
||||
final SSLContext sslContext = service.createContext();
|
||||
assertNotNull(sslContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateContextExpressionLanguageProperties() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final StandardSSLContextService service = new StandardSSLContextService();
|
||||
|
||||
runner.addControllerService(SERVICE_ID, service);
|
||||
runner.setEnvironmentVariableValue("keystore", keyStorePath.toString());
|
||||
runner.setEnvironmentVariableValue("truststore", trustStorePath.toString());
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), "${keystore}");
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEY_STORE_PASS);
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), keyStoreType);
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), "${truststore}");
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), TRUST_STORE_PASS);
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), keyStoreType);
|
||||
runner.enableControllerService(service);
|
||||
|
||||
runner.setProperty(SERVICE_PROPERTY, SERVICE_ID);
|
||||
runner.assertValid(service);
|
||||
|
||||
final SSLContext sslContext = service.createContext();
|
||||
assertNotNull(sslContext);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPropertiesChanged() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final StandardSSLContextService service = new StandardSSLContextService();
|
||||
|
||||
runner.addControllerService(SERVICE_ID, service);
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), keyStorePath.toString());
|
||||
runner.setProperty(service, StandardSSLContextService.KEY_PASSWORD.getName(), KEY_STORE_PASS);
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEY_STORE_PASS);
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), keyStoreType);
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), trustStorePath.toString());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), TRUST_STORE_PASS);
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), keyStoreType);
|
||||
runner.enableControllerService(service);
|
||||
|
||||
runner.setProperty(SERVICE_PROPERTY, SERVICE_ID);
|
||||
runner.assertValid(service);
|
||||
|
||||
runner.disableControllerService(service);
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), "src/test/resources/DOES-NOT-EXIST.jks");
|
||||
runner.assertNotValid(service);
|
||||
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), keyStorePath.toString());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), String.class.getSimpleName());
|
||||
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), TRUST_STORE_PASS);
|
||||
runner.enableControllerService(service);
|
||||
runner.assertValid(service);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testValidPropertiesChangedValidationExpired(@TempDir final Path tempDir) throws InitializationException, IOException {
|
||||
final Path tempKeyStore = tempDir.resolve("keyStore.p12");
|
||||
final Path tempTrustStore = tempDir.resolve("trustStore.p12");
|
||||
|
||||
Files.copy(keyStorePath, tempKeyStore, StandardCopyOption.REPLACE_EXISTING);
|
||||
Files.copy(trustStorePath, tempTrustStore, StandardCopyOption.REPLACE_EXISTING);
|
||||
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final StandardSSLContextService service = new StandardSSLContextService();
|
||||
runner.addControllerService(SERVICE_ID, service);
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE.getName(), tempKeyStore.toString());
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_PASSWORD.getName(), KEY_STORE_PASS);
|
||||
runner.setProperty(service, StandardSSLContextService.KEYSTORE_TYPE.getName(), keyStoreType);
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE.getName(), tempTrustStore.toString());
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), TRUST_STORE_PASS);
|
||||
runner.setProperty(service, StandardSSLContextService.TRUSTSTORE_TYPE.getName(), keyStoreType);
|
||||
runner.enableControllerService(service);
|
||||
|
||||
runner.setProperty(SERVICE_PROPERTY, SERVICE_ID);
|
||||
runner.assertValid(service);
|
||||
|
||||
final MockProcessContext processContext = (MockProcessContext) runner.getProcessContext();
|
||||
// This service does not use the state manager
|
||||
final ValidationContext validationContext = new MockValidationContext(processContext, null);
|
||||
|
||||
// Even though the keystore file is no longer present, because no property changed, the cached result is still valid
|
||||
Collection<ValidationResult> validationResults = service.customValidate(validationContext);
|
||||
assertTrue(validationResults.isEmpty(), "validation results is not empty");
|
||||
|
||||
// Have to exhaust the cached result by checking n-1 more times
|
||||
for (int i = 2; i < service.getValidationCacheExpiration(); i++) {
|
||||
validationResults = service.customValidate(validationContext);
|
||||
assertTrue(validationResults.isEmpty(), "validation results is not empty");
|
||||
}
|
||||
|
||||
validationResults = service.customValidate(validationContext);
|
||||
assertFalse(validationResults.isEmpty(), "validation results is empty");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateContextTrustStoreWithoutKeyStore() throws InitializationException {
|
||||
final TestRunner runner = TestRunners.newTestRunner(TestProcessor.class);
|
||||
final StandardSSLContextService service = new StandardSSLContextService();
|
||||
final Map<String, String> properties = new HashMap<>();
|
||||
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE.getName(), trustStorePath.toString());
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE_PASSWORD.getName(), TRUST_STORE_PASS);
|
||||
properties.put(StandardSSLContextService.TRUSTSTORE_TYPE.getName(), keyStoreType);
|
||||
runner.addControllerService(SERVICE_ID, service, properties);
|
||||
runner.enableControllerService(service);
|
||||
|
||||
runner.setProperty(SERVICE_PROPERTY, SERVICE_ID);
|
||||
runner.assertValid();
|
||||
|
||||
final SSLContext sslContext = service.createContext();
|
||||
assertNotNull(sslContext);
|
||||
}
|
||||
}
|
|
@ -49,7 +49,13 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<artifactId>nifi-security-ssl</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-cert-builder</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
|
|
@ -16,18 +16,29 @@
|
|||
*/
|
||||
package org.apache.nifi.web.client.provider.service;
|
||||
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
|
@ -36,7 +47,16 @@ import static org.mockito.Mockito.when;
|
|||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class StandardKeyManagerProviderTest {
|
||||
static TlsConfiguration tlsConfiguration;
|
||||
@TempDir
|
||||
static Path keyStoreDirectory;
|
||||
|
||||
static Path keyStorePath;
|
||||
|
||||
static String keyStoreType;
|
||||
|
||||
static String keyStorePass;
|
||||
|
||||
private static final String KEY_STORE_EXTENSION = ".p12";
|
||||
|
||||
@Mock
|
||||
SSLContextService sslContextService;
|
||||
|
@ -44,8 +64,21 @@ class StandardKeyManagerProviderTest {
|
|||
StandardKeyManagerProvider provider;
|
||||
|
||||
@BeforeAll
|
||||
static void setTlsConfiguration() {
|
||||
tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
static void setKeyStore() throws Exception {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.build();
|
||||
final char[] protectionParameter = new char[]{};
|
||||
|
||||
keyStorePath = Files.createTempFile(keyStoreDirectory, StandardKeyManagerProviderTest.class.getSimpleName(), KEY_STORE_EXTENSION);
|
||||
try (OutputStream outputStream = Files.newOutputStream(keyStorePath)) {
|
||||
keyStore.store(outputStream, protectionParameter);
|
||||
}
|
||||
|
||||
keyStoreType = keyStore.getType();
|
||||
keyStorePass = new String(protectionParameter);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
|
@ -65,9 +98,9 @@ class StandardKeyManagerProviderTest {
|
|||
@Test
|
||||
void testGetKeyManager() {
|
||||
when(sslContextService.isKeyStoreConfigured()).thenReturn(true);
|
||||
when(sslContextService.getKeyStoreType()).thenReturn(tlsConfiguration.getKeystoreType().getType());
|
||||
when(sslContextService.getKeyStoreFile()).thenReturn(tlsConfiguration.getKeystorePath());
|
||||
when(sslContextService.getKeyStorePassword()).thenReturn(tlsConfiguration.getKeystorePassword());
|
||||
when(sslContextService.getKeyStoreType()).thenReturn(keyStoreType);
|
||||
when(sslContextService.getKeyStoreFile()).thenReturn(keyStorePath.toString());
|
||||
when(sslContextService.getKeyStorePassword()).thenReturn(keyStorePass);
|
||||
|
||||
final Optional<X509KeyManager> keyManager = provider.getKeyManager(sslContextService);
|
||||
|
||||
|
|
|
@ -24,10 +24,11 @@ import okhttp3.mockwebserver.RecordedRequest;
|
|||
import org.apache.nifi.proxy.ProxyConfiguration;
|
||||
import org.apache.nifi.proxy.ProxyConfigurationService;
|
||||
import org.apache.nifi.reporting.InitializationException;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsException;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardTrustManagerBuilder;
|
||||
import org.apache.nifi.security.util.TlsPlatform;
|
||||
import org.apache.nifi.ssl.SSLContextService;
|
||||
import org.apache.nifi.util.NoOpProcessor;
|
||||
import org.apache.nifi.util.TestRunner;
|
||||
|
@ -47,10 +48,17 @@ import org.mockito.junit.jupiter.MockitoExtension;
|
|||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Proxy;
|
||||
import java.net.URI;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
|
@ -88,8 +96,6 @@ class StandardWebClientServiceProviderTest {
|
|||
|
||||
private static final boolean TUNNEL_PROXY_DISABLED = false;
|
||||
|
||||
static TlsConfiguration tlsConfiguration;
|
||||
|
||||
static SSLContext sslContext;
|
||||
|
||||
static X509TrustManager trustManager;
|
||||
|
@ -107,10 +113,21 @@ class StandardWebClientServiceProviderTest {
|
|||
StandardWebClientServiceProvider provider;
|
||||
|
||||
@BeforeAll
|
||||
static void setTlsConfiguration() throws TlsException {
|
||||
tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
sslContext = SslContextFactory.createSslContext(tlsConfiguration);
|
||||
trustManager = SslContextFactory.getX509TrustManager(tlsConfiguration);
|
||||
static void setTlsConfiguration() throws Exception {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.build();
|
||||
final char[] protectionParameter = new char[]{};
|
||||
|
||||
sslContext = new StandardSslContextBuilder()
|
||||
.trustStore(keyStore)
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(protectionParameter)
|
||||
.build();
|
||||
|
||||
trustManager = new StandardTrustManagerBuilder().trustStore(keyStore).build();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
|
@ -163,7 +180,7 @@ class StandardWebClientServiceProviderTest {
|
|||
@Test
|
||||
void testGetWebServiceClientSslContextServiceConfiguredGetUri() throws InitializationException, InterruptedException {
|
||||
when(sslContextService.getIdentifier()).thenReturn(SSL_CONTEXT_SERVICE_ID);
|
||||
when(sslContextService.getSslAlgorithm()).thenReturn(tlsConfiguration.getProtocol());
|
||||
when(sslContextService.getSslAlgorithm()).thenReturn(TlsPlatform.getLatestProtocol());
|
||||
when(sslContextService.createTrustManager()).thenReturn(trustManager);
|
||||
|
||||
runner.addControllerService(SSL_CONTEXT_SERVICE_ID, sslContextService);
|
||||
|
|
|
@ -35,12 +35,12 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<artifactId>nifi-security-identity</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-identity</artifactId>
|
||||
<artifactId>nifi-security-ssl</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
|
||||
|
|
|
@ -16,6 +16,12 @@
|
|||
*/
|
||||
package org.apache.nifi.ldap;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyStore;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -32,10 +38,8 @@ import org.apache.nifi.authentication.exception.InvalidLoginCredentialsException
|
|||
import org.apache.nifi.authentication.exception.ProviderCreationException;
|
||||
import org.apache.nifi.authentication.exception.ProviderDestructionException;
|
||||
import org.apache.nifi.configuration.NonComponentConfigurationContext;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.StandardTlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsException;
|
||||
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.util.FormatUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
@ -244,24 +248,93 @@ public class LdapProvider implements LoginIdentityProvider {
|
|||
}
|
||||
}
|
||||
|
||||
public static SSLContext getConfiguredSslContext(final NonComponentConfigurationContext configurationContext) {
|
||||
final String rawKeystore = configurationContext.getProperty("TLS - Keystore");
|
||||
final String rawKeystorePassword = configurationContext.getProperty("TLS - Keystore Password");
|
||||
// TODO: Should support different key password
|
||||
final String rawKeystoreType = configurationContext.getProperty("TLS - Keystore Type");
|
||||
final String rawTruststore = configurationContext.getProperty("TLS - Truststore");
|
||||
final String rawTruststorePassword = configurationContext.getProperty("TLS - Truststore Password");
|
||||
final String rawTruststoreType = configurationContext.getProperty("TLS - Truststore Type");
|
||||
final String rawProtocol = configurationContext.getProperty("TLS - Protocol");
|
||||
private static SSLContext getConfiguredSslContext(final NonComponentConfigurationContext configurationContext) {
|
||||
final String rawProtocol = configurationContext.getProperty(ProviderProperty.TLS_PROTOCOL.getProperty());
|
||||
|
||||
SSLContext sslContext = null;
|
||||
try {
|
||||
TlsConfiguration tlsConfiguration = new StandardTlsConfiguration(rawKeystore, rawKeystorePassword, null, rawKeystoreType,
|
||||
rawTruststore, rawTruststorePassword, rawTruststoreType, rawProtocol);
|
||||
return SslContextFactory.createSslContext(tlsConfiguration);
|
||||
} catch (TlsException e) {
|
||||
final KeyStore trustStore = getTrustStore(configurationContext);
|
||||
if (trustStore == null) {
|
||||
logger.debug("Truststore not configured");
|
||||
} else {
|
||||
final StandardSslContextBuilder sslContextBuilder = new StandardSslContextBuilder();
|
||||
sslContextBuilder.protocol(rawProtocol);
|
||||
sslContextBuilder.trustStore(trustStore);
|
||||
|
||||
final KeyStore keyStore = getKeyStore(configurationContext);
|
||||
if (keyStore == null) {
|
||||
logger.debug("Keystore not configured");
|
||||
} else {
|
||||
final String keyStorePassword = configurationContext.getProperty(ProviderProperty.KEYSTORE_PASSWORD.getProperty());
|
||||
final char[] keyPassword = keyStorePassword.toCharArray();
|
||||
|
||||
sslContextBuilder.keyStore(keyStore);
|
||||
sslContextBuilder.keyPassword(keyPassword);
|
||||
sslContext = sslContextBuilder.build();
|
||||
}
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
logger.error("Encountered an error configuring TLS for LDAP identity provider: {}", e.getLocalizedMessage());
|
||||
throw new ProviderCreationException("Error configuring TLS for LDAP identity provider", e);
|
||||
}
|
||||
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
private static KeyStore getKeyStore(final NonComponentConfigurationContext configurationContext) throws IOException {
|
||||
final String rawKeystore = configurationContext.getProperty(ProviderProperty.KEYSTORE.getProperty());
|
||||
final String rawKeystorePassword = configurationContext.getProperty(ProviderProperty.KEYSTORE_PASSWORD.getProperty());
|
||||
final String rawKeystoreType = configurationContext.getProperty(ProviderProperty.KEYSTORE_TYPE.getProperty());
|
||||
|
||||
final KeyStore keyStore;
|
||||
|
||||
if (rawKeystore == null || rawKeystore.isBlank()) {
|
||||
keyStore = null;
|
||||
} else if (rawKeystorePassword == null) {
|
||||
throw new ProviderCreationException("Keystore Password not configured");
|
||||
} else {
|
||||
final StandardKeyStoreBuilder builder = new StandardKeyStoreBuilder();
|
||||
builder.type(rawKeystoreType);
|
||||
|
||||
final char[] keyStorePassword = rawKeystorePassword.toCharArray();
|
||||
builder.password(keyStorePassword);
|
||||
|
||||
final Path trustStorePath = Paths.get(rawKeystore);
|
||||
try (InputStream trustStoreStream = Files.newInputStream(trustStorePath)) {
|
||||
builder.inputStream(trustStoreStream);
|
||||
keyStore = builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
private static KeyStore getTrustStore(final NonComponentConfigurationContext configurationContext) throws IOException {
|
||||
final String rawTruststore = configurationContext.getProperty(ProviderProperty.TRUSTSTORE.getProperty());
|
||||
final String rawTruststorePassword = configurationContext.getProperty(ProviderProperty.TRUSTSTORE_PASSWORD.getProperty());
|
||||
final String rawTruststoreType = configurationContext.getProperty(ProviderProperty.TRUSTSTORE_TYPE.getProperty());
|
||||
|
||||
final KeyStore trustStore;
|
||||
|
||||
if (rawTruststore == null || rawTruststore.isBlank()) {
|
||||
trustStore = null;
|
||||
} else if (rawTruststorePassword == null) {
|
||||
throw new ProviderCreationException("Truststore Password not configured");
|
||||
} else {
|
||||
final StandardKeyStoreBuilder builder = new StandardKeyStoreBuilder();
|
||||
builder.type(rawTruststoreType);
|
||||
|
||||
final char[] trustStorePassword = rawTruststorePassword.toCharArray();
|
||||
builder.password(trustStorePassword);
|
||||
|
||||
final Path trustStorePath = Paths.get(rawTruststore);
|
||||
try (InputStream trustStoreStream = Files.newInputStream(trustStorePath)) {
|
||||
builder.inputStream(trustStoreStream);
|
||||
trustStore = builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
return trustStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -278,8 +351,7 @@ public class LdapProvider implements LoginIdentityProvider {
|
|||
// use dn if configured
|
||||
if (IdentityStrategy.USE_DN.equals(identityStrategy)) {
|
||||
// attempt to get the ldap user details to get the DN
|
||||
if (authentication.getPrincipal() instanceof LdapUserDetails) {
|
||||
final LdapUserDetails userDetails = (LdapUserDetails) authentication.getPrincipal();
|
||||
if (authentication.getPrincipal() instanceof LdapUserDetails userDetails) {
|
||||
return new AuthenticationResponse(userDetails.getDn(), credentials.getUsername(), expiration, issuer);
|
||||
} else {
|
||||
logger.warn("Unable to determine user DN for {}, using username.", authentication.getName());
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.ldap;
|
||||
|
||||
public enum ProviderProperty {
|
||||
KEYSTORE("TLS - Keystore"),
|
||||
|
||||
KEYSTORE_PASSWORD("TLS - Keystore Password"),
|
||||
|
||||
KEYSTORE_TYPE("TLS - Keystore Type"),
|
||||
|
||||
TRUSTSTORE("TLS - Truststore"),
|
||||
|
||||
TRUSTSTORE_PASSWORD("TLS - Truststore Password"),
|
||||
|
||||
TRUSTSTORE_TYPE("TLS - Truststore Type"),
|
||||
|
||||
TLS_PROTOCOL("TLS - Protocol");
|
||||
|
||||
private final String property;
|
||||
|
||||
ProviderProperty(final String property) {
|
||||
this.property = property;
|
||||
}
|
||||
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
}
|
|
@ -34,11 +34,10 @@ import org.apache.nifi.authorization.util.IdentityMappingUtil;
|
|||
import org.apache.nifi.components.PropertyValue;
|
||||
import org.apache.nifi.ldap.LdapAuthenticationStrategy;
|
||||
import org.apache.nifi.ldap.LdapsSocketFactory;
|
||||
import org.apache.nifi.ldap.ProviderProperty;
|
||||
import org.apache.nifi.ldap.ReferralStrategy;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.StandardTlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsException;
|
||||
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.util.FormatUtils;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -66,6 +65,12 @@ import javax.naming.NamingException;
|
|||
import javax.naming.directory.Attribute;
|
||||
import javax.naming.directory.SearchControls;
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
import java.security.KeyStore;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
|
@ -494,7 +499,7 @@ public class LdapUserGroupProvider implements UserGroupProvider {
|
|||
}
|
||||
|
||||
do {
|
||||
userList.addAll(ldapTemplate.search(userSearchBase, userFilter.encode(), userControls, new AbstractContextMapper<User>() {
|
||||
userList.addAll(ldapTemplate.search(userSearchBase, userFilter.encode(), userControls, new AbstractContextMapper<>() {
|
||||
@Override
|
||||
protected User doMapFromContext(DirContextOperations ctx) {
|
||||
// get the user
|
||||
|
@ -567,7 +572,7 @@ public class LdapUserGroupProvider implements UserGroupProvider {
|
|||
}
|
||||
|
||||
do {
|
||||
groupList.addAll(ldapTemplate.search(groupSearchBase, groupFilter.encode(), groupControls, new AbstractContextMapper<Group>() {
|
||||
groupList.addAll(ldapTemplate.search(groupSearchBase, groupFilter.encode(), groupControls, new AbstractContextMapper<>() {
|
||||
@Override
|
||||
protected Group doMapFromContext(DirContextOperations ctx) {
|
||||
// get the group identity
|
||||
|
@ -830,22 +835,91 @@ public class LdapUserGroupProvider implements UserGroupProvider {
|
|||
}
|
||||
|
||||
private SSLContext getConfiguredSslContext(final AuthorizerConfigurationContext configurationContext) {
|
||||
final String rawKeystore = configurationContext.getProperty("TLS - Keystore").getValue();
|
||||
final String rawKeystorePassword = configurationContext.getProperty("TLS - Keystore Password").getValue();
|
||||
final String rawKeystoreType = configurationContext.getProperty("TLS - Keystore Type").getValue();
|
||||
final String rawTruststore = configurationContext.getProperty("TLS - Truststore").getValue();
|
||||
final String rawTruststorePassword = configurationContext.getProperty("TLS - Truststore Password").getValue();
|
||||
final String rawTruststoreType = configurationContext.getProperty("TLS - Truststore Type").getValue();
|
||||
final String rawProtocol = configurationContext.getProperty("TLS - Protocol").getValue();
|
||||
final String rawProtocol = configurationContext.getProperty(ProviderProperty.TLS_PROTOCOL.getProperty()).getValue();
|
||||
|
||||
SSLContext sslContext = null;
|
||||
try {
|
||||
TlsConfiguration tlsConfiguration = new StandardTlsConfiguration(rawKeystore, rawKeystorePassword, null, rawKeystoreType,
|
||||
rawTruststore, rawTruststorePassword, rawTruststoreType, rawProtocol);
|
||||
return SslContextFactory.createSslContext(tlsConfiguration);
|
||||
} catch (TlsException e) {
|
||||
final KeyStore trustStore = getTrustStore(configurationContext);
|
||||
if (trustStore == null) {
|
||||
logger.debug("Truststore not configured");
|
||||
} else {
|
||||
final StandardSslContextBuilder sslContextBuilder = new StandardSslContextBuilder();
|
||||
sslContextBuilder.protocol(rawProtocol);
|
||||
sslContextBuilder.trustStore(trustStore);
|
||||
|
||||
final KeyStore keyStore = getKeyStore(configurationContext);
|
||||
if (keyStore == null) {
|
||||
logger.debug("Keystore not configured");
|
||||
} else {
|
||||
final String keyStorePassword = configurationContext.getProperty(ProviderProperty.KEYSTORE_PASSWORD.getProperty()).getValue();
|
||||
final char[] keyPassword = keyStorePassword.toCharArray();
|
||||
|
||||
sslContextBuilder.keyStore(keyStore);
|
||||
sslContextBuilder.keyPassword(keyPassword);
|
||||
sslContext = sslContextBuilder.build();
|
||||
}
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
logger.error("Encountered an error configuring TLS for LDAP user group provider: {}", e.getLocalizedMessage());
|
||||
throw new ProviderCreationException("Error configuring TLS for LDAP user group provider", e);
|
||||
}
|
||||
|
||||
return sslContext;
|
||||
}
|
||||
|
||||
private static KeyStore getKeyStore(final AuthorizerConfigurationContext configurationContext) throws IOException {
|
||||
final String rawKeystore = configurationContext.getProperty(ProviderProperty.KEYSTORE.getProperty()).getValue();
|
||||
final String rawKeystorePassword = configurationContext.getProperty(ProviderProperty.KEYSTORE_PASSWORD.getProperty()).getValue();
|
||||
final String rawKeystoreType = configurationContext.getProperty(ProviderProperty.KEYSTORE_TYPE.getProperty()).getValue();
|
||||
|
||||
final KeyStore keyStore;
|
||||
|
||||
if (rawKeystore == null || rawKeystore.isBlank()) {
|
||||
keyStore = null;
|
||||
} else if (rawKeystorePassword == null) {
|
||||
throw new ProviderCreationException("Keystore Password not configured");
|
||||
} else {
|
||||
final StandardKeyStoreBuilder builder = new StandardKeyStoreBuilder();
|
||||
builder.type(rawKeystoreType);
|
||||
|
||||
final char[] keyStorePassword = rawKeystorePassword.toCharArray();
|
||||
builder.password(keyStorePassword);
|
||||
|
||||
final Path trustStorePath = Paths.get(rawKeystore);
|
||||
try (InputStream trustStoreStream = Files.newInputStream(trustStorePath)) {
|
||||
builder.inputStream(trustStoreStream);
|
||||
keyStore = builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
return keyStore;
|
||||
}
|
||||
|
||||
private static KeyStore getTrustStore(final AuthorizerConfigurationContext configurationContext) throws IOException {
|
||||
final String rawTruststore = configurationContext.getProperty(ProviderProperty.TRUSTSTORE.getProperty()).getValue();
|
||||
final String rawTruststorePassword = configurationContext.getProperty(ProviderProperty.TRUSTSTORE_PASSWORD.getProperty()).getValue();
|
||||
final String rawTruststoreType = configurationContext.getProperty(ProviderProperty.TRUSTSTORE_TYPE.getProperty()).getValue();
|
||||
|
||||
final KeyStore trustStore;
|
||||
|
||||
if (rawTruststore == null || rawTruststore.isBlank()) {
|
||||
trustStore = null;
|
||||
} else if (rawTruststorePassword == null) {
|
||||
throw new ProviderCreationException("Truststore Password not configured");
|
||||
} else {
|
||||
final StandardKeyStoreBuilder builder = new StandardKeyStoreBuilder();
|
||||
builder.type(rawTruststoreType);
|
||||
|
||||
final char[] trustStorePassword = rawTruststorePassword.toCharArray();
|
||||
builder.password(trustStorePassword);
|
||||
|
||||
final Path trustStorePath = Paths.get(rawTruststore);
|
||||
try (InputStream trustStoreStream = Files.newInputStream(trustStorePath)) {
|
||||
builder.inputStream(trustStoreStream);
|
||||
trustStore = builder.build();
|
||||
}
|
||||
}
|
||||
|
||||
return trustStore;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -87,12 +87,6 @@
|
|||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-utils</artifactId>
|
||||
|
@ -264,10 +258,6 @@
|
|||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-utils</artifactId>
|
||||
|
|
|
@ -75,11 +75,6 @@
|
|||
<artifactId>nifi-security-utils-api</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<scope>compile</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-utils</artifactId>
|
||||
|
|
|
@ -56,11 +56,6 @@
|
|||
<artifactId>nifi-framework-core-api</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-cert</artifactId>
|
||||
|
|
|
@ -151,11 +151,6 @@
|
|||
<artifactId>nifi-framework-cluster-protocol</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-site-to-site-client</artifactId>
|
||||
|
|
|
@ -127,11 +127,6 @@
|
|||
<artifactId>nifi-property-encryptor</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-xml-processing</artifactId>
|
||||
|
|
|
@ -81,11 +81,6 @@
|
|||
<artifactId>nifi-security-utils-api</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-nar-utils</artifactId>
|
||||
|
@ -340,6 +335,12 @@
|
|||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-cert-builder</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-handler</artifactId>
|
||||
|
|
|
@ -53,9 +53,9 @@ import org.apache.nifi.controller.repository.claim.ContentClaim;
|
|||
import org.apache.nifi.controller.repository.claim.ResourceClaim;
|
||||
import org.apache.nifi.events.EventReporter;
|
||||
import org.apache.nifi.provenance.ProvenanceRepository;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.Timeout;
|
||||
|
@ -64,6 +64,7 @@ import org.mockito.invocation.InvocationOnMock;
|
|||
import org.mockito.stubbing.Answer;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
@ -71,10 +72,16 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.KeyStoreException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.UnrecoverableKeyException;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
@ -185,10 +192,21 @@ public class LoadBalancedQueueIT {
|
|||
clientRepoRecords = Collections.synchronizedList(new ArrayList<>());
|
||||
clientFlowFileRepo = createFlowFileRepository(clientRepoRecords);
|
||||
|
||||
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
sslContext = SslContextFactory.createSslContext(tlsConfiguration);
|
||||
sslContext = getSslContext();
|
||||
}
|
||||
|
||||
private SSLContext getSslContext() throws GeneralSecurityException {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.build();
|
||||
return new StandardSslContextBuilder()
|
||||
.trustStore(keyStore)
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(new char[]{})
|
||||
.build();
|
||||
}
|
||||
|
||||
private ContentClaim createContentClaim(final byte[] bytes) {
|
||||
final ResourceClaim resourceClaim = mock(ResourceClaim.class);
|
||||
|
|
|
@ -29,9 +29,9 @@ import io.netty.channel.SimpleChannelInboundHandler;
|
|||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.ssl.SslHandler;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.security.util.TlsPlatform;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
@ -40,6 +40,7 @@ import org.junit.jupiter.api.condition.EnabledIf;
|
|||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLEngine;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.InetSocketAddress;
|
||||
|
@ -49,6 +50,12 @@ import java.net.SocketException;
|
|||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
|
@ -92,8 +99,18 @@ public class TestPeerChannel {
|
|||
|
||||
@BeforeAll
|
||||
public static void setConfiguration() throws GeneralSecurityException {
|
||||
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
sslContext = SslContextFactory.createSslContext(tlsConfiguration);
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.build();
|
||||
final char[] protectionParameter = new char[]{};
|
||||
|
||||
sslContext = new StandardSslContextBuilder()
|
||||
.trustStore(keyStore)
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(protectionParameter)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -17,10 +17,9 @@
|
|||
package org.apache.nifi.controller.queue.clustered.server;
|
||||
|
||||
import org.apache.nifi.events.EventReporter;
|
||||
import org.apache.nifi.security.util.SslContextFactory;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsException;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
|
@ -31,11 +30,17 @@ import javax.net.ssl.SSLContext;
|
|||
import javax.net.ssl.SSLException;
|
||||
import javax.net.ssl.SSLSocket;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
|
@ -60,9 +65,19 @@ class ConnectionLoadBalanceServerTest {
|
|||
EventReporter eventReporter;
|
||||
|
||||
@BeforeAll
|
||||
static void setSslContext() throws TlsException {
|
||||
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
sslContext = SslContextFactory.createSslContext(tlsConfiguration);
|
||||
static void setSslContext() throws Exception {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.build();
|
||||
final char[] protectionParameter = new char[]{};
|
||||
|
||||
sslContext = new StandardSslContextBuilder()
|
||||
.trustStore(keyStore)
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(protectionParameter)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
@ -1,190 +0,0 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.apache.nifi.controller.state.server;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.zookeeper.server.ServerCnxnFactory;
|
||||
import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
// This class tests the behaviors involved with the ZooKeeperStateServer::create method. The servers are not started,
|
||||
// and TLS connections are not used.
|
||||
public class TestZooKeeperStateServerConfigurations {
|
||||
private static final String INSECURE_ZOOKEEPER_PROPS = getPath("insecure.zookeeper.properties");
|
||||
private static final String SECURE_ZOOKEEPER_PROPS = getPath("secure.zookeeper.properties");
|
||||
private static final String ZOOKEEPER_PROPERTIES_FILE_KEY = "nifi.state.management.embedded.zookeeper.properties";
|
||||
private static final String ZOOKEEPER_CNXN_FACTORY = "org.apache.zookeeper.server.NettyServerCnxnFactory";
|
||||
|
||||
private static final Map<String, String> INSECURE_PROPS = new HashMap<String, String>() {{
|
||||
put(ZOOKEEPER_PROPERTIES_FILE_KEY, INSECURE_ZOOKEEPER_PROPS);
|
||||
}};
|
||||
|
||||
private static final Map<String, String> SECURE_NIFI_PROPS = new HashMap<>();
|
||||
|
||||
private static final Map<String, String> INSECURE_NIFI_PROPS = new HashMap<String, String>() {{
|
||||
putAll(INSECURE_PROPS);
|
||||
put(NiFiProperties.WEB_HTTP_PORT, "8080");
|
||||
put(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, "false");
|
||||
}};
|
||||
|
||||
private NiFiProperties secureNiFiProps;
|
||||
private NiFiProperties insecureNiFiProps;
|
||||
private QuorumPeerConfig secureQuorumPeerConfig;
|
||||
private QuorumPeerConfig insecureQuorumPeerConfig;
|
||||
private Properties secureZooKeeperProps;
|
||||
private Properties insecureZooKeeperProps;
|
||||
|
||||
private static TlsConfiguration tlsConfiguration;
|
||||
|
||||
@BeforeAll
|
||||
public static void setTlsConfiguration() {
|
||||
tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
|
||||
SECURE_NIFI_PROPS.put(NiFiProperties.STATE_MANAGEMENT_ZOOKEEPER_PROPERTIES, SECURE_ZOOKEEPER_PROPS);
|
||||
SECURE_NIFI_PROPS.put(NiFiProperties.WEB_HTTPS_PORT, "8443");
|
||||
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
|
||||
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
|
||||
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_KEYSTORE_PASSWD, tlsConfiguration.getKeystorePassword());
|
||||
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
|
||||
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
|
||||
SECURE_NIFI_PROPS.put(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, tlsConfiguration.getTruststorePassword());
|
||||
SECURE_NIFI_PROPS.put(NiFiProperties.ZOOKEEPER_CLIENT_SECURE, "true");
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
public void setupWithValidProperties() throws IOException, QuorumPeerConfig.ConfigException {
|
||||
|
||||
// Secure properties setup
|
||||
secureNiFiProps = NiFiProperties.createBasicNiFiProperties(null, SECURE_NIFI_PROPS);
|
||||
assertNotNull(secureNiFiProps);
|
||||
|
||||
// This shows that a ZooKeeper server is created from valid NiFi properties:
|
||||
final ZooKeeperStateServer secureZooKeeperStateServer = ZooKeeperStateServer.create(secureNiFiProps);
|
||||
assertNotNull(secureZooKeeperStateServer);
|
||||
|
||||
secureQuorumPeerConfig = secureZooKeeperStateServer.getQuorumPeerConfig();
|
||||
assertNotNull(secureQuorumPeerConfig);
|
||||
|
||||
secureZooKeeperProps = new Properties();
|
||||
secureZooKeeperProps.load( FileUtils.openInputStream(new File(SECURE_ZOOKEEPER_PROPS)));
|
||||
assertNotNull(secureZooKeeperProps);
|
||||
|
||||
// Insecure properties setup
|
||||
insecureNiFiProps = NiFiProperties.createBasicNiFiProperties(null, INSECURE_NIFI_PROPS);
|
||||
assertNotNull(insecureNiFiProps);
|
||||
|
||||
// This shows that a ZooKeeper server is created from valid NiFi properties:
|
||||
final ZooKeeperStateServer insecureZooKeeperStateServer = ZooKeeperStateServer.create(insecureNiFiProps);
|
||||
assertNotNull(insecureZooKeeperStateServer);
|
||||
|
||||
insecureQuorumPeerConfig = insecureZooKeeperStateServer.getQuorumPeerConfig();
|
||||
assertNotNull(insecureQuorumPeerConfig);
|
||||
|
||||
insecureZooKeeperProps = new Properties();
|
||||
insecureZooKeeperProps.load(FileUtils.openInputStream(new File(INSECURE_ZOOKEEPER_PROPS)));
|
||||
assertNotNull(insecureZooKeeperProps);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
public void clearConnectionProperties() {
|
||||
Collections.unmodifiableSet(System.getProperties().stringPropertyNames()).stream()
|
||||
.filter(name -> name.startsWith("zookeeper."))
|
||||
.forEach(System::clearProperty);
|
||||
}
|
||||
|
||||
// This test shows that a ZooKeeperStateServer cannot be created from empty NiFi properties.
|
||||
@Test
|
||||
public void testCreateFromEmptyNiFiProperties() throws IOException, QuorumPeerConfig.ConfigException {
|
||||
final NiFiProperties emptyProps = NiFiProperties.createBasicNiFiProperties(null, new HashMap<>());
|
||||
|
||||
assertNotNull(emptyProps);
|
||||
assertNull(ZooKeeperStateServer.create(emptyProps));
|
||||
}
|
||||
|
||||
// This test shows that a ZooKeeperStateServer can be created from insecure NiFi properties.
|
||||
@Test
|
||||
public void testCreateFromValidInsecureNiFiProperties() throws IOException, QuorumPeerConfig.ConfigException {
|
||||
final NiFiProperties insecureProps = NiFiProperties.createBasicNiFiProperties(null, INSECURE_PROPS);
|
||||
final ZooKeeperStateServer server = ZooKeeperStateServer.create(insecureProps);
|
||||
|
||||
assertNotNull(server);
|
||||
assertNotNull(server.getQuorumPeerConfig().getClientPortAddress());
|
||||
}
|
||||
|
||||
// This test shows that the client can specify a secure port and that port is used:
|
||||
@Test
|
||||
public void testCreateWithSpecifiedSecureClientPort() throws IOException, QuorumPeerConfig.ConfigException {
|
||||
final NiFiProperties secureProps = NiFiProperties.createBasicNiFiProperties(null, new HashMap<String, String>() {{
|
||||
putAll(SECURE_NIFI_PROPS);
|
||||
put(ZOOKEEPER_PROPERTIES_FILE_KEY, SECURE_ZOOKEEPER_PROPS);
|
||||
}});
|
||||
|
||||
final ZooKeeperStateServer server = ZooKeeperStateServer.create(secureProps);
|
||||
assertNotNull(server);
|
||||
|
||||
final QuorumPeerConfig config = server.getQuorumPeerConfig();
|
||||
assertEquals(secureZooKeeperProps.getProperty("secureClientPort"), String.valueOf(config.getSecureClientPortAddress().getPort()));
|
||||
}
|
||||
|
||||
// This shows that a secure NiFi with an secure ZooKeeper will not have an insecure client address or port:
|
||||
@Test
|
||||
public void testCreateRemovesInsecureClientPort() {
|
||||
assertNotNull(secureZooKeeperProps.getProperty("secureClientPort"));
|
||||
assertNotEquals("", secureZooKeeperProps.getProperty("clientPort"));
|
||||
assertNull(secureQuorumPeerConfig.getClientPortAddress());
|
||||
}
|
||||
|
||||
// This test shows that a connection class is set when none is specified (QuorumPeerConfig::parseProperties sets the System property):
|
||||
@Test
|
||||
public void testCreateWithUnspecifiedConnectionClass() {
|
||||
assertEquals(org.apache.zookeeper.server.NettyServerCnxnFactory.class.getName(), System.getProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY));
|
||||
}
|
||||
|
||||
// This test shows that a specified connection class is honored (QuorumPeerConfig::parseProperties sets the System property):
|
||||
@Test
|
||||
public void testCreateWithSpecifiedConnectionClass() throws IOException, QuorumPeerConfig.ConfigException {
|
||||
final NiFiProperties secureProps = NiFiProperties.createBasicNiFiProperties(null, new HashMap<String, String>() {{
|
||||
putAll(SECURE_NIFI_PROPS);
|
||||
put(ZOOKEEPER_PROPERTIES_FILE_KEY, SECURE_ZOOKEEPER_PROPS);
|
||||
}});
|
||||
|
||||
assertNotNull(ZooKeeperStateServer.create(secureProps));
|
||||
assertEquals(ZOOKEEPER_CNXN_FACTORY, System.getProperty(ServerCnxnFactory.ZOOKEEPER_SERVER_CNXN_FACTORY));
|
||||
}
|
||||
|
||||
private static String getPath(String path) {
|
||||
return new File("src/test/resources/TestZooKeeperStateServerConfigurations/" + path).getAbsolutePath();
|
||||
}
|
||||
}
|
|
@ -32,11 +32,6 @@
|
|||
<artifactId>nifi-security-utils-api</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-framework-nar-utils</artifactId>
|
||||
|
|
|
@ -26,11 +26,6 @@
|
|||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-nar-utils</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils-api</artifactId>
|
||||
|
|
|
@ -120,8 +120,9 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<artifactId>nifi-security-cert-builder</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
*/
|
||||
package org.apache.nifi.web.server.connector;
|
||||
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.util.TlsException;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.eclipse.jetty.alpn.server.ALPNServerConnectionFactory;
|
||||
import org.eclipse.jetty.http2.server.HTTP2ServerConnectionFactory;
|
||||
|
@ -29,8 +29,19 @@ import org.eclipse.jetty.server.SslConnectionFactory;
|
|||
import org.eclipse.jetty.util.ssl.SslContextFactory;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.Properties;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
@ -52,12 +63,40 @@ class FrameworkServerConnectorFactoryTest {
|
|||
|
||||
private static final String INCLUDED_CIPHER_SUITE_PATTERN = ".*AES_256_GCM.*";
|
||||
|
||||
private static TlsConfiguration tlsConfiguration;
|
||||
private static final String ALIAS = "entry-0";
|
||||
|
||||
private static final String KEY_STORE_EXTENSION = ".p12";
|
||||
|
||||
private static final String KEY_STORE_PASS = FrameworkServerConnectorFactoryTest.class.getName();
|
||||
|
||||
@TempDir
|
||||
private static Path keyStoreDirectory;
|
||||
|
||||
private static String keyStoreType;
|
||||
|
||||
private static Path keyStorePath;
|
||||
|
||||
private static SSLContext sslContext;
|
||||
|
||||
@BeforeAll
|
||||
static void setTlsConfiguration() {
|
||||
final TemporaryKeyStoreBuilder builder = new TemporaryKeyStoreBuilder();
|
||||
tlsConfiguration = builder.build();
|
||||
static void setConfiguration() throws Exception {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder().build();
|
||||
keyStore.setKeyEntry(ALIAS, keyPair.getPrivate(), KEY_STORE_PASS.toCharArray(), new Certificate[]{certificate});
|
||||
|
||||
keyStorePath = Files.createTempFile(keyStoreDirectory, FrameworkServerConnectorFactoryTest.class.getSimpleName(), KEY_STORE_EXTENSION);
|
||||
try (OutputStream outputStream = Files.newOutputStream(keyStorePath)) {
|
||||
keyStore.store(outputStream, KEY_STORE_PASS.toCharArray());
|
||||
}
|
||||
|
||||
keyStoreType = keyStore.getType().toUpperCase();
|
||||
|
||||
sslContext = new StandardSslContextBuilder()
|
||||
.keyStore(keyStore)
|
||||
.trustStore(keyStore)
|
||||
.keyPassword(KEY_STORE_PASS.toCharArray())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -140,13 +179,13 @@ class FrameworkServerConnectorFactoryTest {
|
|||
private Properties getHttpsProperties() {
|
||||
final Properties serverProperties = new Properties();
|
||||
serverProperties.setProperty(NiFiProperties.WEB_HTTPS_PORT, Integer.toString(HTTPS_PORT));
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD, tlsConfiguration.getKeystorePassword());
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_KEY_PASSWD, tlsConfiguration.getKeyPassword());
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, tlsConfiguration.getTruststorePassword());
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_KEYSTORE, keyStorePath.toString());
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE, keyStoreType);
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD, KEY_STORE_PASS);
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_KEY_PASSWD, KEY_STORE_PASS);
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE, keyStorePath.toString());
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, keyStoreType);
|
||||
serverProperties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, KEY_STORE_PASS);
|
||||
return serverProperties;
|
||||
}
|
||||
|
||||
|
@ -154,12 +193,7 @@ class FrameworkServerConnectorFactoryTest {
|
|||
final NiFiProperties properties = getProperties(serverProperties);
|
||||
final Server server = new Server();
|
||||
final FrameworkServerConnectorFactory factory = new FrameworkServerConnectorFactory(server, properties);
|
||||
try {
|
||||
final SSLContext sslContext = org.apache.nifi.security.util.SslContextFactory.createSslContext(tlsConfiguration);
|
||||
factory.setSslContext(sslContext);
|
||||
} catch (final TlsException e) {
|
||||
throw new IllegalStateException("Failed to create SSL Context", e);
|
||||
}
|
||||
factory.setSslContext(sslContext);
|
||||
return factory;
|
||||
}
|
||||
|
||||
|
|
|
@ -443,11 +443,6 @@
|
|||
<artifactId>nifi-framework-core</artifactId>
|
||||
<scope>provided</scope> <!-- expected to be provided by parent classloader -->
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-utils</artifactId>
|
||||
|
|
|
@ -101,11 +101,6 @@
|
|||
<artifactId>nifi-web-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-identity</artifactId>
|
||||
|
@ -317,6 +312,18 @@
|
|||
<artifactId>mockwebserver</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-cert-builder</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-ssl</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-python-framework-api</artifactId>
|
||||
|
|
|
@ -20,12 +20,11 @@ import okhttp3.HttpUrl;
|
|||
import okhttp3.mockwebserver.MockResponse;
|
||||
import okhttp3.mockwebserver.MockWebServer;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardKeyManagerBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardSslContextBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardTrustManagerBuilder;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.apache.nifi.web.security.saml2.SamlConfigurationException;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
|
@ -36,16 +35,20 @@ import org.springframework.security.saml2.provider.service.registration.Saml2Mes
|
|||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import javax.net.ssl.SSLSocketFactory;
|
||||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
import javax.net.ssl.X509KeyManager;
|
||||
import javax.net.ssl.X509TrustManager;
|
||||
import java.io.FileInputStream;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.Objects;
|
||||
import java.util.Properties;
|
||||
|
||||
|
@ -109,13 +112,18 @@ class StandardRegistrationBuilderProviderTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testGetRegistrationBuilderHttpsUrl() throws IOException {
|
||||
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
final X509KeyManager keyManager = getKeyManager(tlsConfiguration);
|
||||
final X509TrustManager trustManager = getTrustManager(tlsConfiguration);
|
||||
void testGetRegistrationBuilderHttpsUrl() throws Exception {
|
||||
final KeyStore keyStore = getKeyStore();
|
||||
final char[] protectionParameter = new char[]{};
|
||||
|
||||
final X509KeyManager keyManager = new StandardKeyManagerBuilder()
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(protectionParameter)
|
||||
.build();
|
||||
final X509TrustManager trustManager = new StandardTrustManagerBuilder().trustStore(keyStore).build();
|
||||
final SSLContext sslContext = new StandardSslContextBuilder()
|
||||
.keyManager(keyManager)
|
||||
.keyPassword(tlsConfiguration.getKeyPassword().toCharArray())
|
||||
.keyPassword(protectionParameter)
|
||||
.trustManager(trustManager)
|
||||
.build();
|
||||
|
||||
|
@ -127,7 +135,7 @@ class StandardRegistrationBuilderProviderTest {
|
|||
mockWebServer.enqueue(response);
|
||||
final String metadataUrl = getMetadataUrl();
|
||||
|
||||
final NiFiProperties properties = getProperties(metadataUrl, tlsConfiguration);
|
||||
final NiFiProperties properties = getPropertiesTrustStoreStrategy(metadataUrl);
|
||||
|
||||
assertRegistrationFound(properties, keyManager, trustManager);
|
||||
}
|
||||
|
@ -145,31 +153,12 @@ class StandardRegistrationBuilderProviderTest {
|
|||
assertEquals(Saml2MessageBinding.POST, registration.getAssertionConsumerServiceBinding());
|
||||
}
|
||||
|
||||
private X509ExtendedKeyManager getKeyManager(final TlsConfiguration tlsConfiguration) throws IOException {
|
||||
try (InputStream inputStream = new FileInputStream(tlsConfiguration.getKeystorePath())) {
|
||||
final KeyStore keyStore = new StandardKeyStoreBuilder()
|
||||
.inputStream(inputStream)
|
||||
.password(tlsConfiguration.getKeystorePassword().toCharArray())
|
||||
.type(tlsConfiguration.getKeystoreType().getType())
|
||||
.build();
|
||||
|
||||
return new StandardKeyManagerBuilder()
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(tlsConfiguration.getFunctionalKeyPassword().toCharArray())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
private X509ExtendedTrustManager getTrustManager(final TlsConfiguration tlsConfiguration) throws IOException {
|
||||
try (InputStream inputStream = new FileInputStream(tlsConfiguration.getTruststorePath())) {
|
||||
final KeyStore trustStore = new StandardKeyStoreBuilder()
|
||||
.inputStream(inputStream)
|
||||
.password(tlsConfiguration.getTruststorePassword().toCharArray())
|
||||
.type(tlsConfiguration.getTruststoreType().getType())
|
||||
.build();
|
||||
|
||||
return new StandardTrustManagerBuilder().trustStore(trustStore).build();
|
||||
}
|
||||
private KeyStore getKeyStore() throws GeneralSecurityException {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
return new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.build();
|
||||
}
|
||||
|
||||
private NiFiProperties getProperties(final String metadataUrl) {
|
||||
|
@ -178,19 +167,11 @@ class StandardRegistrationBuilderProviderTest {
|
|||
return NiFiProperties.createBasicNiFiProperties(null, properties);
|
||||
}
|
||||
|
||||
private NiFiProperties getProperties(final String metadataUrl, final TlsConfiguration tlsConfiguration) {
|
||||
private NiFiProperties getPropertiesTrustStoreStrategy(final String metadataUrl) {
|
||||
final Properties properties = new Properties();
|
||||
properties.setProperty(NiFiProperties.SECURITY_USER_SAML_IDP_METADATA_URL, metadataUrl);
|
||||
properties.setProperty(NiFiProperties.SECURITY_USER_SAML_HTTP_CLIENT_TRUSTSTORE_STRATEGY, StandardRegistrationBuilderProvider.NIFI_TRUST_STORE_STRATEGY);
|
||||
|
||||
properties.setProperty(NiFiProperties.SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
|
||||
properties.setProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
|
||||
properties.setProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD, tlsConfiguration.getKeystorePassword());
|
||||
properties.setProperty(NiFiProperties.SECURITY_KEY_PASSWD, tlsConfiguration.getKeyPassword());
|
||||
properties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
|
||||
properties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
|
||||
properties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, tlsConfiguration.getTruststorePassword());
|
||||
|
||||
return NiFiProperties.createBasicNiFiProperties(null, properties);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,11 +16,10 @@
|
|||
*/
|
||||
package org.apache.nifi.web.security.saml2.registration;
|
||||
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardKeyManagerBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardKeyStoreBuilder;
|
||||
import org.apache.nifi.security.ssl.StandardTrustManagerBuilder;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.util.NiFiProperties;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.opensaml.xmlsec.signature.support.SignatureConstants;
|
||||
|
@ -30,12 +29,14 @@ import org.springframework.security.saml2.provider.service.registration.RelyingP
|
|||
import javax.net.ssl.X509ExtendedKeyManager;
|
||||
import javax.net.ssl.X509ExtendedTrustManager;
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
@ -75,12 +76,17 @@ class StandardRelyingPartyRegistrationRepositoryTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
void testFindByRegistrationIdSingleLogoutEnabled() throws IOException {
|
||||
final TlsConfiguration tlsConfiguration = new TemporaryKeyStoreBuilder().build();
|
||||
final X509ExtendedKeyManager keyManager = getKeyManager(tlsConfiguration);
|
||||
final X509ExtendedTrustManager trustManager = getTrustManager(tlsConfiguration);
|
||||
void testFindByRegistrationIdSingleLogoutEnabled() throws Exception {
|
||||
final KeyStore keyStore = getKeyStore();
|
||||
final char[] protectionParameter = new char[]{};
|
||||
|
||||
final NiFiProperties properties = getSingleLogoutProperties(tlsConfiguration);
|
||||
final X509ExtendedKeyManager keyManager = new StandardKeyManagerBuilder()
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(protectionParameter)
|
||||
.build();
|
||||
final X509ExtendedTrustManager trustManager = new StandardTrustManagerBuilder().trustStore(keyStore).build();
|
||||
|
||||
final NiFiProperties properties = getSingleLogoutProperties();
|
||||
final StandardRelyingPartyRegistrationRepository repository = new StandardRelyingPartyRegistrationRepository(properties, keyManager, trustManager);
|
||||
|
||||
final RelyingPartyRegistration registration = repository.findByRegistrationId(Saml2RegistrationProperty.REGISTRATION_ID.getProperty());
|
||||
|
@ -128,19 +134,11 @@ class StandardRelyingPartyRegistrationRepositoryTest {
|
|||
return NiFiProperties.createBasicNiFiProperties(null, properties);
|
||||
}
|
||||
|
||||
private NiFiProperties getSingleLogoutProperties(final TlsConfiguration tlsConfiguration) {
|
||||
private NiFiProperties getSingleLogoutProperties() {
|
||||
final Properties properties = getStandardProperties();
|
||||
properties.setProperty(NiFiProperties.SECURITY_USER_SAML_SINGLE_LOGOUT_ENABLED, Boolean.TRUE.toString());
|
||||
properties.setProperty(NiFiProperties.SECURITY_USER_SAML_SIGNATURE_ALGORITHM, SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA512);
|
||||
|
||||
properties.setProperty(NiFiProperties.SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
|
||||
properties.setProperty(NiFiProperties.SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
|
||||
properties.setProperty(NiFiProperties.SECURITY_KEYSTORE_PASSWD, tlsConfiguration.getKeystorePassword());
|
||||
properties.setProperty(NiFiProperties.SECURITY_KEY_PASSWD, tlsConfiguration.getKeyPassword());
|
||||
properties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
|
||||
properties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
|
||||
properties.setProperty(NiFiProperties.SECURITY_TRUSTSTORE_PASSWD, tlsConfiguration.getTruststorePassword());
|
||||
|
||||
return NiFiProperties.createBasicNiFiProperties(null, properties);
|
||||
}
|
||||
|
||||
|
@ -157,30 +155,11 @@ class StandardRelyingPartyRegistrationRepositoryTest {
|
|||
return resource.toString();
|
||||
}
|
||||
|
||||
private X509ExtendedKeyManager getKeyManager(final TlsConfiguration tlsConfiguration) throws IOException {
|
||||
try (InputStream inputStream = new FileInputStream(tlsConfiguration.getKeystorePath())) {
|
||||
final KeyStore keyStore = new StandardKeyStoreBuilder()
|
||||
.inputStream(inputStream)
|
||||
.password(tlsConfiguration.getKeystorePassword().toCharArray())
|
||||
.type(tlsConfiguration.getKeystoreType().getType())
|
||||
.build();
|
||||
|
||||
return new StandardKeyManagerBuilder()
|
||||
.keyStore(keyStore)
|
||||
.keyPassword(tlsConfiguration.getFunctionalKeyPassword().toCharArray())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
private X509ExtendedTrustManager getTrustManager(final TlsConfiguration tlsConfiguration) throws IOException {
|
||||
try (InputStream inputStream = new FileInputStream(tlsConfiguration.getTruststorePath())) {
|
||||
final KeyStore trustStore = new StandardKeyStoreBuilder()
|
||||
.inputStream(inputStream)
|
||||
.password(tlsConfiguration.getTruststorePassword().toCharArray())
|
||||
.type(tlsConfiguration.getTruststoreType().getType())
|
||||
.build();
|
||||
|
||||
return new StandardTrustManagerBuilder().trustStore(trustStore).build();
|
||||
}
|
||||
private KeyStore getKeyStore() throws GeneralSecurityException {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
return new EphemeralKeyStoreBuilder()
|
||||
.addPrivateKeyEntry(new KeyStore.PrivateKeyEntry(keyPair.getPrivate(), new Certificate[]{certificate}))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -90,7 +90,7 @@
|
|||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<artifactId>nifi-security-cert-builder</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
|
|
@ -17,15 +17,26 @@
|
|||
package org.apache.nifi.registry.jetty.connector;
|
||||
|
||||
import org.apache.nifi.registry.properties.NiFiRegistryProperties;
|
||||
import org.apache.nifi.security.util.TemporaryKeyStoreBuilder;
|
||||
import org.apache.nifi.security.util.TlsConfiguration;
|
||||
import org.apache.nifi.security.cert.builder.StandardCertificateBuilder;
|
||||
import org.apache.nifi.security.ssl.EphemeralKeyStoreBuilder;
|
||||
import org.apache.nifi.util.StringUtils;
|
||||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import javax.security.auth.x500.X500Principal;
|
||||
import java.io.OutputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.KeyStore;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
|
@ -46,15 +57,34 @@ class ApplicationServerConnectorFactoryTest {
|
|||
|
||||
private static final String LOCALHOST = "127.0.0.1";
|
||||
|
||||
private static final String PROPRIETARY_TRUST_STORE_TYPE = "JKS";
|
||||
private static final String ALIAS = "entry-0";
|
||||
|
||||
static TlsConfiguration tlsConfiguration;
|
||||
private static final String KEY_STORE_EXTENSION = ".p12";
|
||||
|
||||
private static final String KEY_STORE_PASS = ApplicationServerConnectorFactoryTest.class.getName();
|
||||
|
||||
@TempDir
|
||||
private static Path keyStoreDirectory;
|
||||
|
||||
private static String keyStoreType;
|
||||
|
||||
private static Path keyStorePath;
|
||||
|
||||
Server server;
|
||||
|
||||
@BeforeAll
|
||||
static void setTlsConfiguration() {
|
||||
tlsConfiguration = new TemporaryKeyStoreBuilder().trustStoreType(PROPRIETARY_TRUST_STORE_TYPE).build();
|
||||
static void setConfiguration() throws Exception {
|
||||
final KeyPair keyPair = KeyPairGenerator.getInstance("RSA").generateKeyPair();
|
||||
final X509Certificate certificate = new StandardCertificateBuilder(keyPair, new X500Principal("CN=localhost"), Duration.ofHours(1)).build();
|
||||
final KeyStore keyStore = new EphemeralKeyStoreBuilder().build();
|
||||
keyStore.setKeyEntry(ALIAS, keyPair.getPrivate(), KEY_STORE_PASS.toCharArray(), new Certificate[]{certificate});
|
||||
|
||||
keyStorePath = Files.createTempFile(keyStoreDirectory, ApplicationServerConnectorFactoryTest.class.getSimpleName(), KEY_STORE_EXTENSION);
|
||||
try (OutputStream outputStream = Files.newOutputStream(keyStorePath)) {
|
||||
keyStore.store(outputStream, KEY_STORE_PASS.toCharArray());
|
||||
}
|
||||
|
||||
keyStoreType = keyStore.getType().toUpperCase();
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
|
@ -144,12 +174,12 @@ class ApplicationServerConnectorFactoryTest {
|
|||
|
||||
private Properties getSecurityProperties() {
|
||||
final Properties securityProperties = new Properties();
|
||||
securityProperties.put(NiFiRegistryProperties.SECURITY_KEYSTORE, tlsConfiguration.getKeystorePath());
|
||||
securityProperties.put(NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE, tlsConfiguration.getKeystoreType().getType());
|
||||
securityProperties.put(NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD, tlsConfiguration.getKeystorePassword());
|
||||
securityProperties.put(NiFiRegistryProperties.SECURITY_TRUSTSTORE, tlsConfiguration.getTruststorePath());
|
||||
securityProperties.put(NiFiRegistryProperties.SECURITY_TRUSTSTORE_TYPE, tlsConfiguration.getTruststoreType().getType());
|
||||
securityProperties.put(NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD, tlsConfiguration.getTruststorePassword());
|
||||
securityProperties.put(NiFiRegistryProperties.SECURITY_KEYSTORE, keyStorePath.toString());
|
||||
securityProperties.put(NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE, keyStoreType);
|
||||
securityProperties.put(NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD, KEY_STORE_PASS);
|
||||
securityProperties.put(NiFiRegistryProperties.SECURITY_TRUSTSTORE, keyStorePath.toString());
|
||||
securityProperties.put(NiFiRegistryProperties.SECURITY_TRUSTSTORE_TYPE, keyStoreType);
|
||||
securityProperties.put(NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD, KEY_STORE_PASS);
|
||||
return securityProperties;
|
||||
}
|
||||
|
||||
|
|
|
@ -284,11 +284,6 @@
|
|||
<artifactId>nifi-bootstrap</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-security-utils</artifactId>
|
||||
<version>2.0.0-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.nifi</groupId>
|
||||
<artifactId>nifi-xml-processing</artifactId>
|
||||
|
|
Loading…
Reference in New Issue