From b862fff8f0cc7f2d2f7fd25a2f7c8802bee21670 Mon Sep 17 00:00:00 2001 From: Jeremy Snyder Date: Tue, 20 Sep 2022 14:24:36 -0400 Subject: [PATCH] NIFI-10498 Added Cipher Suite configuration to NiFi Registry This closes #6458 Signed-off-by: David Handermann --- .../main/asciidoc/administration-guide.adoc | 23 +++++++++++++++ .../nifi/registry/jetty/JettyServer.java | 18 ++++++++++++ .../jetty/JettyServerGroovyTest.groovy | 29 +++++++++++++++++++ .../properties/NiFiRegistryProperties.java | 10 +++++++ 4 files changed, 80 insertions(+) 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 f8c6f8839f..c14e86b242 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 @@ -101,6 +101,29 @@ should run on. If it is desired that the HTTPS interface be accessible from all NOTE: It is important when enabling HTTPS that the `nifi.registry.web.http.port` property be unset. +[[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.registry.web.https.ciphersuites.include` | Set of ciphers that are available to be used by incoming client connections. Replaces system defaults if set. +|`nifi.registry.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 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 04d08de7b0..6d8b47ce1a 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 @@ -73,6 +73,8 @@ 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 FileFilter WAR_FILTER = new FileFilter() { @Override @@ -214,6 +216,10 @@ public class JettyServer { } } + private static String[] getCipherSuites(final String cipherSuitesProperty) { + return cipherSuitesProperty.split(CIPHER_SUITE_SEPARATOR_PATTERN); + } + private SslContextFactory createSslContextFactory() { final SslContextFactory.Server contextFactory = new SslContextFactory.Server(); @@ -259,6 +265,18 @@ public class JettyServer { 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; } 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 index a96e5c1872..e0c1c2f23e 100644 --- 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 @@ -133,4 +133,33 @@ class JettyServerGroovyTest extends GroovyTestCase { // 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-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 3f8ec6bb94..0b908715ce 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 @@ -51,6 +51,8 @@ public class NiFiRegistryProperties extends ApplicationProperties { public static final String WEB_HTTP_HOST = "nifi.registry.web.http.host"; public static final String WEB_HTTPS_PORT = "nifi.registry.web.https.port"; 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_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"; @@ -174,6 +176,14 @@ public class NiFiRegistryProperties extends ApplicationProperties { return getProperty(WEB_HTTPS_HOST); } + public String getHttpsCipherSuitesInclude() { + return getProperty(WEB_HTTPS_CIPHERSUITES_INCLUDE); + } + + public String getHttpsCipherSuitesExclude() { + return getProperty(WEB_HTTPS_CIPHERSUITES_EXCLUDE); + } + public boolean getNeedClientAuth() { boolean needClientAuth = true; String rawNeedClientAuth = getProperty(SECURITY_NEED_CLIENT_AUTH);