From b753c1c72d85c482bcc8bf125023107d79f54ed4 Mon Sep 17 00:00:00 2001 From: exceptionfactory Date: Tue, 11 Oct 2022 14:29:42 -0500 Subject: [PATCH] NIFI-10625 Added support for HTTP/2 in Registry - Added nifi-security-ssl for generalized SSLContext creation - Removed static keystore and truststore test files from nifi-registry-jetty Signed-off-by: Nathan Gough This closes #6514. --- nifi-commons/nifi-security-ssl/pom.xml | 25 ++ .../ssl/BuilderConfigurationException.java | 41 ++++ .../nifi/security/ssl/KeyStoreBuilder.java | 31 +++ .../nifi/security/ssl/SslContextBuilder.java | 31 +++ .../security/ssl/StandardKeyStoreBuilder.java | 117 +++++++++ .../ssl/StandardSslContextBuilder.java | 171 +++++++++++++ .../ssl/StandardKeyStoreBuilderTest.java | 57 +++++ .../ssl/StandardSslContextBuilderTest.java | 78 ++++++ nifi-commons/pom.xml | 1 + nifi-registry/nifi-registry-assembly/pom.xml | 1 + .../main/asciidoc/administration-guide.adoc | 7 + .../nifi-registry-jetty/pom.xml | 29 +++ .../nifi/registry/jetty/JettyServer.java | 164 ++----------- .../ApplicationServerConnectorFactory.java | 231 ++++++++++++++++++ .../jetty/JettyServerGroovyTest.groovy | 165 ------------- ...ApplicationServerConnectorFactoryTest.java | 146 +++++++++++ .../resources/keystoreDifferentPasswords.jks | Bin 3128 -> 0 bytes .../test/resources/keystoreSamePassword.jks | Bin 3128 -> 0 bytes .../src/test/resources/truststore.jks | Bin 935 -> 0 bytes .../properties/NiFiRegistryProperties.java | 12 + .../resources/conf/nifi-registry.properties | 1 + nifi-registry/pom.xml | 12 + 22 files changed, 1008 insertions(+), 312 deletions(-) create mode 100644 nifi-commons/nifi-security-ssl/pom.xml create mode 100644 nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/BuilderConfigurationException.java create mode 100644 nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/KeyStoreBuilder.java create mode 100644 nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/SslContextBuilder.java create mode 100644 nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilder.java create mode 100644 nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardSslContextBuilder.java create mode 100644 nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilderTest.java create mode 100644 nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardSslContextBuilderTest.java create mode 100644 nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactory.java delete mode 100644 nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/groovy/org/apache/nifi/registry/jetty/JettyServerGroovyTest.groovy create mode 100644 nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactoryTest.java delete mode 100644 nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreDifferentPasswords.jks delete mode 100644 nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreSamePassword.jks delete mode 100644 nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/truststore.jks diff --git a/nifi-commons/nifi-security-ssl/pom.xml b/nifi-commons/nifi-security-ssl/pom.xml new file mode 100644 index 0000000000..b8340373fa --- /dev/null +++ b/nifi-commons/nifi-security-ssl/pom.xml @@ -0,0 +1,25 @@ + + + 4.0.0 + + org.apache.nifi + nifi-commons + 1.19.0-SNAPSHOT + + nifi-security-ssl + Shared TLS security components without additional dependencies + + diff --git a/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/BuilderConfigurationException.java b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/BuilderConfigurationException.java new file mode 100644 index 0000000000..74ad3a96e4 --- /dev/null +++ b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/BuilderConfigurationException.java @@ -0,0 +1,41 @@ +/* + * 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; + +/** + * Exception indicating runtime failure to create configured objects + */ +public class BuilderConfigurationException extends RuntimeException { + /** + * Builder Configuration Exception Constructor with standard properties + * + * @param message Exception Message + * @param cause Exception Cause + */ + public BuilderConfigurationException(final String message, final Throwable cause) { + super(message, cause); + } + + /** + * Builder Configuration Exception Constructor without Throwable cause + * + * @param message Exception Message + */ + public BuilderConfigurationException(final String message) { + super(message); + } +} diff --git a/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/KeyStoreBuilder.java b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/KeyStoreBuilder.java new file mode 100644 index 0000000000..ecc2911b7c --- /dev/null +++ b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/KeyStoreBuilder.java @@ -0,0 +1,31 @@ +/* + * 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; + +/** + * Builder interface for instances of java.security.KeyStore + */ +public interface KeyStoreBuilder { + /** + * Build Key Store based on configured properties + * + * @return Key Store + */ + KeyStore build(); +} diff --git a/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/SslContextBuilder.java b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/SslContextBuilder.java new file mode 100644 index 0000000000..620299ad76 --- /dev/null +++ b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/SslContextBuilder.java @@ -0,0 +1,31 @@ +/* + * 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 javax.net.ssl.SSLContext; + +/** + * Builder interface for instances of javax.net.ssl.SSLContext + */ +public interface SslContextBuilder { + /** + * Build SSLContext using configured properties + * + * @return SSLContext + */ + SSLContext build(); +} diff --git a/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilder.java b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilder.java new file mode 100644 index 0000000000..2f5271bd51 --- /dev/null +++ b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilder.java @@ -0,0 +1,117 @@ +/* + * 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.io.IOException; +import java.io.InputStream; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.cert.CertificateException; +import java.util.Objects; + +/** + * Standard implementation of Key Store Builder + */ +public class StandardKeyStoreBuilder implements KeyStoreBuilder { + private String provider; + + private String type = KeyStore.getDefaultType(); + + private InputStream inputStream; + + private char[] password; + + /** + * Build Key Store using configured properties + * + * @return Key Store + */ + @Override + public KeyStore build() { + final KeyStore keyStore = getKeyStore(); + + if (inputStream == null) { + throw new BuilderConfigurationException("Key Store InputStream not configured"); + } + + try { + keyStore.load(inputStream, password); + } catch (final IOException|NoSuchAlgorithmException|CertificateException e) { + throw new BuilderConfigurationException("Key Store loading failed", e); + } + + return keyStore; + } + + /** + * Set Key Store Provider for Key Store implementation + * + * @param provider Key Store Provider + * @return Builder + */ + public StandardKeyStoreBuilder provider(final String provider) { + this.provider = Objects.requireNonNull(provider, "Key Store Provider required"); + return this; + } + + /** + * Set Key Store Type defaults to platform configuration derived from KeyStore.getDefaultType() + * + * @param type Key Store Type + * @return Builder + */ + public StandardKeyStoreBuilder type(final String type) { + this.type = Objects.requireNonNull(type, "Key Store Type required"); + return this; + } + + /** + * Set Key Store Password + * + * @param password Key Store Password + * @return Builder + */ + public StandardKeyStoreBuilder password(final char[] password) { + this.password = Objects.requireNonNull(password, "Key Store Password required"); + return this; + } + + /** + * Set Key Store InputStream to be loaded + * + * @param inputStream Key Store InputStream + * @return Builder + */ + public StandardKeyStoreBuilder inputStream(final InputStream inputStream) { + this.inputStream = Objects.requireNonNull(inputStream, "Key Store InputStream required"); + return this; + } + + private KeyStore getKeyStore() { + try { + return provider == null ? KeyStore.getInstance(type) : KeyStore.getInstance(type, provider); + } catch (final KeyStoreException e) { + final String message = String.format("Key Store Type [%s] creation failed", type); + throw new BuilderConfigurationException(message, e); + } catch (final NoSuchProviderException e) { + final String message = String.format("Key Store Type [%s] Provider [%s] creation failed", type, provider); + throw new BuilderConfigurationException(message, e); + } + } +} diff --git a/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardSslContextBuilder.java b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardSslContextBuilder.java new file mode 100644 index 0000000000..9d82ea9c72 --- /dev/null +++ b/nifi-commons/nifi-security-ssl/src/main/java/org/apache/nifi/security/ssl/StandardSslContextBuilder.java @@ -0,0 +1,171 @@ +/* + * 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 javax.net.ssl.KeyManager; +import javax.net.ssl.KeyManagerFactory; +import javax.net.ssl.SSLContext; +import javax.net.ssl.TrustManager; +import javax.net.ssl.TrustManagerFactory; +import java.security.KeyManagementException; +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; +import java.security.UnrecoverableKeyException; +import java.util.Objects; + +/** + * Standard implementation of SSL Context Builder + */ +public class StandardSslContextBuilder implements SslContextBuilder { + private static final String DEFAULT_PROTOCOL = "TLS"; + + private String protocol = DEFAULT_PROTOCOL; + + private KeyStore keyStore; + + private char[] keyPassword; + + private KeyStore trustStore; + + /** + * Build and initialize an SSL Context using configured Key Manager and Trust Manager sources + * + * @return SSL Context + */ + @Override + public SSLContext build() { + final SSLContext sslContext = getSslContext(); + final SecureRandom secureRandom = new SecureRandom(); + final KeyManager[] keyManagers = getKeyManagers(); + final TrustManager[] trustManagers = getTrustManagers(); + + try { + sslContext.init(keyManagers, trustManagers, secureRandom); + } catch (final KeyManagementException e) { + throw new BuilderConfigurationException("SSLContext initialization failed", e); + } + + return sslContext; + } + + /** + * Set TLS Protocol defaults to TLS without a specific version number + * + * @param protocol TLS Protocol + * @return Builder + */ + public StandardSslContextBuilder protocol(final String protocol) { + this.protocol = Objects.requireNonNull(protocol, "Protocol required"); + return this; + } + + /** + * Set Key Store with Private Key and Certificate Entry + * + * @param keyStore Key Store + * @return Builder + */ + public StandardSslContextBuilder keyStore(final KeyStore keyStore) { + this.keyStore = Objects.requireNonNull(keyStore, "Key Store required"); + return this; + } + + /** + * Set Key Password for reading Private Key entries from Key Store + * + * @param keyPassword Key Password + * @return Builder + */ + public StandardSslContextBuilder keyPassword(final char[] keyPassword) { + this.keyPassword = Objects.requireNonNull(keyPassword, "Key Password required"); + return this; + } + + /** + * Set Trust Store with Certificate Entries + * + * @param trustStore Trust Store + * @return Builder + */ + public StandardSslContextBuilder trustStore(final KeyStore trustStore) { + this.trustStore = Objects.requireNonNull(trustStore, "Trust Store required"); + return this; + } + + private KeyManager[] getKeyManagers() { + final KeyManager[] keyManagers; + if (keyStore == null) { + keyManagers = null; + } else { + final KeyManagerFactory keyManagerFactory = getKeyManagerFactory(); + try { + keyManagerFactory.init(keyStore, keyPassword); + } catch (final KeyStoreException|NoSuchAlgorithmException|UnrecoverableKeyException e) { + throw new BuilderConfigurationException("Key Manager initialization failed", e); + } + keyManagers = keyManagerFactory.getKeyManagers(); + } + return keyManagers; + } + + private TrustManager[] getTrustManagers() { + final TrustManager[] trustManagers; + if (trustStore == null) { + trustManagers = null; + } else { + final TrustManagerFactory trustManagerFactory = getTrustManagerFactory(); + try { + trustManagerFactory.init(trustStore); + } catch (final KeyStoreException e) { + throw new BuilderConfigurationException("Trust Manager initialization failed", e); + } + trustManagers = trustManagerFactory.getTrustManagers(); + } + return trustManagers; + } + + private KeyManagerFactory getKeyManagerFactory() { + final String algorithm = KeyManagerFactory.getDefaultAlgorithm(); + try { + return KeyManagerFactory.getInstance(algorithm); + } catch (final NoSuchAlgorithmException e) { + final String message = String.format("KeyManagerFactory creation failed with algorithm [%s]", algorithm); + throw new BuilderConfigurationException(message, e); + } + } + + private TrustManagerFactory getTrustManagerFactory() { + final String algorithm = TrustManagerFactory.getDefaultAlgorithm(); + try { + return TrustManagerFactory.getInstance(algorithm); + } catch (final NoSuchAlgorithmException e) { + final String message = String.format("TrustManagerFactory creation failed with algorithm [%s]", algorithm); + throw new BuilderConfigurationException(message, e); + } + } + + private SSLContext getSslContext() { + try { + return SSLContext.getInstance(protocol); + } catch (final NoSuchAlgorithmException e) { + final String message = String.format("SSLContext creation failed with protocol [%s]", protocol); + throw new BuilderConfigurationException(message, e); + } + } +} diff --git a/nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilderTest.java b/nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilderTest.java new file mode 100644 index 0000000000..f18be8ebe1 --- /dev/null +++ b/nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardKeyStoreBuilderTest.java @@ -0,0 +1,57 @@ +/* + * 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.Test; + +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.KeyStore; +import java.util.UUID; + +import static java.nio.file.Files.createTempFile; +import static org.junit.jupiter.api.Assertions.assertNotNull; + +class StandardKeyStoreBuilderTest { + + private static final String TYPE = KeyStore.getDefaultType(); + + private static final char[] PASSWORD = UUID.randomUUID().toString().toCharArray(); + + @Test + void testBuild() throws Exception { + final Path path = createTempFile(StandardKeyStoreBuilderTest.class.getSimpleName(), TYPE); + path.toFile().deleteOnExit(); + + final KeyStore sourceKeyStore = KeyStore.getInstance(TYPE); + sourceKeyStore.load(null); + try (final OutputStream outputStream = Files.newOutputStream(path)) { + sourceKeyStore.store(outputStream, PASSWORD); + } + + final StandardKeyStoreBuilder builder = new StandardKeyStoreBuilder(); + builder.type(TYPE); + builder.password(PASSWORD); + try (final InputStream inputStream = Files.newInputStream(path)) { + builder.inputStream(inputStream); + final KeyStore keyStore = builder.build(); + assertNotNull(keyStore); + } + } +} diff --git a/nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardSslContextBuilderTest.java b/nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardSslContextBuilderTest.java new file mode 100644 index 0000000000..ea00afcb84 --- /dev/null +++ b/nifi-commons/nifi-security-ssl/src/test/java/org/apache/nifi/security/ssl/StandardSslContextBuilderTest.java @@ -0,0 +1,78 @@ +/* + * 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.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import javax.net.ssl.SSLContext; + +import java.security.KeyStore; + +import static org.junit.jupiter.api.Assertions.assertNotNull; + +@ExtendWith(MockitoExtension.class) +class StandardSslContextBuilderTest { + private static final String TLS_PROTOCOL = "TLS"; + + @Mock + KeyStore trustStore; + + @Test + void testBuild() { + final StandardSslContextBuilder builder = new StandardSslContextBuilder(); + + final SSLContext sslContext = builder.build(); + + assertNotNull(sslContext); + } + + @Test + void testBuildProtocol() { + final StandardSslContextBuilder builder = new StandardSslContextBuilder(); + builder.protocol(TLS_PROTOCOL); + + final SSLContext sslContext = builder.build(); + + assertNotNull(sslContext); + } + + @Test + void testBuildKeyStore() throws Exception { + final KeyStore keyStore = KeyStore.getInstance(KeyStore.getDefaultType()); + keyStore.load(null); + + final StandardSslContextBuilder builder = new StandardSslContextBuilder(); + builder.keyStore(keyStore); + + final SSLContext sslContext = builder.build(); + + assertNotNull(sslContext); + } + + @Test + void testBuildTrustStore() { + final StandardSslContextBuilder builder = new StandardSslContextBuilder(); + builder.trustStore(trustStore); + + final SSLContext sslContext = builder.build(); + + assertNotNull(sslContext); + } +} diff --git a/nifi-commons/pom.xml b/nifi-commons/pom.xml index 07769c4abe..5d4cebe621 100644 --- a/nifi-commons/pom.xml +++ b/nifi-commons/pom.xml @@ -57,6 +57,7 @@ nifi-security-kerberos nifi-security-kms nifi-security-socket-ssl + nifi-security-ssl nifi-security-utils-api nifi-security-utils nifi-single-user-utils diff --git a/nifi-registry/nifi-registry-assembly/pom.xml b/nifi-registry/nifi-registry-assembly/pom.xml index ea27204bb3..def70a61f9 100644 --- a/nifi-registry/nifi-registry-assembly/pom.xml +++ b/nifi-registry/nifi-registry-assembly/pom.xml @@ -165,6 +165,7 @@ 18080 + http/1.1 ./work/jetty 200 true diff --git a/nifi-registry/nifi-registry-core/nifi-registry-docs/src/main/asciidoc/administration-guide.adoc b/nifi-registry/nifi-registry-core/nifi-registry-docs/src/main/asciidoc/administration-guide.adoc index c14e86b242..86e37357bc 100644 --- a/nifi-registry/nifi-registry-core/nifi-registry-docs/src/main/asciidoc/administration-guide.adoc +++ b/nifi-registry/nifi-registry-core/nifi-registry-docs/src/main/asciidoc/administration-guide.adoc @@ -1013,6 +1013,13 @@ These properties pertain to the web-based User Interface. |`nifi.registry.web.http.port`|The HTTP port. The default value is `18080`. |`nifi.registry.web.https.host`|The HTTPS host. It is blank by default. |`nifi.registry.web.https.port`|The HTTPS port. It is blank by default. When configuring NiFi Registry to run securely, this port should be configured. +|`nifi.registry.web.https.application.protocols`|The space-separated list of application protocols supported when running with HTTPS enabled. + +The default value is `http/1.1`. + +The value can be set to `h2 http/1.1` to support Application Layer Protocol Negotiation (ALPN) for HTTP/2 or HTTP/1.1 based on client capabilities. + +The value can be set to `h2` to require HTTP/2 and disable HTTP/1.1. |`nifi.registry.web.jetty.working.directory`|The location of the Jetty working directory. The default value is `./work/jetty`. |`nifi.registry.web.jetty.threads`|The number of Jetty threads. The default value is `200`. |==== diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/pom.xml b/nifi-registry/nifi-registry-core/nifi-registry-jetty/pom.xml index 694e6ad348..60c2dbeb7b 100644 --- a/nifi-registry/nifi-registry-core/nifi-registry-jetty/pom.xml +++ b/nifi-registry/nifi-registry-core/nifi-registry-jetty/pom.xml @@ -28,6 +28,29 @@ nifi-registry-properties 1.19.0-SNAPSHOT + + org.apache.nifi + nifi-jetty-configuration + 1.19.0-SNAPSHOT + + + org.apache.nifi + nifi-security-ssl + 1.19.0-SNAPSHOT + + + org.apache.nifi + nifi-security-utils-api + 1.19.0-SNAPSHOT + + + org.eclipse.jetty.http2 + http2-server + + + org.eclipse.jetty + jetty-alpn-server + org.eclipse.jetty jetty-server @@ -62,5 +85,11 @@ apache-jstl compile + + org.apache.nifi + nifi-security-utils + 1.19.0-SNAPSHOT + test + diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java index 6d8b47ce1a..f868a39270 100644 --- a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java +++ b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/JettyServer.java @@ -17,6 +17,8 @@ package org.apache.nifi.registry.jetty; import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.jetty.configuration.connector.ServerConnectorFactory; +import org.apache.nifi.registry.jetty.connector.ApplicationServerConnectorFactory; import org.apache.nifi.registry.jetty.headers.ContentSecurityPolicyFilter; import org.apache.nifi.registry.jetty.headers.StrictTransportSecurityFilter; import org.apache.nifi.registry.jetty.headers.XFrameOptionsFilter; @@ -26,17 +28,12 @@ import org.apache.nifi.registry.security.crypto.CryptoKeyProvider; import org.eclipse.jetty.annotations.AnnotationConfiguration; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.HttpConfiguration; -import org.eclipse.jetty.server.HttpConnectionFactory; -import org.eclipse.jetty.server.SecureRequestCustomizer; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; -import org.eclipse.jetty.server.SslConnectionFactory; import org.eclipse.jetty.server.handler.HandlerCollection; import org.eclipse.jetty.servlet.DefaultServlet; import org.eclipse.jetty.servlet.FilterHolder; import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.eclipse.jetty.webapp.Configuration; import org.eclipse.jetty.webapp.JettyWebXmlConfiguration; @@ -72,16 +69,12 @@ public class JettyServer { private static final Logger logger = LoggerFactory.getLogger(JettyServer.class); private static final String WEB_DEFAULTS_XML = "org/apache/nifi-registry/web/webdefault.xml"; - private static final int HEADER_BUFFER_SIZE = 16 * 1024; // 16kb - private static final String CIPHER_SUITE_SEPARATOR_PATTERN = ",\\s*"; + private static final String ALL_PATHS = "/*"; - private static final FileFilter WAR_FILTER = new FileFilter() { - @Override - public boolean accept(File pathname) { - final String nameToTest = pathname.getName().toLowerCase(); - return nameToTest.endsWith(".war") && pathname.isFile(); - } + private static final FileFilter WAR_FILTER = pathname -> { + final String nameToTest = pathname.getName().toLowerCase(); + return nameToTest.endsWith(".war") && pathname.isFile(); }; private final NiFiRegistryProperties properties; @@ -89,9 +82,7 @@ public class JettyServer { private final String docsLocation; private final Server server; - private WebAppContext webUiContext; private WebAppContext webApiContext; - private WebAppContext webDocsContext; public JettyServer(final NiFiRegistryProperties properties, final CryptoKeyProvider cryptoKeyProvider, final String docsLocation) { final QueuedThreadPool threadPool = new QueuedThreadPool(properties.getWebThreads()); @@ -156,128 +147,9 @@ public class JettyServer { } private void configureConnectors() { - // create the http configuration - final HttpConfiguration httpConfiguration = new HttpConfiguration(); - httpConfiguration.setRequestHeaderSize(HEADER_BUFFER_SIZE); - httpConfiguration.setResponseHeaderSize(HEADER_BUFFER_SIZE); - httpConfiguration.setSendServerVersion(properties.shouldSendServerVersion()); - - if (properties.getPort() != null) { - final Integer port = properties.getPort(); - if (port < 0 || (int) Math.pow(2, 16) <= port) { - throw new IllegalStateException("Invalid HTTP port: " + port); - } - - logger.info("Configuring Jetty for HTTP on port: " + port); - - // create the connector - final ServerConnector http = new ServerConnector(server, new HttpConnectionFactory(httpConfiguration)); - - // set host and port - if (StringUtils.isNotBlank(properties.getHttpHost())) { - http.setHost(properties.getHttpHost()); - } - http.setPort(port); - - // add this connector - server.addConnector(http); - } else if (properties.getSslPort() != null) { - final Integer port = properties.getSslPort(); - if (port < 0 || (int) Math.pow(2, 16) <= port) { - throw new IllegalStateException("Invalid HTTPs port: " + port); - } - - if (StringUtils.isBlank(properties.getKeyStorePath())) { - throw new IllegalStateException(NiFiRegistryProperties.SECURITY_KEYSTORE - + " must be provided to configure Jetty for HTTPs"); - } - - logger.info("Configuring Jetty for HTTPs on port: " + port); - - // add some secure config - final HttpConfiguration httpsConfiguration = new HttpConfiguration(httpConfiguration); - httpsConfiguration.setSecureScheme("https"); - httpsConfiguration.setSecurePort(properties.getSslPort()); - httpsConfiguration.addCustomizer(new SecureRequestCustomizer()); - - // build the connector - final ServerConnector https = new ServerConnector(server, - new SslConnectionFactory(createSslContextFactory(), "http/1.1"), - new HttpConnectionFactory(httpsConfiguration)); - - // set host and port - if (StringUtils.isNotBlank(properties.getHttpsHost())) { - https.setHost(properties.getHttpsHost()); - } - https.setPort(port); - - // add this connector - server.addConnector(https); - } - } - - private static String[] getCipherSuites(final String cipherSuitesProperty) { - return cipherSuitesProperty.split(CIPHER_SUITE_SEPARATOR_PATTERN); - } - - private SslContextFactory createSslContextFactory() { - final SslContextFactory.Server contextFactory = new SslContextFactory.Server(); - - // if needClientAuth is false then set want to true so we can optionally use certs - if (properties.getNeedClientAuth()) { - logger.info("Setting Jetty's SSLContextFactory needClientAuth to true"); - contextFactory.setNeedClientAuth(true); - } else { - logger.info("Setting Jetty's SSLContextFactory wantClientAuth to true"); - contextFactory.setWantClientAuth(true); - } - - /* below code sets JSSE system properties when values are provided */ - // keystore properties - if (StringUtils.isNotBlank(properties.getKeyStorePath())) { - contextFactory.setKeyStorePath(properties.getKeyStorePath()); - } - if (StringUtils.isNotBlank(properties.getKeyStoreType())) { - contextFactory.setKeyStoreType(properties.getKeyStoreType()); - } - - - final String keystorePassword = properties.getKeyStorePassword(); - final String keyPassword = properties.getKeyPassword(); - - if (StringUtils.isEmpty(keystorePassword)) { - throw new IllegalArgumentException("The keystore password cannot be null or empty"); - } else { - // if no key password was provided, then assume the key password is the same as the keystore password. - final String defaultKeyPassword = (StringUtils.isBlank(keyPassword)) ? keystorePassword : keyPassword; - contextFactory.setKeyStorePassword(keystorePassword); - contextFactory.setKeyManagerPassword(defaultKeyPassword); - } - - // truststore properties - if (StringUtils.isNotBlank(properties.getTrustStorePath())) { - contextFactory.setTrustStorePath(properties.getTrustStorePath()); - } - if (StringUtils.isNotBlank(properties.getTrustStoreType())) { - contextFactory.setTrustStoreType(properties.getTrustStoreType()); - } - if (StringUtils.isNotBlank(properties.getTrustStorePassword())) { - contextFactory.setTrustStorePassword(properties.getTrustStorePassword()); - } - - final String includeCipherSuites = properties.getHttpsCipherSuitesInclude(); - if (StringUtils.isNotBlank(includeCipherSuites)) { - final String[] cipherSuites = getCipherSuites(includeCipherSuites); - contextFactory.setIncludeCipherSuites(cipherSuites); - } - - final String excludeCipherSuites = properties.getHttpsCipherSuitesExclude(); - if (StringUtils.isNotBlank(excludeCipherSuites)) { - final String[] cipherSuites = getCipherSuites(excludeCipherSuites); - contextFactory.setExcludeCipherSuites(cipherSuites); - } - - return contextFactory; + final ServerConnectorFactory serverConnectorFactory = new ApplicationServerConnectorFactory(server, properties); + final ServerConnector serverConnector = serverConnectorFactory.getServerConnector(); + server.addConnector(serverConnector); } private void loadWars() throws IOException { @@ -309,7 +181,7 @@ public class JettyServer { throw new IllegalStateException("Unable to locate NiFi Registry Web Docs"); } - webUiContext = loadWar(webUiWar, "/nifi-registry"); + WebAppContext webUiContext = loadWar(webUiWar, "/nifi-registry"); webUiContext.getInitParams().put("oidc-supported", String.valueOf(properties.isOidcEnabled())); webApiContext = loadWar(webApiWar, "/nifi-registry-api", getWebApiAdditionalClasspath()); @@ -322,7 +194,7 @@ public class JettyServer { webApiContext.setAttribute("org.eclipse.jetty.server.webapp.WebInfIncludeJarPattern", ".*/spring-[^/]*\\.jar$"); final String docsContextPath = "/nifi-registry-docs"; - webDocsContext = loadWar(webDocsWar, docsContextPath); + WebAppContext webDocsContext = loadWar(webDocsWar, docsContextPath); addDocsServlets(webDocsContext); final HandlerCollection handlers = new HandlerCollection(); @@ -371,20 +243,18 @@ public class JettyServer { webappContext.setMaxFormContentSize(600000); // add HTTP security headers to all responses - final String ALL_PATHS = "/*"; ArrayList> filters = new ArrayList<>(Arrays.asList(XFrameOptionsFilter.class, ContentSecurityPolicyFilter.class, XSSProtectionFilter.class)); - if(properties.isHTTPSConfigured()) { + if (properties.isHTTPSConfigured()) { filters.add(StrictTransportSecurityFilter.class); } - filters.forEach( (filter) -> addFilters(filter, ALL_PATHS, webappContext)); + filters.forEach( (filter) -> addFilters(filter, webappContext)); // start out assuming the system ClassLoader will be the parent, but if additional resources were specified then // inject a new ClassLoader in between the system and webapp ClassLoaders that contains the additional resources ClassLoader parentClassLoader = ClassLoader.getSystemClassLoader(); if (additionalResources != null && additionalResources.length > 0) { - URLClassLoader additionalClassLoader = new URLClassLoader(additionalResources, ClassLoader.getSystemClassLoader()); - parentClassLoader = additionalClassLoader; + parentClassLoader = new URLClassLoader(additionalResources, ClassLoader.getSystemClassLoader()); } webappContext.setClassLoader(new WebAppClassLoader(parentClassLoader, webappContext)); @@ -393,10 +263,10 @@ public class JettyServer { return webappContext; } - private void addFilters(Class clazz, String path, WebAppContext webappContext) { + private void addFilters(Class clazz, final WebAppContext webappContext) { FilterHolder holder = new FilterHolder(clazz); holder.setName(clazz.getSimpleName()); - webappContext.addFilter(holder, path, EnumSet.allOf(DispatcherType.class)); + webappContext.addFilter(holder, ALL_PATHS, EnumSet.allOf(DispatcherType.class)); } private URL[] getWebApiAdditionalClasspath() { @@ -451,7 +321,7 @@ public class JettyServer { logger.info("]"); } - return resources.toArray(new URL[resources.size()]); + return resources.toArray(new URL[0]); } private void addDocsServlets(WebAppContext docsContext) { diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactory.java b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactory.java new file mode 100644 index 0000000000..640c3cd96c --- /dev/null +++ b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/main/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactory.java @@ -0,0 +1,231 @@ +/* + * 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.registry.jetty.connector; + +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.nifi.jetty.configuration.connector.ApplicationLayerProtocol; +import org.apache.nifi.jetty.configuration.connector.StandardServerConnectorFactory; +import org.apache.nifi.registry.properties.NiFiRegistryProperties; +import org.apache.nifi.security.ssl.StandardKeyStoreBuilder; +import org.apache.nifi.security.ssl.StandardSslContextBuilder; +import org.apache.nifi.security.util.TlsPlatform; +import org.eclipse.jetty.server.HttpConfiguration; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.server.ServerConnector; +import org.eclipse.jetty.util.ssl.SslContextFactory; + +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.Arrays; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * Registry Application extension of Standard Jetty Server Connector Factory + */ +public class ApplicationServerConnectorFactory extends StandardServerConnectorFactory { + + private static final int HEADER_SIZE = 16384; + + private static final String CIPHER_SUITE_SEPARATOR_PATTERN = ",\\s*"; + + private final String includeCipherSuites; + + private final String excludeCipherSuites; + + private final String host; + + private SslContextFactory.Server sslContextFactory; + + public ApplicationServerConnectorFactory(final Server server, final NiFiRegistryProperties properties) { + super(server, getPort(properties)); + + host = getHost(properties); + includeCipherSuites = properties.getHttpsCipherSuitesInclude(); + excludeCipherSuites = properties.getHttpsCipherSuitesExclude(); + + if (properties.isHTTPSConfigured()) { + if (properties.getNeedClientAuth()) { + setNeedClientAuth(true); + } else { + setWantClientAuth(true); + } + + final SSLContext sslContext = buildSslContext(properties); + setSslContext(sslContext); + + setApplicationLayerProtocols(properties); + + // Set Transport Layer Security Protocols based on platform configuration + setIncludeSecurityProtocols(TlsPlatform.getPreferredProtocols().toArray(new String[0])); + } + } + + /** + * Get Server Connector using configured properties + * + * @return Server Connector + */ + @Override + public ServerConnector getServerConnector() { + final ServerConnector serverConnector = super.getServerConnector(); + serverConnector.setHost(host); + return serverConnector; + } + + /** + * Get Jetty Server SSL Context Factory and reuse the same instance for multiple invocations + * + * @return Jetty Server SSL Context Factory + */ + @Override + protected SslContextFactory.Server getSslContextFactory() { + if (sslContextFactory == null) { + sslContextFactory = super.getSslContextFactory(); + + if (StringUtils.isNotBlank(includeCipherSuites)) { + final String[] cipherSuites = getCipherSuites(includeCipherSuites); + sslContextFactory.setIncludeCipherSuites(cipherSuites); + } + if (StringUtils.isNotBlank(excludeCipherSuites)) { + final String[] cipherSuites = getCipherSuites(excludeCipherSuites); + sslContextFactory.setExcludeCipherSuites(cipherSuites); + } + } + + return sslContextFactory; + } + + /** + * Get HTTP Configuration with additional settings + * + * @return HTTP Configuration + */ + @Override + protected HttpConfiguration getHttpConfiguration() { + final HttpConfiguration httpConfiguration = super.getHttpConfiguration(); + + httpConfiguration.setRequestHeaderSize(HEADER_SIZE); + httpConfiguration.setResponseHeaderSize(HEADER_SIZE); + + return httpConfiguration; + } + + private String[] getCipherSuites(final String cipherSuitesProperty) { + return cipherSuitesProperty.split(CIPHER_SUITE_SEPARATOR_PATTERN); + } + + private SSLContext buildSslContext(final NiFiRegistryProperties properties) { + final KeyStore keyStore = buildKeyStore(properties); + final char[] keyPassword = getKeyPassword(properties); + final KeyStore trustStore = buildTrustStore(properties); + + return new StandardSslContextBuilder() + .keyStore(keyStore) + .keyPassword(keyPassword) + .trustStore(trustStore) + .build(); + } + + private char[] getKeyPassword(final NiFiRegistryProperties properties) { + final String keyStorePassword = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD); + final String keyPassword = properties.getProperty(NiFiRegistryProperties.SECURITY_KEY_PASSWD, keyStorePassword); + return keyPassword.toCharArray(); + } + + private KeyStore buildKeyStore(final NiFiRegistryProperties properties) { + final String keyStore = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_KEYSTORE); + final String keyStoreType = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE); + final String keyStorePassword = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD); + return buildStore(keyStore, keyStoreType, keyStorePassword); + } + + private KeyStore buildTrustStore(final NiFiRegistryProperties properties) { + final String trustStore = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_TRUSTSTORE); + final String trustStoreType = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE); + final String trustStorePassword = getRequiredProperty(properties, NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD); + return buildStore(trustStore, trustStoreType, trustStorePassword); + } + + private KeyStore buildStore( + final String store, + final String storeType, + final String storePassword + ) { + final Path trustStorePath = Paths.get(store); + try (final InputStream inputStream = Files.newInputStream(trustStorePath)) { + return new StandardKeyStoreBuilder() + .type(storeType) + .password(storePassword.toCharArray()) + .inputStream(inputStream) + .build(); + } catch (final IOException e) { + final String message = String.format("Store Path [%s] read failed", store); + throw new IllegalStateException(message, e); + } + } + + private String getRequiredProperty(final NiFiRegistryProperties properties, final String property) { + final String requiredProperty = properties.getProperty(property); + if (requiredProperty == null || requiredProperty.isEmpty()) { + throw new IllegalStateException(String.format("Required Property [%s] not configured", property)); + } + return requiredProperty; + } + + private void setApplicationLayerProtocols(final NiFiRegistryProperties properties) { + final Set protocols = properties.getWebHttpsApplicationProtocols(); + + final Set applicationLayerProtocols = Arrays.stream(ApplicationLayerProtocol.values()) + .filter( + applicationLayerProtocol -> protocols.contains(applicationLayerProtocol.getProtocol()) + ) + .collect(Collectors.toSet()); + setApplicationLayerProtocols(applicationLayerProtocols); + } + + private static String getHost(final NiFiRegistryProperties properties) { + final String host; + + if (properties.isHTTPSConfigured()) { + host = properties.getHttpsHost(); + } else { + host = properties.getHttpHost(); + } + + return host; + } + + private static int getPort(final NiFiRegistryProperties properties) { + final Integer httpsPort = properties.getSslPort(); + final Integer httpPort = properties.getPort(); + + if (ObjectUtils.allNull(httpsPort, httpPort)) { + throw new IllegalStateException("Invalid port configuration: Neither nifi.registry.web.https.port nor nifi.registry.web.http.port specified"); + } else if (ObjectUtils.allNotNull(httpsPort, httpPort)) { + throw new IllegalStateException("Invalid port configuration: Both nifi.registry.web.https.port and nifi.registry.web.http.port specified"); + } + + return ObjectUtils.defaultIfNull(httpsPort, httpPort); + } +} diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/groovy/org/apache/nifi/registry/jetty/JettyServerGroovyTest.groovy b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/groovy/org/apache/nifi/registry/jetty/JettyServerGroovyTest.groovy deleted file mode 100644 index e0c1c2f23e..0000000000 --- a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/groovy/org/apache/nifi/registry/jetty/JettyServerGroovyTest.groovy +++ /dev/null @@ -1,165 +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.registry.jetty - -import org.apache.nifi.registry.properties.NiFiRegistryProperties -import org.eclipse.jetty.util.ssl.SslContextFactory -import org.junit.Rule -import org.junit.Test -import org.junit.rules.ExpectedException -import org.junit.runner.RunWith -import org.mockito.junit.MockitoJUnitRunner -import org.slf4j.Logger -import org.slf4j.LoggerFactory -import org.eclipse.jetty.server.Server - -@RunWith(MockitoJUnitRunner.class) -class JettyServerGroovyTest extends GroovyTestCase { - - private static final Logger logger = LoggerFactory.getLogger(JettyServerGroovyTest.class) - - private static final keyPassword = "keyPassword" - private static final keystorePassword = "keystorePassword" - private static final truststorePassword = "truststorePassword" - private static final matchingPassword = "thePassword" - - @Test - void testCreateSslContextFactoryWithKeystoreAndKeypassword() throws Exception { - - // Arrange - NiFiRegistryProperties properties = new NiFiRegistryProperties() - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE, "src/test/resources/truststore.jks") - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD, truststorePassword) - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_TYPE, "JKS") - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE, "src/test/resources/keystoreDifferentPasswords.jks") - properties.setProperty(NiFiRegistryProperties.SECURITY_KEY_PASSWD, keyPassword) - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD, keystorePassword) - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE, "JKS") - - Server internalServer = new Server() - JettyServer testServer = new JettyServer(internalServer, properties) - - // Act - SslContextFactory sslContextFactory = testServer.createSslContextFactory() - sslContextFactory.start() - - // Assert - assertNotNull(sslContextFactory) - assertNotNull(sslContextFactory.getSslContext()) - } - - @Test - void testCreateSslContextFactoryWithOnlyKeystorePassword() throws Exception { - - // Arrange - NiFiRegistryProperties properties = new NiFiRegistryProperties() - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE, "src/test/resources/truststore.jks") - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD, truststorePassword) - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_TYPE, "JKS") - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE, "src/test/resources/keystoreSamePassword.jks") - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD, matchingPassword) - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE, "JKS") - - Server internalServer = new Server() - JettyServer testServer = new JettyServer(internalServer, properties) - - // Act - SslContextFactory sslContextFactory = testServer.createSslContextFactory() - sslContextFactory.start() - - // Assert - assertNotNull(sslContextFactory) - assertNotNull(sslContextFactory.getSslContext()) - } - - @Test - void testCreateSslContextFactoryWithMatchingPasswordsDefined() throws Exception { - - // Arrange - NiFiRegistryProperties properties = new NiFiRegistryProperties() - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE, "src/test/resources/truststore.jks") - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD, truststorePassword) - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_TYPE, "JKS") - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE, "src/test/resources/keystoreSamePassword.jks") - properties.setProperty(NiFiRegistryProperties.SECURITY_KEY_PASSWD, matchingPassword) - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD, matchingPassword) - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE, "JKS") - - Server internalServer = new Server() - JettyServer testServer = new JettyServer(internalServer, properties) - - // Act - SslContextFactory sslContextFactory = testServer.createSslContextFactory() - sslContextFactory.start() - - // Assert - assertNotNull(sslContextFactory) - assertNotNull(sslContextFactory.getSslContext()) - } - - @Rule public ExpectedException exception = ExpectedException.none() - - @Test - void testCreateSslContextFactoryWithNoKeystorePasswordFails() throws Exception { - - // Arrange - exception.expect(IllegalArgumentException.class) - exception.expectMessage("The keystore password cannot be null or empty") - - NiFiRegistryProperties properties = new NiFiRegistryProperties() - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE, "src/test/resources/truststore.jks") - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD, truststorePassword) - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_TYPE, "JKS") - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE, "src/test/resources/keystoreSamePassword.jks") - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE, "JKS") - - Server internalServer = new Server() - JettyServer testServer = new JettyServer(internalServer, properties) - - // Act but expect exception - SslContextFactory sslContextFactory = testServer.createSslContextFactory() - } - - @Test - void testCreateSslContextFactoryWithCipherSuites() throws Exception { - - // Arrange - NiFiRegistryProperties properties = new NiFiRegistryProperties() - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE, "src/test/resources/truststore.jks") - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_PASSWD, truststorePassword) - properties.setProperty(NiFiRegistryProperties.SECURITY_TRUSTSTORE_TYPE, "JKS") - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE, "src/test/resources/keystoreSamePassword.jks") - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_PASSWD, matchingPassword) - properties.setProperty(NiFiRegistryProperties.SECURITY_KEYSTORE_TYPE, "JKS") - properties.setProperty(NiFiRegistryProperties.WEB_HTTPS_CIPHERSUITES_INCLUDE, "TLS_DHE_RSA_WITH_AES_128_GCM_SHA256,TLS_DHE_RSA_WITH_AES_256_GCM_SHA384") - properties.setProperty(NiFiRegistryProperties.WEB_HTTPS_CIPHERSUITES_EXCLUDE, "BAD_CIPHER") - - Server internalServer = new Server() - JettyServer testServer = new JettyServer(internalServer, properties) - - // Act - SslContextFactory sslContextFactory = testServer.createSslContextFactory() - sslContextFactory.start() - - // Assert this - assertNotNull(sslContextFactory) - assertNotNull(sslContextFactory.getSslContext()) - assertEquals("INCLUDE_CIPHER_SUITES", sslContextFactory.getIncludeCipherSuites(), ["TLS_DHE_RSA_WITH_AES_128_GCM_SHA256","TLS_DHE_RSA_WITH_AES_256_GCM_SHA384"]) - assertEquals("EXCLUDE_CIPHER_SUITES", sslContextFactory.getExcludeCipherSuites(), ["BAD_CIPHER"]) - } - -} diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactoryTest.java b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactoryTest.java new file mode 100644 index 0000000000..fa4b12fa80 --- /dev/null +++ b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/java/org/apache/nifi/registry/jetty/connector/ApplicationServerConnectorFactoryTest.java @@ -0,0 +1,146 @@ +/* + * 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.registry.jetty.connector; + +import org.apache.nifi.registry.properties.NiFiRegistryProperties; +import org.apache.nifi.remote.io.socket.NetworkUtils; +import org.apache.nifi.security.util.TemporaryKeyStoreBuilder; +import org.apache.nifi.security.util.TlsConfiguration; +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 java.util.List; +import java.util.Properties; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +class ApplicationServerConnectorFactoryTest { + + private static final String SSL_PROTOCOL = "ssl"; + + private static final String H2_PROTOCOL = "h2"; + + private static final String INCLUDE_CIPHERS = ".*GCM.*"; + + private static final String EXCLUDE_CIPHERS = "*.CBC.*"; + + private static final String LOCALHOST = "127.0.0.1"; + + static TlsConfiguration tlsConfiguration; + + Server server; + + @BeforeAll + static void setTlsConfiguration() { + tlsConfiguration = new TemporaryKeyStoreBuilder().build(); + } + + @BeforeEach + void setServer() { + server = new Server(); + } + + @Test + void testGetServerConnectorRequiredProperties() { + final int port = NetworkUtils.getAvailableTcpPort(); + final Properties configuredProperties = new Properties(); + configuredProperties.put(NiFiRegistryProperties.WEB_HTTP_PORT, Integer.toString(port)); + + final NiFiRegistryProperties properties = getProperties(configuredProperties); + final ApplicationServerConnectorFactory factory = new ApplicationServerConnectorFactory(server, properties); + + final ServerConnector serverConnector = factory.getServerConnector(); + + assertNotNull(serverConnector); + assertNull(serverConnector.getHost()); + } + + @Test + void testGetServerConnectorHostProperty() { + final int port = NetworkUtils.getAvailableTcpPort(); + final Properties configuredProperties = new Properties(); + configuredProperties.put(NiFiRegistryProperties.WEB_HTTP_PORT, Integer.toString(port)); + configuredProperties.put(NiFiRegistryProperties.WEB_HTTP_HOST, LOCALHOST); + + final NiFiRegistryProperties properties = getProperties(configuredProperties); + final ApplicationServerConnectorFactory factory = new ApplicationServerConnectorFactory(server, properties); + + final ServerConnector serverConnector = factory.getServerConnector(); + + assertNotNull(serverConnector); + assertEquals(LOCALHOST, serverConnector.getHost()); + } + + @Test + void testGetServerConnectorSslProperties() { + final int port = NetworkUtils.getAvailableTcpPort(); + final Properties configuredProperties = getSecurityProperties(); + configuredProperties.put(NiFiRegistryProperties.WEB_HTTPS_PORT, Integer.toString(port)); + + final NiFiRegistryProperties properties = getProperties(configuredProperties); + final ApplicationServerConnectorFactory factory = new ApplicationServerConnectorFactory(server, properties); + + final ServerConnector serverConnector = factory.getServerConnector(); + + assertNotNull(serverConnector); + assertNull(serverConnector.getHost()); + assertTrue(serverConnector.getProtocols().contains(SSL_PROTOCOL)); + } + + @Test + void testGetServerConnectorHttp2Properties() { + final int port = NetworkUtils.getAvailableTcpPort(); + final Properties configuredProperties = getSecurityProperties(); + configuredProperties.put(NiFiRegistryProperties.WEB_HTTPS_PORT, Integer.toString(port)); + configuredProperties.put(NiFiRegistryProperties.WEB_HTTPS_APPLICATION_PROTOCOLS, H2_PROTOCOL); + configuredProperties.put(NiFiRegistryProperties.WEB_HTTPS_CIPHERSUITES_INCLUDE, INCLUDE_CIPHERS); + configuredProperties.put(NiFiRegistryProperties.WEB_HTTPS_CIPHERSUITES_EXCLUDE, EXCLUDE_CIPHERS); + + final NiFiRegistryProperties properties = getProperties(configuredProperties); + final ApplicationServerConnectorFactory factory = new ApplicationServerConnectorFactory(server, properties); + + final ServerConnector serverConnector = factory.getServerConnector(); + + assertNotNull(serverConnector); + assertNull(serverConnector.getHost()); + + final List protocols = serverConnector.getProtocols(); + assertTrue(protocols.contains(SSL_PROTOCOL)); + assertTrue(protocols.contains(H2_PROTOCOL)); + } + + 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()); + return securityProperties; + } + + private NiFiRegistryProperties getProperties(final Properties configuredProperties) { + return new NiFiRegistryProperties(configuredProperties); + } +} diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreDifferentPasswords.jks b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreDifferentPasswords.jks deleted file mode 100644 index 98c8903ad96b59580a92d697c3048cd0db15fb67..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3128 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 HXz4!yKIcL^ diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreSamePassword.jks b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreSamePassword.jks deleted file mode 100644 index aeedd7f952e3b108e0ea3cc3a2015b425680d57b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3128 zcmchYc{J4j8pmh1uUST!1u1)9%VdcplAkh|BwIqp*2pr5mLEoTQ4AB2C0o`eN@a<$ zWvqz^sSv3qTYh7!?o{{wPQQEZJ->7Rxqm$8e4gh#=Q*GAd4FE-=PTQxm1@;9HMDQvjz%Z~9L<9_mgJ2Zcy!k9q6D5<& zM0FNLXI_{)koBVDwUW4>laLn0oAiy=JDglvm$oLcxiodia87spwx&OJy9lk4H+|ed zLpycCI%JYWFL)`GA&V0X^vLq>%<1HBSH34-e=46O@80{zvqO^lJ|S1WLHr+hb$Mia zW2@q3(de=cqZK`Nwp^{y8Kd*+!zUO+1(B-;K|+FEXO*=7e)aAswJI(NHk zpkG`H8i9>C=}wi6>b1IgXQO95??gN^g4&8SJe}LJa}rWp_jSSSU5ju zh=={=`f#>tqFhG^`lv6h95}zNH@`X?V=#VAEKehL@LWk7Zr{9Ec%(^etzySfNN23J zb~VZHL-q4qW*oX=gFGfew6WUnx=$QUpy!Jqh66m zoGm=WG_Jzq^Qx+jhdrryw~bF&f*RkTftFXCIco{afn5x83%y4WulsU>9(<+|5n|jL zKpLJYtCrm-uv+vo?l}Xa#&$jlKa*R&p=q4CRSfi~Xf_MxbXwN9$T>=KlX`S0d}J~i ziM#)17MvgGPCdx1rEQf8Qt|235vLNRmYMo?`2i22r==)~9@3$HlFn*OOq1INRp}Bx zA=~WlPA-Bl22dA!GX01r@DPbA-{gjmeUa{$K>; zRDw^?um@wEUy#k>n@@V>io{NZF=I|zwLeW;wR3edn`ul*3@IF)+^aTSl1?O~@RDj> zzV(YVr)iG##g+Djr@ZmJ>W~_=OKhdy4w9<|4>stSs&|#vF%{iwiB>4+Af$R_>#eTF zR_Pm-A-yE<=FlB?jmkDsny2TzNiEZFeKE+sFj>5J-nrVFNA{w=IL9;IMM>{W@_&rc z5@0$mvW2_2q)93=Z?mr>vtGrhrxqnY_cxXwcf0krq;83pEOevVwK`!j?6t%Pi{H+~ zS*4xuDXb2Jgrz%Ey4%IrkNv~HH=dsVqxoq<=WjGJgBD>hH}oaERA`Z0Nv^{LNo9@aJ!>~$96 zL-E%kfJ?x@bz;+Ubm-*La^=F4Na>Q^7z>u$cD*~`4){aLIz!8?e#(7lyL z%H7pPv04?}J450hL86??6b~8^;3t41-FP|chC&gbA^+h*3a0PTlp@KJSa~!nht$vesW78|orJm!}$efou+He|z#H!KWw>2?0T-WaYVf=g1 zIsvs*H`Cc(wf&5n-_i?K!vlJHU(QteXY5s(F_X0MTmD?89=3UBRczlYcuT)*J!`!5 z(0}uXO~UQeZ~(^-p`ic;8U#=vPI*uW7y{u1dopM;3{DRS5J2!K#hAsg1;AiFI0yjL z1pqz-^f-)!q7~?zQcq#kcIJnJ?EDtjg`Q@p!^xE@GG- zPwyUP-zbq5XMBShd}kUbqdKBM|9vC$1EWS<4$qT##C(L0r1taxPh}4#5hz=3T1Fe$7RMt@4(&SXVC_ySbm$54+SU$%f-50e1I5Zs*235Rn*A%C90 z0Q!5U2uumg0uDl+q11v5rKtk`wTz0CRh`Uebs3BIE%S)iu@JMJ@z38=Rz z?+NP@l9LgZ&Ek3f_+yvH0z~uNpfty)IxUCvEI=&7;RXlVz=imiHwW|xI7rgph~ z)ZE_Nzk4~k!T0Ky8cpQ}uXH}lr{vM9QP+Fjr}lyVM9`Ci;AswmM}9#N+wuQF@PCK; z?|@9)Ot(Xr1Z26n6^3beO9G1XpnZ_g!mlicP5al#2q6%`Tuz{JxJ+LxdLixov_j@r0mMC23 z8aoGReNB(7JpmYd-w*eRucp1g)i)tI->CWmfQ~cs=6+HsIK#LFKmT{!( zFE`E$TBld4=hWPee7Azk0b^S%Cu5JK@5;Yt5JRhg_)f%KmxA9M`b{HReRxaEto(`g zHEL1*qcw|J2KU4@9KzGtwt|P~Q1M0%pSBRP=QbKn=dLMxl$hZ2gt8ckDa;_vsU*pK zlgs5TZS&nRzw1)k<5qhQumJAHc=>|>A$p%eK-V2zS z7@3&3K=KO5fSMQ#c-c6$+C196^D;7WvoaVM81fo$voVLVaPzR_Wu|2s${WanxXe6a zA*sbB3eJuS8ZP+?e)%N|Aw{LdC7K3u;=G0yhDJbaVQge%93{?cVrF1!U;^cmVPX@b z60)-xSs9p{82K3tni#p5niv@wx_?iNU~?&)m6Wuk(kN5hK=UPoHB-fse{Z9HM?R1U zo&WXSQa6|BFJ1P{`F7Z4Q>K^mmg4vCKgcj>NVErpwTWN7`nLP}_ndmkz5m73gLXeW z5dFt~;v>lqAK%Ar*7f_SxBlggYa-42PRkySJdnUp%aglH_DWFmbW`3Ac0K-&PbI za+7H4UKSFv`pq>rPPT-2*2(fq5|V!1I=+zcYD`{C@}bh_vx1NBi;2rTnZp?QLq|oq zk>|~J1N~p;-k2(El-;&t5BHkQrW-9Hdag#=gnegXW@KPoTyIcozz2*PS$;;w|12!P zi2Y~41L6y_uo^G}#lR6K%f}+dB654lc8^nKkGxLtv;I8)z2C=-@#Jv>d5~gd76}8f z2J8wz5ihICB4!}M#-Yu|$jZvj%mimKB1aK0dot3xsmo+_p z@6Xkv+Di?ZT>|WG?Vld7P^v5;PTlaQ;2HNRCZDEM-F&~j**fLN7MD(W|D{!3BAabC zMFd{A(@B36b^n-q!HYLMpW+NwT=Cnq$KVR@-R4v0nvY!IJU#1}hvwh7fY$5G)zdpG zITWKe+I-skk}q)TTEn@US2n)*EHRf+;h_J$cJHZb3%A;~O<%*5d#7WDB@_eopM{=cKs@1N4bl~`>3x|z&+mvdvy getWebHttpsApplicationProtocols() { + final String protocols = getProperty(WEB_HTTPS_APPLICATION_PROTOCOLS, DEFAULT_WEB_HTTPS_APPLICATION_PROTOCOLS); + return Arrays.stream(protocols.split("\\s+")).collect(Collectors.toSet()); + } + public File getExtensionsWorkingDirectory() { return new File(getProperty(EXTENSIONS_WORKING_DIR, DEFAULT_EXTENSIONS_WORKING_DIR)); } diff --git a/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties b/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties index db5b429dec..c7a6b9fd64 100644 --- a/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties +++ b/nifi-registry/nifi-registry-core/nifi-registry-resources/src/main/resources/conf/nifi-registry.properties @@ -19,6 +19,7 @@ nifi.registry.web.http.host=${nifi.registry.web.http.host} nifi.registry.web.http.port=${nifi.registry.web.http.port} nifi.registry.web.https.host=${nifi.registry.web.https.host} nifi.registry.web.https.port=${nifi.registry.web.https.port} +nifi.registry.web.https.application.protocols=${nifi.registry.web.https.application.protocols} nifi.registry.web.jetty.working.directory=${nifi.registry.jetty.work.dir} nifi.registry.web.jetty.threads=${nifi.registry.web.jetty.threads} nifi.registry.web.should.send.server.version=${nifi.registry.web.should.send.server.version} diff --git a/nifi-registry/pom.xml b/nifi-registry/pom.xml index 753d4647d4..4dfa2f11ee 100644 --- a/nifi-registry/pom.xml +++ b/nifi-registry/pom.xml @@ -93,6 +93,18 @@ apache-jstl compile + + org.eclipse.jetty.http2 + http2-server + ${jetty.version} + compile + + + org.eclipse.jetty + jetty-alpn-server + ${jetty.version} + compile + jakarta.xml.bind