From fec34036a58880947562a8d24ac100ec0f5a82a5 Mon Sep 17 00:00:00 2001 From: exceptionfactory Date: Thu, 16 Feb 2023 20:51:25 -0600 Subject: [PATCH] NIFI-11195 Refactored Identity Mapping to nifi-security-identity - Moved StringUtils from nifi-properties to nifi-property-utils - Moved Peer Identity methods from CertificateUtils to specific Site-to-Site classes Signed-off-by: Joe Gresock This closes #6977. --- nifi-commons/nifi-properties/pom.xml | 12 +- .../org/apache/nifi/util/StringUtils.java | 0 .../org/apache/nifi/util/StringUtilsTest.java | 0 nifi-commons/nifi-security-identity/pom.xml | 35 ++++ .../authorization/util/IdentityMapping.java | 0 .../util/IdentityMappingUtil.java | 0 .../nifi/security/util/CertificateUtils.java | 140 -------------- nifi-commons/nifi-site-to-site-client/pom.xml | 18 +- .../client/socket/EndpointConnectionPool.java | 16 +- .../socket/SocketPeerIdentityProvider.java | 33 ++++ .../StandardSocketPeerIdentityProvider.java | 74 ++++++++ .../remote/util/SiteToSiteRestApiClient.java | 6 +- .../remote/client/PeerSelectorTest.groovy | 23 --- .../remote/client/http/TestHttpClient.java | 174 +----------------- ...tandardSocketPeerIdentityProviderTest.java | 103 +++++++++++ nifi-commons/nifi-utils/pom.xml | 6 + nifi-commons/pom.xml | 1 + .../nifi-email-processors/pom.xml | 6 + .../nifi-event-transport/pom.xml | 10 + .../nifi-standard-record-utils/pom.xml | 4 + .../nifi-reporting-utils/pom.xml | 4 + .../nifi-framework/nifi-documentation/pom.xml | 1 - .../nifi-file-authorizer/pom.xml | 3 +- .../nifi-framework-core/pom.xml | 5 + .../nifi-framework/nifi-site-to-site/pom.xml | 9 + .../nifi/remote/SocketRemoteSiteListener.java | 43 ++++- .../src/test/resources/localhost-ks.jks | Bin 3076 -> 0 bytes .../src/test/resources/localhost-ts.jks | Bin 911 -> 0 bytes .../nifi-web/nifi-web-api/pom.xml | 6 + .../nifi-web/nifi-web-security/pom.xml | 5 + .../nifi-gcp-parameter-providers/pom.xml | 1 - .../nifi-ldap-iaa-providers/pom.xml | 5 + .../nifi-record-sink-service/pom.xml | 5 + .../nifi-web-client-provider-service/pom.xml | 6 + 34 files changed, 388 insertions(+), 366 deletions(-) rename nifi-commons/{nifi-properties => nifi-property-utils}/src/main/java/org/apache/nifi/util/StringUtils.java (100%) rename nifi-commons/{nifi-properties => nifi-property-utils}/src/test/java/org/apache/nifi/util/StringUtilsTest.java (100%) create mode 100644 nifi-commons/nifi-security-identity/pom.xml rename nifi-commons/{nifi-security-utils => nifi-security-identity}/src/main/java/org/apache/nifi/authorization/util/IdentityMapping.java (100%) rename nifi-commons/{nifi-security-utils => nifi-security-identity}/src/main/java/org/apache/nifi/authorization/util/IdentityMappingUtil.java (100%) create mode 100644 nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketPeerIdentityProvider.java create mode 100644 nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/StandardSocketPeerIdentityProvider.java create mode 100644 nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/socket/StandardSocketPeerIdentityProviderTest.java delete mode 100755 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/resources/localhost-ks.jks delete mode 100755 nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/resources/localhost-ts.jks diff --git a/nifi-commons/nifi-properties/pom.xml b/nifi-commons/nifi-properties/pom.xml index 3f5286effd..dea20ce96f 100644 --- a/nifi-commons/nifi-properties/pom.xml +++ b/nifi-commons/nifi-properties/pom.xml @@ -15,6 +15,12 @@ --> 4.0.0 + + org.apache.nifi + nifi-commons + 1.24.0-SNAPSHOT + + nifi-properties org.apache.nifi @@ -23,10 +29,4 @@ compile - - org.apache.nifi - nifi-commons - 1.24.0-SNAPSHOT - - nifi-properties diff --git a/nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/StringUtils.java b/nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/util/StringUtils.java similarity index 100% rename from nifi-commons/nifi-properties/src/main/java/org/apache/nifi/util/StringUtils.java rename to nifi-commons/nifi-property-utils/src/main/java/org/apache/nifi/util/StringUtils.java diff --git a/nifi-commons/nifi-properties/src/test/java/org/apache/nifi/util/StringUtilsTest.java b/nifi-commons/nifi-property-utils/src/test/java/org/apache/nifi/util/StringUtilsTest.java similarity index 100% rename from nifi-commons/nifi-properties/src/test/java/org/apache/nifi/util/StringUtilsTest.java rename to nifi-commons/nifi-property-utils/src/test/java/org/apache/nifi/util/StringUtilsTest.java diff --git a/nifi-commons/nifi-security-identity/pom.xml b/nifi-commons/nifi-security-identity/pom.xml new file mode 100644 index 0000000000..b6c3fd397f --- /dev/null +++ b/nifi-commons/nifi-security-identity/pom.xml @@ -0,0 +1,35 @@ + + + 4.0.0 + + org.apache.nifi + nifi-commons + 2.0.0-SNAPSHOT + + nifi-security-identity + + + org.apache.nifi + nifi-properties + 2.0.0-SNAPSHOT + + + org.apache.commons + commons-lang3 + + + + diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/authorization/util/IdentityMapping.java b/nifi-commons/nifi-security-identity/src/main/java/org/apache/nifi/authorization/util/IdentityMapping.java similarity index 100% rename from nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/authorization/util/IdentityMapping.java rename to nifi-commons/nifi-security-identity/src/main/java/org/apache/nifi/authorization/util/IdentityMapping.java diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/authorization/util/IdentityMappingUtil.java b/nifi-commons/nifi-security-identity/src/main/java/org/apache/nifi/authorization/util/IdentityMappingUtil.java similarity index 100% rename from nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/authorization/util/IdentityMappingUtil.java rename to nifi-commons/nifi-security-identity/src/main/java/org/apache/nifi/authorization/util/IdentityMappingUtil.java diff --git a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java index d386e0f0fc..c00ea50262 100644 --- a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java +++ b/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.java @@ -18,12 +18,10 @@ package org.apache.nifi.security.util; import java.io.ByteArrayInputStream; import java.math.BigInteger; -import java.net.Socket; import java.security.KeyPair; import java.security.NoSuchAlgorithmException; import java.security.PublicKey; import java.security.Security; -import java.security.cert.Certificate; import java.security.cert.CertificateException; import java.security.cert.CertificateFactory; import java.security.cert.CertificateParsingException; @@ -42,8 +40,6 @@ import javax.naming.InvalidNameException; import javax.naming.ldap.LdapName; import javax.naming.ldap.Rdn; import javax.net.ssl.SSLException; -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSocket; import org.apache.commons.lang3.StringUtils; import org.bouncycastle.asn1.ASN1Encodable; import org.bouncycastle.asn1.ASN1ObjectIdentifier; @@ -81,14 +77,8 @@ import org.slf4j.LoggerFactory; public final class CertificateUtils { private static final Logger logger = LoggerFactory.getLogger(CertificateUtils.class); - private static final String PEER_NOT_AUTHENTICATED_MSG = "peer not authenticated"; private static final Map dnOrderMap = createDnOrderMap(); - public static final String JAVA_8_MAX_SUPPORTED_TLS_PROTOCOL_VERSION = "TLSv1.2"; - public static final String JAVA_11_MAX_SUPPORTED_TLS_PROTOCOL_VERSION = "TLSv1.3"; - public static final String[] JAVA_8_SUPPORTED_TLS_PROTOCOL_VERSIONS = new String[]{JAVA_8_MAX_SUPPORTED_TLS_PROTOCOL_VERSION}; - public static final String[] JAVA_11_SUPPORTED_TLS_PROTOCOL_VERSIONS = new String[]{JAVA_11_MAX_SUPPORTED_TLS_PROTOCOL_VERSION, JAVA_8_MAX_SUPPORTED_TLS_PROTOCOL_VERSION}; - static { Security.addProvider(new BouncyCastleProvider()); } @@ -206,136 +196,6 @@ public final class CertificateUtils { return result; } - /** - * Returns the DN extracted from the peer certificate (the server DN if run on the client; the client DN (if available) if run on the server). - *

- * If the client auth setting is WANT or NONE and a client certificate is not present, this method will return {@code null}. - * If the client auth is NEED, it will throw a {@link CertificateException}. - * - * @param socket the SSL Socket - * @return the extracted DN - * @throws CertificateException if there is a problem parsing the certificate - */ - public static String extractPeerDNFromSSLSocket(Socket socket) throws CertificateException { - String dn = null; - if (socket instanceof SSLSocket) { - final SSLSocket sslSocket = (SSLSocket) socket; - - boolean clientMode = sslSocket.getUseClientMode(); - logger.debug("SSL Socket in {} mode", clientMode ? "client" : "server"); - ClientAuth clientAuth = getClientAuthStatus(sslSocket); - logger.debug("SSL Socket client auth status: {}", clientAuth); - - if (clientMode) { - logger.debug("This socket is in client mode, so attempting to extract certificate from remote 'server' socket"); - dn = extractPeerDNFromServerSSLSocket(sslSocket); - } else { - logger.debug("This socket is in server mode, so attempting to extract certificate from remote 'client' socket"); - dn = extractPeerDNFromClientSSLSocket(sslSocket); - } - } - - return dn; - } - - /** - * Returns the DN extracted from the client certificate. - *

- * If the client auth setting is WANT or NONE and a certificate is not present (and {@code respectClientAuth} is {@code true}), this method will return {@code null}. - * If the client auth is NEED, it will throw a {@link CertificateException}. - * - * @param sslSocket the SSL Socket - * @return the extracted DN - * @throws CertificateException if there is a problem parsing the certificate - */ - private static String extractPeerDNFromClientSSLSocket(SSLSocket sslSocket) throws CertificateException { - String dn = null; - - /** The clientAuth value can be "need", "want", or "none" - * A client must send client certificates for need, should for want, and will not for none. - * This method should throw an exception if none are provided for need, return null if none are provided for want, and return null (without checking) for none. - */ - - ClientAuth clientAuth = getClientAuthStatus(sslSocket); - logger.debug("SSL Socket client auth status: {}", clientAuth); - - if (clientAuth != ClientAuth.NONE) { - try { - final Certificate[] certChains = sslSocket.getSession().getPeerCertificates(); - if (certChains != null && certChains.length > 0) { - X509Certificate x509Certificate = convertAbstractX509Certificate(certChains[0]); - dn = x509Certificate.getSubjectDN().getName().trim(); - logger.debug("Extracted DN={} from client certificate", dn); - } - } catch (SSLPeerUnverifiedException e) { - if (e.getMessage().equals(PEER_NOT_AUTHENTICATED_MSG)) { - logger.error("The incoming request did not contain client certificates and thus the DN cannot" + - " be extracted. Check that the other endpoint is providing a complete client certificate chain"); - } - if (clientAuth == ClientAuth.WANT) { - logger.warn("Suppressing missing client certificate exception because client auth is set to 'want'"); - return null; - } - throw new CertificateException(e); - } - } - return dn; - } - - /** - * Returns the DN extracted from the server certificate. - * - * @param socket the SSL Socket - * @return the extracted DN - * @throws CertificateException if there is a problem parsing the certificate - */ - private static String extractPeerDNFromServerSSLSocket(Socket socket) throws CertificateException { - String dn = null; - if (socket instanceof SSLSocket) { - final SSLSocket sslSocket = (SSLSocket) socket; - try { - final Certificate[] certChains = sslSocket.getSession().getPeerCertificates(); - if (certChains != null && certChains.length > 0) { - X509Certificate x509Certificate = convertAbstractX509Certificate(certChains[0]); - dn = x509Certificate.getSubjectDN().getName().trim(); - logger.debug("Extracted DN={} from server certificate", dn); - } - } catch (SSLPeerUnverifiedException e) { - if (e.getMessage().equals(PEER_NOT_AUTHENTICATED_MSG)) { - logger.error("The server did not present a certificate and thus the DN cannot" + - " be extracted. Check that the other endpoint is providing a complete certificate chain"); - } - throw new CertificateException(e); - } - } - return dn; - } - - private static ClientAuth getClientAuthStatus(SSLSocket sslSocket) { - return sslSocket.getNeedClientAuth() ? ClientAuth.REQUIRED : sslSocket.getWantClientAuth() ? ClientAuth.WANT : ClientAuth.NONE; - } - - /** - * Accepts a legacy {@link javax.security.cert.X509Certificate} and returns an {@link X509Certificate}. The {@code javax.*} package certificate classes are for legacy compatibility and should - * not be used for new development. - * - * @param legacyCertificate the {@code javax.security.cert.X509Certificate} - * @return a new {@code java.security.cert.X509Certificate} - * @throws CertificateException if there is an error generating the new certificate - */ - @SuppressWarnings("deprecation") - public static X509Certificate convertLegacyX509Certificate(javax.security.cert.X509Certificate legacyCertificate) throws CertificateException { - if (legacyCertificate == null) { - throw new IllegalArgumentException("The X.509 certificate cannot be null"); - } - - try { - return formX509Certificate(legacyCertificate.getEncoded()); - } catch (javax.security.cert.CertificateEncodingException e) { - throw new CertificateException(e); - } - } - /** * Accepts an abstract {@link java.security.cert.Certificate} and returns an {@link X509Certificate}. Because {@code sslSocket.getSession().getPeerCertificates()} returns an array of the * abstract certificates, they must be translated to X.509 to replace the functionality of {@code sslSocket.getSession().getPeerCertificateChain()}. diff --git a/nifi-commons/nifi-site-to-site-client/pom.xml b/nifi-commons/nifi-site-to-site-client/pom.xml index e7912f4095..25eb9053dc 100644 --- a/nifi-commons/nifi-site-to-site-client/pom.xml +++ b/nifi-commons/nifi-site-to-site-client/pom.xml @@ -46,16 +46,6 @@ nifi-utils 1.24.0-SNAPSHOT - - org.apache.nifi - nifi-security-utils - 1.24.0-SNAPSHOT - - - org.apache.nifi - nifi-security-utils-api - 1.24.0-SNAPSHOT - org.apache.nifi nifi-security-ssl @@ -136,6 +126,14 @@ validation-api 2.0.1.Final + + jakarta.xml.bind + jakarta.xml.bind-api + + + org.glassfish.jaxb + jaxb-runtime + diff --git a/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java b/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java index 83fce50b97..878d16b1e3 100644 --- a/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java +++ b/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/EndpointConnectionPool.java @@ -26,12 +26,12 @@ import java.net.InetAddress; import java.net.InetSocketAddress; import java.net.Socket; import java.net.URI; -import java.security.cert.CertificateException; import java.util.ArrayList; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Objects; +import java.util.Optional; import java.util.Set; import java.util.concurrent.BlockingQueue; import java.util.concurrent.ConcurrentHashMap; @@ -64,7 +64,6 @@ import org.apache.nifi.remote.io.socket.SocketCommunicationsSession; import org.apache.nifi.remote.protocol.CommunicationsSession; import org.apache.nifi.remote.protocol.SiteToSiteTransportProtocol; import org.apache.nifi.remote.protocol.socket.SocketClientProtocol; -import org.apache.nifi.security.util.CertificateUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -72,6 +71,8 @@ public class EndpointConnectionPool implements PeerStatusProvider { private static final Logger logger = LoggerFactory.getLogger(EndpointConnectionPool.class); + private static final SocketPeerIdentityProvider socketPeerIdentityProvider = new StandardSocketPeerIdentityProvider(); + private final ConcurrentMap> connectionQueueMap = new ConcurrentHashMap<>(); private final Set activeConnections = Collections.synchronizedSet(new HashSet<>()); @@ -449,11 +450,12 @@ public class EndpointConnectionPool implements PeerStatusProvider { socket.setSoTimeout(commsTimeout); commsSession = new SocketCommunicationsSession(socket); - try { - final String dn = CertificateUtils.extractPeerDNFromSSLSocket(socket); - commsSession.setUserDn(dn); - } catch (final CertificateException ex) { - throw new IOException(ex); + final Optional peerIdentity = socketPeerIdentityProvider.getPeerIdentity(socket); + if (peerIdentity.isPresent()) { + final String userDn = peerIdentity.get(); + commsSession.setUserDn(userDn); + } else { + throw new IOException(String.format("Site-to-Site Peer [%s] Identity not found", socket.getRemoteSocketAddress())); } } else { diff --git a/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketPeerIdentityProvider.java b/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketPeerIdentityProvider.java new file mode 100644 index 0000000000..6d1a1c4297 --- /dev/null +++ b/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/SocketPeerIdentityProvider.java @@ -0,0 +1,33 @@ +/* + * 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.remote.client.socket; + +import java.net.Socket; +import java.util.Optional; + +/** + * Abstraction for reading identity information from socket connections + */ +public interface SocketPeerIdentityProvider { + /** + * Get Peer Identity from Socket + * + * @param socket Socket + * @return Peer Identity or empty when not found + */ + Optional getPeerIdentity(Socket socket); +} diff --git a/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/StandardSocketPeerIdentityProvider.java b/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/StandardSocketPeerIdentityProvider.java new file mode 100644 index 0000000000..45a64f1820 --- /dev/null +++ b/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/client/socket/StandardSocketPeerIdentityProvider.java @@ -0,0 +1,74 @@ +/* + * 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.remote.client.socket; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import java.net.Socket; +import java.security.Principal; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Optional; + +/** + * Standard implementation attempts to read X.509 certificates from an SSLSocket + */ +public class StandardSocketPeerIdentityProvider implements SocketPeerIdentityProvider { + private static final Logger logger = LoggerFactory.getLogger(StandardSocketPeerIdentityProvider.class); + + @Override + public Optional getPeerIdentity(final Socket socket) { + final Optional peerIdentity; + + if (socket instanceof SSLSocket) { + final SSLSocket sslSocket = (SSLSocket) socket; + final SSLSession sslSession = sslSocket.getSession(); + peerIdentity = getPeerIdentity(sslSession); + } else { + peerIdentity = Optional.empty(); + } + + return peerIdentity; + } + + private Optional getPeerIdentity(final SSLSession sslSession) { + String peerIdentity = null; + + final String peerHost = sslSession.getPeerHost(); + final int peerPort = sslSession.getPeerPort(); + + try { + final Certificate[] peerCertificates = sslSession.getPeerCertificates(); + if (peerCertificates == null || peerCertificates.length == 0) { + logger.warn("Peer Identity not found: Peer Certificates not provided [{}:{}]", peerHost, peerPort); + } else { + final X509Certificate peerCertificate = (X509Certificate) peerCertificates[0]; + final Principal subjectDistinguishedName = peerCertificate.getSubjectDN(); + peerIdentity = subjectDistinguishedName.getName(); + } + } catch (final SSLPeerUnverifiedException e) { + logger.warn("Peer Identity not found: Peer Unverified [{}:{}]", peerHost, peerPort); + logger.debug("TLS Protocol [{}] Peer Unverified [{}:{}]", sslSession.getProtocol(), peerHost, peerPort, e); + } + + return Optional.ofNullable(peerIdentity); + } +} diff --git a/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/util/SiteToSiteRestApiClient.java b/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/util/SiteToSiteRestApiClient.java index 8726c49ce5..cd34cbccf3 100644 --- a/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/util/SiteToSiteRestApiClient.java +++ b/nifi-commons/nifi-site-to-site-client/src/main/java/org/apache/nifi/remote/util/SiteToSiteRestApiClient.java @@ -47,7 +47,6 @@ import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.charset.StandardCharsets; import java.security.cert.Certificate; -import java.security.cert.CertificateException; import java.security.cert.X509Certificate; import java.util.Arrays; import java.util.Collection; @@ -128,7 +127,6 @@ import org.apache.nifi.remote.protocol.ResponseCode; import org.apache.nifi.remote.protocol.http.HttpHeaders; import org.apache.nifi.remote.protocol.http.HttpProxy; import org.apache.nifi.reporting.Severity; -import org.apache.nifi.security.util.CertificateUtils; import org.apache.nifi.stream.io.StreamUtils; import org.apache.nifi.web.api.dto.ControllerDTO; import org.apache.nifi.web.api.dto.remote.PeerDTO; @@ -319,9 +317,9 @@ public class SiteToSiteRestApiClient implements Closeable { } try { - final X509Certificate cert = CertificateUtils.convertAbstractX509Certificate(certChain[0]); + final X509Certificate cert = (X509Certificate) certChain[0]; trustedPeerDn = cert.getSubjectDN().getName().trim(); - } catch (final CertificateException e) { + } catch (final RuntimeException e) { final String msg = "Could not extract subject DN from SSL session peer certificate"; logger.warn(msg); eventReporter.reportEvent(Severity.WARNING, EVENT_CATEGORY, msg); diff --git a/nifi-commons/nifi-site-to-site-client/src/test/groovy/org/apache/nifi/remote/client/PeerSelectorTest.groovy b/nifi-commons/nifi-site-to-site-client/src/test/groovy/org/apache/nifi/remote/client/PeerSelectorTest.groovy index d644f8e5d1..fc1c76535a 100644 --- a/nifi-commons/nifi-site-to-site-client/src/test/groovy/org/apache/nifi/remote/client/PeerSelectorTest.groovy +++ b/nifi-commons/nifi-site-to-site-client/src/test/groovy/org/apache/nifi/remote/client/PeerSelectorTest.groovy @@ -16,21 +16,16 @@ */ package org.apache.nifi.remote.client - import org.apache.nifi.remote.PeerDescription import org.apache.nifi.remote.PeerStatus import org.apache.nifi.remote.TransferDirection import org.apache.nifi.remote.protocol.SiteToSiteTransportProtocol import org.apache.nifi.remote.util.PeerStatusCache -import org.bouncycastle.jce.provider.BouncyCastleProvider -import org.junit.jupiter.api.AfterEach -import org.junit.jupiter.api.BeforeAll import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.slf4j.Logger import org.slf4j.LoggerFactory -import java.security.Security import java.util.concurrent.ArrayBlockingQueue import static org.junit.jupiter.api.Assertions.assertEquals @@ -52,16 +47,6 @@ class PeerSelectorTest { private static mockPSP private static mockPP - - @BeforeAll - static void setUpOnce() throws Exception { - Security.addProvider(new BouncyCastleProvider()) - - logger.metaClass.methodMissing = { String name, args -> - logger.info("[${name?.toUpperCase()}] ${(args as List).join(" ")}") - } - } - @BeforeEach void setUp() { // Mock collaborators @@ -69,11 +54,6 @@ class PeerSelectorTest { mockPP = mockPeerPersistence() } - @AfterEach - void tearDown() { - - } - private static String buildRemoteInstanceUris(List nodes = DEFAULT_NODES) { String remoteInstanceUris = "http://" + nodes.join(":8443/nifi-api,http://") + ":8443/nifi-api"; remoteInstanceUris @@ -206,7 +186,6 @@ class PeerSelectorTest { new PeerStatusCache(peerStatuses, System.currentTimeMillis(), remoteInstanceUris, SiteToSiteTransportProtocol.HTTP) }, save : { PeerStatusCache psc -> - logger.mock("Persisting PeerStatusCache: ${psc}") }] as PeerPersistence } @@ -985,8 +964,6 @@ class PeerSelectorTest { bootstrapDescription }, fetchRemotePeerStatuses : { PeerDescription pd -> - // Depending on the scenario, return given peer statuses - logger.mock("Scenario ${currentAttempt} fetchRemotePeerStatus for ${pd}") switch (currentAttempt) { case 1: return [bootstrapStatus, node2Status] as Set diff --git a/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/http/TestHttpClient.java b/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/http/TestHttpClient.java index 93a1fe8def..892388708b 100644 --- a/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/http/TestHttpClient.java +++ b/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/http/TestHttpClient.java @@ -22,10 +22,8 @@ import org.apache.nifi.events.EventReporter; import org.apache.nifi.remote.Peer; import org.apache.nifi.remote.Transaction; import org.apache.nifi.remote.TransferDirection; -import org.apache.nifi.remote.client.KeystoreType; import org.apache.nifi.remote.client.SiteToSiteClient; import org.apache.nifi.remote.codec.StandardFlowFileCodec; -import org.apache.nifi.remote.exception.HandshakeException; import org.apache.nifi.remote.io.CompressionInputStream; import org.apache.nifi.remote.io.CompressionOutputStream; import org.apache.nifi.remote.protocol.DataPacket; @@ -34,8 +32,6 @@ import org.apache.nifi.remote.protocol.SiteToSiteTransportProtocol; import org.apache.nifi.remote.protocol.http.HttpHeaders; import org.apache.nifi.remote.protocol.http.HttpProxy; import org.apache.nifi.remote.util.StandardDataPacket; -import org.apache.nifi.security.util.TemporaryKeyStoreBuilder; -import org.apache.nifi.security.util.TlsConfiguration; import org.apache.nifi.stream.io.StreamUtils; import org.apache.nifi.web.api.dto.ControllerDTO; import org.apache.nifi.web.api.dto.PortDTO; @@ -45,16 +41,11 @@ import org.apache.nifi.web.api.entity.PeersEntity; import org.apache.nifi.web.api.entity.TransactionResultEntity; 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.ContextHandlerCollection; import org.eclipse.jetty.servlet.ServletContextHandler; import org.eclipse.jetty.servlet.ServletHandler; -import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.QueuedThreadPool; import org.junit.jupiter.api.AfterAll; import org.junit.jupiter.api.AfterEach; @@ -117,7 +108,6 @@ public class TestHttpClient { private static Server server; private static ServerConnector httpConnector; - private static ServerConnector sslConnector; private static CountDownLatch testCaseFinished; private static HttpProxyServer proxyServer; @@ -126,11 +116,8 @@ public class TestHttpClient { private static Set inputPorts; private static Set outputPorts; private static Set peers; - private static Set peersSecure; private static String serverChecksum; - private static TlsConfiguration tlsConfiguration; - private static final int INITIAL_TRANSACTIONS = 0; private static final AtomicInteger outputExtendTransactions = new AtomicInteger(INITIAL_TRANSACTIONS); @@ -141,16 +128,13 @@ public class TestHttpClient { public static class SiteInfoServlet extends HttpServlet { @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { final ControllerDTO controller = new ControllerDTO(); if (req.getLocalPort() == httpConnector.getLocalPort()) { controller.setRemoteSiteHttpListeningPort(httpConnector.getLocalPort()); controller.setSiteToSiteSecure(false); - } else { - controller.setRemoteSiteHttpListeningPort(sslConnector.getLocalPort()); - controller.setSiteToSiteSecure(true); } controller.setId("remote-controller-id"); @@ -175,7 +159,7 @@ public class TestHttpClient { public static class WrongSiteInfoServlet extends HttpServlet { @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { // This response simulates when a Site-to-Site is given a URL which has wrong path. respondWithText(resp, "

You may have mistyped...

", 200); } @@ -184,16 +168,13 @@ public class TestHttpClient { public static class PeersServlet extends HttpServlet { @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { final PeersEntity peersEntity = new PeersEntity(); if (req.getLocalPort() == httpConnector.getLocalPort()) { assertNotNull(peers, "Test case should set depending on the test scenario."); peersEntity.setPeers(peers); - } else { - assertNotNull(peersSecure, "Test case should set depending on the test scenario."); - peersEntity.setPeers(peersSecure); } respondWithJson(resp, peersEntity); @@ -368,7 +349,7 @@ public class TestHttpClient { } @Override - protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { + protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException { final int reqProtocolVersion = getReqProtocolVersion(req); @@ -477,24 +458,10 @@ public class TestHttpClient { final ServletHandler wrongPathServletHandler = new ServletHandler(); wrongPathContextHandler.insertHandler(wrongPathServletHandler); - final SslContextFactory sslContextFactory = new SslContextFactory.Server(); - - setTlsConfiguration(); - sslContextFactory.setKeyStorePath(tlsConfiguration.getKeystorePath()); - sslContextFactory.setKeyStorePassword(tlsConfiguration.getKeystorePassword()); - sslContextFactory.setKeyStoreType(tlsConfiguration.getKeystoreType().getType()); - sslContextFactory.setProtocol(TlsConfiguration.getHighestCurrentSupportedTlsProtocolVersion()); httpConnector = new ServerConnector(server); - final HttpConfiguration https = new HttpConfiguration(); - https.addCustomizer(new SecureRequestCustomizer()); - sslConnector = new ServerConnector(server, - new SslConnectionFactory(sslContextFactory, "http/1.1"), - new HttpConnectionFactory(https)); - logger.info("SSL Connector: " + sslConnector.dump()); - - server.setConnectors(new Connector[] { httpConnector, sslConnector }); + server.setConnectors(new Connector[] { httpConnector }); wrongPathServletHandler.addServletWithMapping(WrongSiteInfoServlet.class, "/site-to-site"); @@ -528,8 +495,6 @@ public class TestHttpClient { server.start(); - logger.info("Starting server on port {} for HTTP, and {} for HTTPS", httpConnector.getLocalPort(), sslConnector.getLocalPort()); - startProxyServer(); startProxyServerWithAuth(); } @@ -634,15 +599,6 @@ public class TestHttpClient { peers = new HashSet<>(); peers.add(peer); - final PeerDTO peerSecure = new PeerDTO(); - peerSecure.setHostname("localhost"); - peerSecure.setPort(sslConnector.getLocalPort()); - peerSecure.setFlowFileCount(10); - peerSecure.setSecure(true); - - peersSecure = new HashSet<>(); - peersSecure.add(peerSecure); - inputPorts = new HashSet<>(); final PortDTO runningInputPort = new PortDTO(); @@ -711,18 +667,6 @@ public class TestHttpClient { ; } - private SiteToSiteClient.Builder getDefaultBuilderHTTPS() { - return new SiteToSiteClient.Builder().transportProtocol(SiteToSiteTransportProtocol.HTTP) - .url("https://localhost:" + sslConnector.getLocalPort() + "/nifi") - .timeout(3, TimeUnit.MINUTES) - .keystoreFilename(tlsConfiguration.getKeystorePath()) - .keystorePass(tlsConfiguration.getKeystorePassword()) - .keystoreType(KeystoreType.valueOf(tlsConfiguration.getKeystoreType().getType())) - .truststoreFilename(tlsConfiguration.getTruststorePath()) - .truststorePass(tlsConfiguration.getTruststorePassword()) - .truststoreType(KeystoreType.valueOf(tlsConfiguration.getTruststoreType().getType())); - } - private static void consumeDataPacket(DataPacket packet) throws IOException { final ByteArrayOutputStream bos = new ByteArrayOutputStream(); StreamUtils.copy(packet.getData(), bos); @@ -893,31 +837,6 @@ public class TestHttpClient { } - @Test - public void testSendAccessDeniedHTTPS() throws Exception { - - try ( - final SiteToSiteClient client = getDefaultBuilderHTTPS() - .portName("input-access-denied") - .build() - ) { - assertThrows(HandshakeException.class, () -> client.createTransaction(TransferDirection.SEND)); - } - } - - @Test - public void testSendSuccessHTTPS() throws Exception { - - try ( - final SiteToSiteClient client = getDefaultBuilderHTTPS() - .portName("input-running") - .build() - ) { - testSend(client); - } - - } - private interface SendData { void apply(final Transaction transaction) throws IOException; } @@ -1013,47 +932,6 @@ public class TestHttpClient { } - @Test - public void testSendLargeFileHTTPS() throws Exception { - - try ( - SiteToSiteClient client = getDefaultBuilderHTTPS() - .portName("input-running") - .build() - ) { - testSendLargeFile(client); - } - - } - - @Test - public void testSendLargeFileHTTPSWithProxy() throws Exception { - - try ( - SiteToSiteClient client = getDefaultBuilderHTTPS() - .portName("input-running") - .httpProxy(new HttpProxy("localhost", proxyServer.getListenAddress().getPort(), null, null)) - .build() - ) { - testSendLargeFile(client); - } - - } - - @Test - public void testSendLargeFileHTTPSWithProxyAuth() throws Exception { - - try ( - SiteToSiteClient client = getDefaultBuilderHTTPS() - .portName("input-running") - .httpProxy(new HttpProxy("localhost", proxyServerWithAuth.getListenAddress().getPort(), PROXY_USER, PROXY_PASSWORD)) - .build() - ) { - testSendLargeFile(client); - } - - } - @Test public void testSendSuccessCompressed() throws Exception { @@ -1264,44 +1142,6 @@ public class TestHttpClient { } } - @Test - public void testReceiveSuccessHTTPS() throws Exception { - - try ( - SiteToSiteClient client = getDefaultBuilderHTTPS() - .portName("output-running") - .build() - ) { - testReceive(client); - } - } - - @Test - public void testReceiveSuccessHTTPSWithProxy() throws Exception { - - try ( - SiteToSiteClient client = getDefaultBuilderHTTPS() - .portName("output-running") - .httpProxy(new HttpProxy("localhost", proxyServer.getListenAddress().getPort(), null, null)) - .build() - ) { - testReceive(client); - } - } - - @Test - public void testReceiveSuccessHTTPSWithProxyAuth() throws Exception { - - try ( - SiteToSiteClient client = getDefaultBuilderHTTPS() - .portName("output-running") - .httpProxy(new HttpProxy("localhost", proxyServerWithAuth.getListenAddress().getPort(), PROXY_USER, PROXY_PASSWORD)) - .build() - ) { - testReceive(client); - } - } - @Test public void testReceiveSuccessCompressed() throws Exception { @@ -1375,8 +1215,4 @@ public class TestHttpClient { assertNotSame(INITIAL_TRANSACTIONS, outputExtendTransactions.get()); } } - - private static void setTlsConfiguration() { - tlsConfiguration = new TemporaryKeyStoreBuilder().trustStoreType(KeystoreType.JKS.name()).build(); - } } diff --git a/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/socket/StandardSocketPeerIdentityProviderTest.java b/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/socket/StandardSocketPeerIdentityProviderTest.java new file mode 100644 index 0000000000..9fa921050b --- /dev/null +++ b/nifi-commons/nifi-site-to-site-client/src/test/java/org/apache/nifi/remote/client/socket/StandardSocketPeerIdentityProviderTest.java @@ -0,0 +1,103 @@ +/* + * 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.remote.client.socket; + +import org.junit.jupiter.api.BeforeEach; +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.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; +import javax.security.auth.x500.X500Principal; +import java.io.IOException; +import java.net.Socket; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.when; + +@ExtendWith(MockitoExtension.class) +class StandardSocketPeerIdentityProviderTest { + private static final String DISTINGUISHED_NAME = "CN=Common Name,OU=Organizational Unit,O=Organization"; + + @Mock + SSLSocket sslSocket; + + @Mock + SSLSession sslSession; + + @Mock + X509Certificate peerCertificate; + + StandardSocketPeerIdentityProvider provider; + + @BeforeEach + void setProvider() { + provider = new StandardSocketPeerIdentityProvider(); + } + + @Test + void testGetPeerIdentityStandardSocket() throws IOException { + try (Socket socket = new Socket()) { + final Optional peerIdentity = provider.getPeerIdentity(socket); + + assertFalse(peerIdentity.isPresent()); + } + } + + @Test + void testGetPeerIdentitySSLSocketPeerUnverifiedException() throws SSLPeerUnverifiedException { + when(sslSocket.getSession()).thenReturn(sslSession); + when(sslSession.getPeerCertificates()).thenThrow(new SSLPeerUnverifiedException(SSLPeerUnverifiedException.class.getSimpleName())); + + final Optional peerIdentity = provider.getPeerIdentity(sslSocket); + + assertFalse(peerIdentity.isPresent()); + } + + @Test + void testGetPeerIdentitySSLSocketPeerCertificatesNotFound() throws SSLPeerUnverifiedException { + when(sslSocket.getSession()).thenReturn(sslSession); + when(sslSession.getPeerCertificates()).thenReturn(new Certificate[]{}); + + final Optional peerIdentity = provider.getPeerIdentity(sslSocket); + + assertFalse(peerIdentity.isPresent()); + } + + @Test + void testGetPeerIdentityFound() throws SSLPeerUnverifiedException { + when(sslSocket.getSession()).thenReturn(sslSession); + when(sslSession.getPeerCertificates()).thenReturn(new X509Certificate[]{peerCertificate}); + + final X500Principal subjectDistinguishedName = new X500Principal(DISTINGUISHED_NAME); + when(peerCertificate.getSubjectDN()).thenReturn(subjectDistinguishedName); + + final Optional peerIdentity = provider.getPeerIdentity(sslSocket); + + assertTrue(peerIdentity.isPresent()); + final String identity = peerIdentity.get(); + assertEquals(DISTINGUISHED_NAME, identity); + } +} diff --git a/nifi-commons/nifi-utils/pom.xml b/nifi-commons/nifi-utils/pom.xml index ba1b116449..cddbbd4b78 100644 --- a/nifi-commons/nifi-utils/pom.xml +++ b/nifi-commons/nifi-utils/pom.xml @@ -39,6 +39,12 @@ nifi-api 1.24.0-SNAPSHOT + + + org.apache.nifi + nifi-property-utils + 2.0.0-SNAPSHOT + org.hamcrest hamcrest-all diff --git a/nifi-commons/pom.xml b/nifi-commons/pom.xml index edf68a568d..9e3d8ecebf 100644 --- a/nifi-commons/pom.xml +++ b/nifi-commons/pom.xml @@ -55,6 +55,7 @@ nifi-repository-encryption nifi-schema-utils nifi-security-crypto-key + nifi-security-identity nifi-security-kerberos-api nifi-security-kerberos nifi-security-kms diff --git a/nifi-nar-bundles/nifi-email-bundle/nifi-email-processors/pom.xml b/nifi-nar-bundles/nifi-email-bundle/nifi-email-processors/pom.xml index e3d65640e7..71f40c3d24 100644 --- a/nifi-nar-bundles/nifi-email-bundle/nifi-email-processors/pom.xml +++ b/nifi-nar-bundles/nifi-email-bundle/nifi-email-processors/pom.xml @@ -136,6 +136,12 @@ 1.24.0-SNAPSHOT test + + org.apache.nifi + nifi-security-utils + 2.0.0-SNAPSHOT + test + com.icegreen greenmail diff --git a/nifi-nar-bundles/nifi-extension-utils/nifi-event-transport/pom.xml b/nifi-nar-bundles/nifi-extension-utils/nifi-event-transport/pom.xml index 5f083d8537..3eabf7dac0 100644 --- a/nifi-nar-bundles/nifi-extension-utils/nifi-event-transport/pom.xml +++ b/nifi-nar-bundles/nifi-extension-utils/nifi-event-transport/pom.xml @@ -32,6 +32,16 @@ nifi-api 1.24.0-SNAPSHOT + + org.apache.nifi + nifi-security-utils-api + + + org.apache.nifi + nifi-security-utils + 2.0.0-SNAPSHOT + test + org.apache.nifi nifi-mock diff --git a/nifi-nar-bundles/nifi-extension-utils/nifi-record-utils/nifi-standard-record-utils/pom.xml b/nifi-nar-bundles/nifi-extension-utils/nifi-record-utils/nifi-standard-record-utils/pom.xml index e94d2fd14f..b307b0713c 100644 --- a/nifi-nar-bundles/nifi-extension-utils/nifi-record-utils/nifi-standard-record-utils/pom.xml +++ b/nifi-nar-bundles/nifi-extension-utils/nifi-record-utils/nifi-standard-record-utils/pom.xml @@ -38,6 +38,10 @@ nifi-security-socket-ssl 1.24.0-SNAPSHOT + + org.apache.nifi + nifi-security-utils-api + org.apache.nifi diff --git a/nifi-nar-bundles/nifi-extension-utils/nifi-reporting-utils/pom.xml b/nifi-nar-bundles/nifi-extension-utils/nifi-reporting-utils/pom.xml index 3328215597..bc17102e61 100644 --- a/nifi-nar-bundles/nifi-extension-utils/nifi-reporting-utils/pom.xml +++ b/nifi-nar-bundles/nifi-extension-utils/nifi-reporting-utils/pom.xml @@ -34,6 +34,10 @@ nifi-utils 1.24.0-SNAPSHOT + + org.apache.nifi + nifi-security-utils-api + org.glassfish javax.json diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml index 29b1246ffc..1679a4eb01 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-documentation/pom.xml @@ -45,7 +45,6 @@ org.apache.nifi nifi-utils - test org.apache.nifi diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/pom.xml index 868ff8614c..424c55f968 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-file-authorizer/pom.xml @@ -202,7 +202,8 @@ org.apache.nifi - nifi-security-utils + nifi-security-identity + 2.0.0-SNAPSHOT org.apache.nifi diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml index 984c03b33a..50e3a6c719 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-core/pom.xml @@ -77,6 +77,11 @@ nifi-security-kms 1.24.0-SNAPSHOT + + org.apache.nifi + nifi-security-identity + 2.0.0-SNAPSHOT + org.apache.nifi nifi-repository-encryption diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/pom.xml index 501b488bd0..960fc39a0d 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/pom.xml @@ -38,6 +38,15 @@ org.apache.nifi nifi-framework-core-api + + org.apache.nifi + nifi-security-utils-api + + + org.apache.nifi + nifi-security-identity + 2.0.0-SNAPSHOT + org.apache.nifi nifi-mock diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/SocketRemoteSiteListener.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/SocketRemoteSiteListener.java index 5f0284addc..d25edbcf2a 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/SocketRemoteSiteListener.java +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/main/java/org/apache/nifi/remote/SocketRemoteSiteListener.java @@ -26,6 +26,10 @@ import java.net.InetAddress; import java.net.ServerSocket; import java.net.Socket; import java.net.SocketTimeoutException; +import java.security.GeneralSecurityException; +import java.security.Principal; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -33,7 +37,12 @@ import java.util.Optional; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicReference; import javax.net.ssl.SSLContext; +import javax.net.ssl.SSLException; +import javax.net.ssl.SSLPeerUnverifiedException; import javax.net.ssl.SSLServerSocket; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + import org.apache.nifi.groups.ProcessGroup; import org.apache.nifi.remote.cluster.ClusterNodeInformation; import org.apache.nifi.remote.cluster.NodeInformant; @@ -46,7 +55,6 @@ import org.apache.nifi.remote.io.socket.SocketCommunicationsSession; import org.apache.nifi.remote.protocol.CommunicationsSession; import org.apache.nifi.remote.protocol.RequestType; import org.apache.nifi.remote.protocol.ServerProtocol; -import org.apache.nifi.security.util.CertificateUtils; import org.apache.nifi.security.util.TlsConfiguration; import org.apache.nifi.util.NiFiProperties; import org.slf4j.Logger; @@ -160,7 +168,8 @@ public class SocketRemoteSiteListener implements RemoteSiteListener { try { if (secure) { LOG.trace("{} Connection is secure", this); - dn = CertificateUtils.extractPeerDNFromSSLSocket(socket); + final SSLSocket sslSocket = (SSLSocket) socket; + dn = getPeerIdentity(sslSocket); commsSession = new SocketCommunicationsSession(socket); commsSession.setUserDn(dn); @@ -174,7 +183,7 @@ public class SocketRemoteSiteListener implements RemoteSiteListener { // TODO: Add SocketProtocolListener#handleTlsError logic here String msg = String.format("RemoteSiteListener Unable to accept connection from %s due to %s", socket, e.getLocalizedMessage()); // Suppress repeated TLS errors - if (CertificateUtils.isTlsError(e)) { + if (isTlsError(e)) { boolean printedAsWarning = handleTlsError(msg); // TODO: Move into handleTlsError and refactor shared behavior @@ -320,6 +329,32 @@ public class SocketRemoteSiteListener implements RemoteSiteListener { listenerThread.start(); } + private boolean isTlsError(final Throwable e) { + final boolean tlsError; + + if (e instanceof SSLException || e instanceof GeneralSecurityException) { + tlsError = true; + } else if (e.getCause() == null) { + tlsError = false; + } else { + tlsError = isTlsError(e.getCause()); + } + + return tlsError; + } + + private String getPeerIdentity(final SSLSocket sslSocket) throws SSLPeerUnverifiedException { + final SSLSession sslSession = sslSocket.getSession(); + final Certificate[] peerCertificates = sslSession.getPeerCertificates(); + if (peerCertificates == null || peerCertificates.length == 0) { + throw new SSLPeerUnverifiedException(String.format("Peer [%s] certificates not found", sslSocket.getRemoteSocketAddress())); + } + + final X509Certificate peerCertificate = (X509Certificate) peerCertificates[0]; + final Principal subjectDistinguishedName = peerCertificate.getSubjectDN(); + return subjectDistinguishedName.getName(); + } + private boolean handleTlsError(String msg) { if (tlsErrorRecentlySeen()) { LOG.debug(msg); @@ -331,7 +366,7 @@ public class SocketRemoteSiteListener implements RemoteSiteListener { } /** - * Returns {@code true} if any related exception (determined by {@link CertificateUtils#isTlsError(Throwable)}) has occurred within the last + * Returns {@code true} if any related exception has occurred within the last * {@link #EXCEPTION_THRESHOLD_MILLIS} milliseconds. Does not evaluate the error locally, * simply checks the last time the timestamp was updated. * diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/resources/localhost-ks.jks b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/resources/localhost-ks.jks deleted file mode 100755 index 6db775d765c5f59506fcd8953bd0d248b9a40ba5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3076 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 FxB&l(STg_s diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml index 5c911bc404..ed16d77621 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-api/pom.xml @@ -198,6 +198,12 @@ org.apache.nifi nifi-xml-processing + + org.apache.nifi + nifi-security-identity + 2.0.0-SNAPSHOT + provided + org.apache.nifi nifi-web-security diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml index cb475e8930..01994189f7 100644 --- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml +++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/pom.xml @@ -152,6 +152,11 @@ org.apache.nifi nifi-security-utils + + org.apache.nifi + nifi-security-identity + 2.0.0-SNAPSHOT + org.apache.nifi nifi-framework-core diff --git a/nifi-nar-bundles/nifi-gcp-bundle/nifi-gcp-parameter-providers/pom.xml b/nifi-nar-bundles/nifi-gcp-bundle/nifi-gcp-parameter-providers/pom.xml index 7d46735e1e..345d5f778a 100644 --- a/nifi-nar-bundles/nifi-gcp-bundle/nifi-gcp-parameter-providers/pom.xml +++ b/nifi-nar-bundles/nifi-gcp-bundle/nifi-gcp-parameter-providers/pom.xml @@ -32,7 +32,6 @@ org.apache.nifi nifi-utils 1.24.0-SNAPSHOT - provided org.apache.nifi diff --git a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/pom.xml b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/pom.xml index 67a1d44176..c0415fd326 100644 --- a/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/pom.xml +++ b/nifi-nar-bundles/nifi-ldap-iaa-providers-bundle/nifi-ldap-iaa-providers/pom.xml @@ -41,6 +41,11 @@ nifi-security-utils 1.24.0-SNAPSHOT + + org.apache.nifi + nifi-security-identity + 2.0.0-SNAPSHOT + org.apache.nifi nifi-properties diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-record-sink-service-bundle/nifi-record-sink-service/pom.xml b/nifi-nar-bundles/nifi-standard-services/nifi-record-sink-service-bundle/nifi-record-sink-service/pom.xml index ad42c09420..c67812c6f6 100644 --- a/nifi-nar-bundles/nifi-standard-services/nifi-record-sink-service-bundle/nifi-record-sink-service/pom.xml +++ b/nifi-nar-bundles/nifi-standard-services/nifi-record-sink-service-bundle/nifi-record-sink-service/pom.xml @@ -59,6 +59,11 @@ nifi-event-transport 1.24.0-SNAPSHOT + + org.apache.nifi + nifi-security-utils-api + test + org.apache.nifi nifi-mock diff --git a/nifi-nar-bundles/nifi-standard-services/nifi-web-client-provider-bundle/nifi-web-client-provider-service/pom.xml b/nifi-nar-bundles/nifi-standard-services/nifi-web-client-provider-bundle/nifi-web-client-provider-service/pom.xml index bcf2458e7d..0f4ee733c6 100644 --- a/nifi-nar-bundles/nifi-standard-services/nifi-web-client-provider-bundle/nifi-web-client-provider-service/pom.xml +++ b/nifi-nar-bundles/nifi-standard-services/nifi-web-client-provider-bundle/nifi-web-client-provider-service/pom.xml @@ -57,6 +57,12 @@ nifi-utils 1.24.0-SNAPSHOT + + org.apache.nifi + nifi-security-utils + 2.0.0-SNAPSHOT + test + org.apache.nifi nifi-mock