This is an automated email from the ASF dual-hosted git repository. mattyb149 pushed a commit to branch support/nifi-1.x in repository https://gitbox.apache.org/repos/asf/nifi.git
commit fec34036a58880947562a8d24ac100ec0f5a82a5 Author: exceptionfactory <[email protected]> AuthorDate: Thu Feb 16 20:51:25 2023 -0600 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 <[email protected]> This closes #6977. --- nifi-commons/nifi-properties/pom.xml | 12 +- .../java/org/apache/nifi/util/StringUtils.java | 0 .../java/org/apache/nifi/util/StringUtilsTest.java | 0 nifi-commons/nifi-security-identity/pom.xml | 35 +++++ .../nifi/authorization/util/IdentityMapping.java | 0 .../authorization/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 +- .../client/socket/SocketPeerIdentityProvider.java | 33 ++++ .../socket/StandardSocketPeerIdentityProvider.java | 74 +++++++++ .../nifi/remote/util/SiteToSiteRestApiClient.java | 6 +- .../nifi/remote/client/PeerSelectorTest.groovy | 23 --- .../nifi/remote/client/http/TestHttpClient.java | 174 +-------------------- .../StandardSocketPeerIdentityProviderTest.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-framework/nifi-file-authorizer/pom.xml | 3 +- .../nifi-framework/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-framework/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(-) 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 @@ --> <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"> <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-commons</artifactId> + <version>1.24.0-SNAPSHOT</version> + </parent> + <artifactId>nifi-properties</artifactId> <dependencies> <dependency> <groupId>org.apache.nifi</groupId> @@ -23,10 +29,4 @@ <scope>compile</scope> </dependency> </dependencies> - <parent> - <groupId>org.apache.nifi</groupId> - <artifactId>nifi-commons</artifactId> - <version>1.24.0-SNAPSHOT</version> - </parent> - <artifactId>nifi-properties</artifactId> </project> 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 @@ +<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>2.0.0-SNAPSHOT</version> + </parent> + <artifactId>nifi-security-identity</artifactId> + <dependencies> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-properties</artifactId> + <version>2.0.0-SNAPSHOT</version> + </dependency> + <dependency> + <groupId>org.apache.commons</groupId> + <artifactId>commons-lang3</artifactId> + </dependency> + </dependencies> +</project> + 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<ASN1ObjectIdentifier, Integer> 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). - * <p> - * 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. - * <p> - * 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 @@ <artifactId>nifi-utils</artifactId> <version>1.24.0-SNAPSHOT</version> </dependency> - <dependency> - <groupId>org.apache.nifi</groupId> - <artifactId>nifi-security-utils</artifactId> - <version>1.24.0-SNAPSHOT</version> - </dependency> - <dependency> - <groupId>org.apache.nifi</groupId> - <artifactId>nifi-security-utils-api</artifactId> - <version>1.24.0-SNAPSHOT</version> - </dependency> <dependency> <groupId>org.apache.nifi</groupId> <artifactId>nifi-security-ssl</artifactId> @@ -136,6 +126,14 @@ <artifactId>validation-api</artifactId> <version>2.0.1.Final</version> </dependency> + <dependency> + <groupId>jakarta.xml.bind</groupId> + <artifactId>jakarta.xml.bind-api</artifactId> + </dependency> + <dependency> + <groupId>org.glassfish.jaxb</groupId> + <artifactId>jaxb-runtime</artifactId> + </dependency> </dependencies> <profiles> <profile> 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<PeerDescription, BlockingQueue<EndpointConnection>> connectionQueueMap = new ConcurrentHashMap<>(); private final Set<EndpointConnection> 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<String> 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<String> 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<String> getPeerIdentity(final Socket socket) { + final Optional<String> 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<String> 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<String> 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<PeerStatus> 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<PortDTO> inputPorts; private static Set<PortDTO> outputPorts; private static Set<PeerDTO> peers; - private static Set<PeerDTO> 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, "<p class=\"message-pane-content\">You may have mistyped...</p>", 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 <peers> depending on the test scenario."); peersEntity.setPeers(peers); - } else { - assertNotNull(peersSecure, "Test case should set <peersSecure> 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<String> 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<String> 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<String> 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<String> 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 @@ <artifactId>nifi-api</artifactId> <version>1.24.0-SNAPSHOT</version> </dependency> + <!-- Included for StringUtils --> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-property-utils</artifactId> + <version>2.0.0-SNAPSHOT</version> + </dependency> <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-all</artifactId> 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 @@ <module>nifi-repository-encryption</module> <module>nifi-schema-utils</module> <module>nifi-security-crypto-key</module> + <module>nifi-security-identity</module> <module>nifi-security-kerberos-api</module> <module>nifi-security-kerberos</module> <module>nifi-security-kms</module> 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 @@ <version>1.24.0-SNAPSHOT</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-utils</artifactId> + <version>2.0.0-SNAPSHOT</version> + <scope>test</scope> + </dependency> <dependency> <groupId>com.icegreen</groupId> <artifactId>greenmail</artifactId> 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 @@ <artifactId>nifi-api</artifactId> <version>1.24.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-utils-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-utils</artifactId> + <version>2.0.0-SNAPSHOT</version> + <scope>test</scope> + </dependency> <dependency> <groupId>org.apache.nifi</groupId> <artifactId>nifi-mock</artifactId> 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 @@ <artifactId>nifi-security-socket-ssl</artifactId> <version>1.24.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-utils-api</artifactId> + </dependency> <!-- Other modules using nifi-standard-record-utils are expected to have these APIs available, typically through a NAR dependency --> <dependency> <groupId>org.apache.nifi</groupId> 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 @@ <artifactId>nifi-utils</artifactId> <version>1.24.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-utils-api</artifactId> + </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.json</artifactId> 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 @@ <dependency> <groupId>org.apache.nifi</groupId> <artifactId>nifi-utils</artifactId> - <scope>test</scope> </dependency> <dependency> <groupId>org.apache.nifi</groupId> 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 @@ </dependency> <dependency> <groupId>org.apache.nifi</groupId> - <artifactId>nifi-security-utils</artifactId> + <artifactId>nifi-security-identity</artifactId> + <version>2.0.0-SNAPSHOT</version> </dependency> <dependency> <groupId>org.apache.nifi</groupId> 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 @@ <artifactId>nifi-security-kms</artifactId> <version>1.24.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-identity</artifactId> + <version>2.0.0-SNAPSHOT</version> + </dependency> <dependency> <groupId>org.apache.nifi</groupId> <artifactId>nifi-repository-encryption</artifactId> 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 @@ <groupId>org.apache.nifi</groupId> <artifactId>nifi-framework-core-api</artifactId> </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-utils-api</artifactId> + </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-identity</artifactId> + <version>2.0.0-SNAPSHOT</version> + </dependency> <dependency> <groupId>org.apache.nifi</groupId> <artifactId>nifi-mock</artifactId> 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 6db775d765..0000000000 Binary files a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/resources/localhost-ks.jks and /dev/null differ diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/resources/localhost-ts.jks b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/resources/localhost-ts.jks deleted file mode 100755 index 5a4fd2d292..0000000000 Binary files a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-site-to-site/src/test/resources/localhost-ts.jks and /dev/null differ 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 @@ <groupId>org.apache.nifi</groupId> <artifactId>nifi-xml-processing</artifactId> </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-identity</artifactId> + <version>2.0.0-SNAPSHOT</version> + <scope>provided</scope> + </dependency> <dependency> <groupId>org.apache.nifi</groupId> <artifactId>nifi-web-security</artifactId> 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 @@ <groupId>org.apache.nifi</groupId> <artifactId>nifi-security-utils</artifactId> </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-identity</artifactId> + <version>2.0.0-SNAPSHOT</version> + </dependency> <dependency> <groupId>org.apache.nifi</groupId> <artifactId>nifi-framework-core</artifactId> 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 @@ <groupId>org.apache.nifi</groupId> <artifactId>nifi-utils</artifactId> <version>1.24.0-SNAPSHOT</version> - <scope>provided</scope> </dependency> <dependency> <groupId>org.apache.nifi</groupId> 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 @@ <artifactId>nifi-security-utils</artifactId> <version>1.24.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-identity</artifactId> + <version>2.0.0-SNAPSHOT</version> + </dependency> <dependency> <groupId>org.apache.nifi</groupId> <artifactId>nifi-properties</artifactId> 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 @@ <artifactId>nifi-event-transport</artifactId> <version>1.24.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-utils-api</artifactId> + <scope>test</scope> + </dependency> <dependency> <groupId>org.apache.nifi</groupId> <artifactId>nifi-mock</artifactId> 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 @@ <artifactId>nifi-utils</artifactId> <version>1.24.0-SNAPSHOT</version> </dependency> + <dependency> + <groupId>org.apache.nifi</groupId> + <artifactId>nifi-security-utils</artifactId> + <version>2.0.0-SNAPSHOT</version> + <scope>test</scope> + </dependency> <dependency> <groupId>org.apache.nifi</groupId> <artifactId>nifi-mock</artifactId>
