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());
+    }
 }

Reply via email to