This is an automated email from the ASF dual-hosted git repository.
exceptionfactory 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 17fa0cf NIFI-3580 Add configurable TLS Cipher Suite properties
17fa0cf is described below
commit 17fa0cf3c1a509ad87386127def9373eb0c1e5df
Author: Paul Grey <[email protected]>
AuthorDate: Mon Apr 19 15:28:53 2021 -0400
NIFI-3580 Add configurable TLS Cipher Suite properties
This closes #5018
Signed-off-by: David Handermann <[email protected]>
---
.../java/org/apache/nifi/util/NiFiProperties.java | 2 ++
.../src/main/asciidoc/administration-guide.adoc | 30 ++++++++++++++++++
.../src/main/resources/conf/nifi.properties | 4 +++
.../org/apache/nifi/web/server/JettyServer.java | 20 ++++++++++++
.../apache/nifi/web/server/JettyServerTest.java | 36 ++++++++++++++++++++++
5 files changed, 92 insertions(+)
diff --git
a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
index e9f91f9..d4da56e 100644
---
a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
+++
b/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/NiFiProperties.java
@@ -207,6 +207,8 @@ public abstract class NiFiProperties {
public static final String WEB_HTTPS_PORT = "nifi.web.https.port";
public static final String WEB_HTTPS_PORT_FORWARDING =
"nifi.web.https.port.forwarding";
public static final String WEB_HTTPS_HOST = "nifi.web.https.host";
+ public static final String WEB_HTTPS_CIPHERSUITES_INCLUDE =
"nifi.web.https.ciphersuites.include";
+ public static final String WEB_HTTPS_CIPHERSUITES_EXCLUDE =
"nifi.web.https.ciphersuites.exclude";
public static final String WEB_HTTPS_NETWORK_INTERFACE_PREFIX =
"nifi.web.https.network.interface.";
public static final String WEB_WORKING_DIR =
"nifi.web.jetty.working.directory";
public static final String WEB_THREADS = "nifi.web.jetty.threads";
diff --git a/nifi-docs/src/main/asciidoc/administration-guide.adoc
b/nifi-docs/src/main/asciidoc/administration-guide.adoc
index 619058e..e4ab517 100644
--- a/nifi-docs/src/main/asciidoc/administration-guide.adoc
+++ b/nifi-docs/src/main/asciidoc/administration-guide.adoc
@@ -209,6 +209,28 @@ In order to facilitate the secure setup of NiFi, you can
use the `tls-toolkit` c
* <<toolkit-guide.adoc#tls_intermediate_ca,Using An Existing Intermediate
Certificate Authority>>
* <<toolkit-guide.adoc#additional_certificate_commands,Additional Certificate
Commands>>
+[[tls_cipher_suites]]
+=== TLS Cipher Suites
+
+The Java Runtime Environment provides the ability to specify custom TLS cipher
suites to be used by servers when accepting client connections. See
+link:https://java.com/en/configure_crypto.html[here^] for more information.
To use this feature for the NiFi web service, the following NiFi properties
+may be set:
+
+[options="header,footer"]
+|==================================================================================================================================================
+| Property Name | Description
+|`nifi.web.https.ciphersuites.include` | Set of ciphers that are available to
be used by incoming client connections. Replaces system defaults if set.
+|`nifi.web.https.ciphersuites.exclude` | Set of ciphers that must not be used
by incoming client connections. Filters available ciphers if set.
+|==================================================================================================================================================
+
+Each property should take the form of a comma-separated list of common cipher
names as specified
+link:https://docs.oracle.com/javase/8/docs/technotes/guides/security/StandardNames.html#ciphersuites[here^].
Regular expressions
+(for example `^.*GCM_SHA256$`) may also be specified.
+
+The semantics match the use of the following Jetty APIs:
+
+*
link:https://www.eclipse.org/jetty/javadoc/jetty-9/org/eclipse/jetty/util/ssl/SslContextFactory.html#setIncludeCipherSuites(java.lang.String\...)[SslContextFactory.setIncludeCipherSuites()]
+*
link:https://www.eclipse.org/jetty/javadoc/jetty-9/org/eclipse/jetty/util/ssl/SslContextFactory.html#setExcludeCipherSuites(java.lang.String\...)[SslContextFactory.setExcludeCipherSuites()]
[[user_authentication]]
== User Authentication
@@ -3494,6 +3516,14 @@ Providing three total network interfaces, including
`nifi.web.http.network.inte
|`nifi.web.https.host`|The HTTPS host. It is blank by default.
|`nifi.web.https.port`|The HTTPS port. It is blank by default. When
configuring NiFi to run securely, this port should be configured.
|`nifi.web.https.port.forwarding`|Same as `nifi.web.http.port.forwarding`, but
with HTTPS for secure communication. It is blank by default.
+|`nifi.web.https.ciphersuites.include`|Cipher suites used to initialize the
SSLContext of the Jetty HTTPS port. If unspecified, the runtime SSLContext
defaults are used.
+|`nifi.web.https.ciphersuites.exclude`|Cipher suites that may not be used by
an SSL client to establish a connection to Jetty. If unspecified, the runtime
SSLContext defaults are used.
+
+
+In Chrome, the SSL cipher negotiated with Jetty may be examined in the
'Developer Tools' plugin, in the 'Security' tab.
+In Firefox, the SSL cipher negotiated with Jetty may be examined in the
'Secure Connection' widget found to the left of the URL in the browser address
bar.
+
+
|`nifi.web.https.network.interface`*|The name of the network interface to
which NiFi should bind for HTTPS requests. It is blank by default. +
+
*NOTE*: Multiple network interfaces can be specified by using the
`nifi.web.https.network.interface.` prefix with unique suffixes and separate
network interface names as values. +
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
index 1366daf..e0a47d3 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-resources/src/main/resources/conf/nifi.properties
@@ -167,6 +167,10 @@ nifi.web.request.timeout=${nifi.web.request.timeout}
nifi.web.request.ip.whitelist=${nifi.web.request.ip.whitelist}
nifi.web.should.send.server.version=${nifi.web.should.send.server.version}
+# Include or Exclude TLS Cipher Suites for HTTPS
+nifi.web.https.ciphersuites.include=
+nifi.web.https.ciphersuites.exclude=
+
# security properties #
nifi.sensitive.props.key=
nifi.sensitive.props.key.protected=${nifi.sensitive.props.key.protected}
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
index 7dc1ffd..16fa44c 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/main/java/org/apache/nifi/web/server/JettyServer.java
@@ -138,6 +138,10 @@ public class JettyServer implements NiFiServer,
ExtensionUiLoader {
return nameToTest.endsWith(".war") && pathname.isFile();
};
+ // property parsing util
+ private static final String REGEX_SPLIT_PROPERTY = ",\\s*";
+ protected static final String JOIN_ARRAY = ", ";
+
private Server server;
private NiFiProperties props;
@@ -1012,6 +1016,22 @@ public class JettyServer implements NiFiServer,
ExtensionUiLoader {
contextFactory.setIncludeProtocols(TlsConfiguration.getCurrentSupportedTlsProtocolVersions());
contextFactory.setExcludeProtocols("TLS", "TLSv1", "TLSv1.1", "SSL",
"SSLv2", "SSLv2Hello", "SSLv3");
+ // on configuration, replace default application cipher suites with
those configured
+ final String includeCipherSuitesProps =
props.getProperty(NiFiProperties.WEB_HTTPS_CIPHERSUITES_INCLUDE);
+ if (StringUtils.isNotEmpty(includeCipherSuitesProps)) {
+ final String[] includeCipherSuites =
includeCipherSuitesProps.split(REGEX_SPLIT_PROPERTY);
+ logger.info("Setting include cipher suites from configuration;
parsed property = [{}].",
+ StringUtils.join(includeCipherSuites, JOIN_ARRAY));
+ contextFactory.setIncludeCipherSuites(includeCipherSuites);
+ }
+ final String excludeCipherSuitesProps =
props.getProperty(NiFiProperties.WEB_HTTPS_CIPHERSUITES_EXCLUDE);
+ if (StringUtils.isNotEmpty(excludeCipherSuitesProps)) {
+ final String[] excludeCipherSuites =
excludeCipherSuitesProps.split(REGEX_SPLIT_PROPERTY);
+ logger.info("Setting exclude cipher suites from configuration;
parsed property = [{}].",
+ StringUtils.join(excludeCipherSuites, JOIN_ARRAY));
+ contextFactory.setExcludeCipherSuites(excludeCipherSuites);
+ }
+
// require client auth when not supporting login, Kerberos service, or
anonymous access
if (props.isClientAuthRequiredForRestApi()) {
contextFactory.setNeedClientAuth(true);
diff --git
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/JettyServerTest.java
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/JettyServerTest.java
index 63e9ad5..842826d 100644
---
a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/JettyServerTest.java
+++
b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-jetty/src/test/java/org/apache/nifi/web/server/JettyServerTest.java
@@ -19,13 +19,17 @@ package org.apache.nifi.web.server;
import static org.apache.nifi.security.util.KeyStoreUtils.SUN_PROVIDER_NAME;
import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import java.lang.reflect.InvocationTargetException;
import java.util.HashMap;
import java.util.Map;
+
+import org.apache.commons.lang3.StringUtils;
import org.apache.nifi.security.util.KeystoreType;
import org.apache.nifi.util.NiFiProperties;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
@@ -142,4 +146,36 @@ public class JettyServerTest {
verify(mockSCF).setTrustStoreType(trustStoreType);
verify(mockSCF).setTrustStoreProvider(BouncyCastleProvider.PROVIDER_NAME);
}
+
+ /**
+ * Verify correct processing of cipher suites with multiple elements.
Verify call to override runtime ciphers.
+ */
+ @Test
+ public void testConfigureSslIncludeExcludeCiphers() {
+ final String[] includeCipherSuites = {"TLS_AES_256_GCM_SHA384",
"TLS_AES_128_GCM_SHA256"};
+ final String includeCipherSuitesProp =
StringUtils.join(includeCipherSuites, JettyServer.JOIN_ARRAY);
+ final String[] excludeCipherSuites = {".*DHE.*", ".*ECDH.*"};
+ final String excludeCipherSuitesProp =
StringUtils.join(excludeCipherSuites, JettyServer.JOIN_ARRAY);
+ final Map<String, String> addProps = new HashMap<>();
+ addProps.put(NiFiProperties.WEB_HTTPS_CIPHERSUITES_INCLUDE,
includeCipherSuitesProp);
+ addProps.put(NiFiProperties.WEB_HTTPS_CIPHERSUITES_EXCLUDE,
excludeCipherSuitesProp);
+ final NiFiProperties nifiProperties =
NiFiProperties.createBasicNiFiProperties(null, addProps);
+
+ final SslContextFactory.Server mockSCF =
mock(SslContextFactory.Server.class);
+ JettyServer.configureSslContextFactory(mockSCF, nifiProperties);
+ verify(mockSCF, times(1)).setIncludeCipherSuites(includeCipherSuites);
+ verify(mockSCF, times(1)).setExcludeCipherSuites(excludeCipherSuites);
+ }
+
+ /**
+ * Verify skip cipher configuration when NiFiProperties are not specified.
+ */
+ @Test
+ public void testDoNotConfigureSslIncludeExcludeCiphers() {
+ final NiFiProperties nifiProperties =
NiFiProperties.createBasicNiFiProperties(null);
+ final SslContextFactory.Server mockSCF =
mock(SslContextFactory.Server.class);
+ JettyServer.configureSslContextFactory(mockSCF, nifiProperties);
+ verify(mockSCF, times(0)).setIncludeCipherSuites(any());
+ verify(mockSCF, times(0)).setExcludeCipherSuites(any());
+ }
}