You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by th...@apache.org on 2022/10/18 03:56:42 UTC
[nifi] branch main updated: NIFI-10625 Added support for HTTP/2 in Registry
This is an automated email from the ASF dual-hosted git repository.
thenatog pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/nifi.git
The following commit(s) were added to refs/heads/main by this push:
new b753c1c72d NIFI-10625 Added support for HTTP/2 in Registry
b753c1c72d is described below
commit b753c1c72d85c482bcc8bf125023107d79f54ed4
Author: exceptionfactory <ex...@apache.org>
AuthorDate: Tue Oct 11 14:29:42 2022 -0500
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 <th...@gmail.com>
This closes #6514.
---
nifi-commons/nifi-security-ssl/pom.xml | 25 +++
.../ssl/BuilderConfigurationException.java | 41 ++++
.../apache/nifi/security/ssl/KeyStoreBuilder.java | 31 +++
.../nifi/security/ssl/SslContextBuilder.java | 31 +++
.../nifi/security/ssl/StandardKeyStoreBuilder.java | 117 +++++++++++
.../security/ssl/StandardSslContextBuilder.java | 171 +++++++++++++++
.../security/ssl/StandardKeyStoreBuilderTest.java | 57 +++++
.../ssl/StandardSslContextBuilderTest.java | 78 +++++++
nifi-commons/pom.xml | 1 +
nifi-registry/nifi-registry-assembly/pom.xml | 1 +
.../src/main/asciidoc/administration-guide.adoc | 7 +
.../nifi-registry-core/nifi-registry-jetty/pom.xml | 29 +++
.../apache/nifi/registry/jetty/JettyServer.java | 164 ++-------------
.../ApplicationServerConnectorFactory.java | 231 +++++++++++++++++++++
.../registry/jetty/JettyServerGroovyTest.groovy | 165 ---------------
.../ApplicationServerConnectorFactoryTest.java | 146 +++++++++++++
.../test/resources/keystoreDifferentPasswords.jks | Bin 3128 -> 0 bytes
.../src/test/resources/keystoreSamePassword.jks | Bin 3128 -> 0 bytes
.../src/test/resources/truststore.jks | Bin 935 -> 0 bytes
.../properties/NiFiRegistryProperties.java | 12 ++
.../main/resources/conf/nifi-registry.properties | 1 +
nifi-registry/pom.xml | 12 ++
22 files changed, 1008 insertions(+), 312 deletions(-)
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 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <!--
+ 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.
+ -->
+ <modelVersion>4.0.0</modelVersion>
+ <parent>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-commons</artifactId>
+ <version>1.19.0-SNAPSHOT</version>
+ </parent>
+ <artifactId>nifi-security-ssl</artifactId>
+ <description>Shared TLS security components without additional dependencies</description>
+</project>
+
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 @@
<module>nifi-security-kerberos</module>
<module>nifi-security-kms</module>
<module>nifi-security-socket-ssl</module>
+ <module>nifi-security-ssl</module>
<module>nifi-security-utils-api</module>
<module>nifi-security-utils</module>
<module>nifi-single-user-utils</module>
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 @@
<nifi.registry.web.http.port>18080</nifi.registry.web.http.port>
<nifi.registry.web.https.host />
<nifi.registry.web.https.port />
+ <nifi.registry.web.https.application.protocols>http/1.1</nifi.registry.web.https.application.protocols>
<nifi.registry.jetty.work.dir>./work/jetty</nifi.registry.jetty.work.dir>
<nifi.registry.web.jetty.threads>200</nifi.registry.web.jetty.threads>
<nifi.registry.web.should.send.server.version>true</nifi.registry.web.should.send.server.version>
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 @@
<artifactId>nifi-registry-properties</artifactId>
<version>1.19.0-SNAPSHOT</version>
</dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-jetty-configuration</artifactId>
+ <version>1.19.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-security-ssl</artifactId>
+ <version>1.19.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-security-utils-api</artifactId>
+ <version>1.19.0-SNAPSHOT</version>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-server</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-server</artifactId>
+ </dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
@@ -62,5 +85,11 @@
<artifactId>apache-jstl</artifactId>
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>org.apache.nifi</groupId>
+ <artifactId>nifi-security-utils</artifactId>
+ <version>1.19.0-SNAPSHOT</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
</project>
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<Class<? extends Filter>> 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<? extends Filter> clazz, String path, WebAppContext webappContext) {
+ private void addFilters(Class<? extends Filter> 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<String> protocols = properties.getWebHttpsApplicationProtocols();
+
+ final Set<ApplicationLayerProtocol> 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<String> 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 98c8903ad9..0000000000
Binary files a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreDifferentPasswords.jks and /dev/null differ
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 aeedd7f952..0000000000
Binary files a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/keystoreSamePassword.jks and /dev/null differ
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/truststore.jks b/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/truststore.jks
deleted file mode 100644
index 47c8e45c3c..0000000000
Binary files a/nifi-registry/nifi-registry-core/nifi-registry-jetty/src/test/resources/truststore.jks and /dev/null differ
diff --git a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java
index 0b908715ce..eeff77de5b 100644
--- a/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java
+++ b/nifi-registry/nifi-registry-core/nifi-registry-properties/src/main/java/org/apache/nifi/registry/properties/NiFiRegistryProperties.java
@@ -53,6 +53,7 @@ public class NiFiRegistryProperties extends ApplicationProperties {
public static final String WEB_HTTPS_HOST = "nifi.registry.web.https.host";
public static final String WEB_HTTPS_CIPHERSUITES_INCLUDE = "nifi.registry.web.https.ciphersuites.include";
public static final String WEB_HTTPS_CIPHERSUITES_EXCLUDE = "nifi.registry.web.https.ciphersuites.exclude";
+ public static final String WEB_HTTPS_APPLICATION_PROTOCOLS = "nifi.registry.web.https.application.protocols";
public static final String WEB_WORKING_DIR = "nifi.registry.web.jetty.working.directory";
public static final String WEB_THREADS = "nifi.registry.web.jetty.threads";
public static final String WEB_SHOULD_SEND_SERVER_VERSION = "nifi.registry.web.should.send.server.version";
@@ -119,6 +120,7 @@ public class NiFiRegistryProperties extends ApplicationProperties {
// Defaults
public static final String DEFAULT_WEB_WORKING_DIR = "./work/jetty";
+ public static final String DEFAULT_WEB_HTTPS_APPLICATION_PROTOCOLS = "http/1.1";
public static final String DEFAULT_WAR_DIR = "./lib";
public static final String DEFAULT_PROVIDERS_CONFIGURATION_FILE = "./conf/providers.xml";
public static final String DEFAULT_REGISTRY_ALIAS_CONFIGURATION_FILE = "./conf/registry-aliases.xml";
@@ -229,6 +231,16 @@ public class NiFiRegistryProperties extends ApplicationProperties {
return new File(getProperty(WEB_WORKING_DIR, DEFAULT_WEB_WORKING_DIR));
}
+ /**
+ * Get Web HTTPS Application Protocols defaults to HTTP/1.1
+ *
+ * @return Set of configured HTTPS Application Protocols
+ */
+ public Set<String> 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 @@
<artifactId>apache-jstl</artifactId>
<scope>compile</scope>
</dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty.http2</groupId>
+ <artifactId>http2-server</artifactId>
+ <version>${jetty.version}</version>
+ <scope>compile</scope>
+ </dependency>
+ <dependency>
+ <groupId>org.eclipse.jetty</groupId>
+ <artifactId>jetty-alpn-server</artifactId>
+ <version>${jetty.version}</version>
+ <scope>compile</scope>
+ </dependency>
<!-- lib/java11 -->
<dependency>
<groupId>jakarta.xml.bind</groupId>