You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nifi.apache.org by mc...@apache.org on 2016/07/06 18:45:55 UTC
nifi git commit: NIFI-2119 Refactored CertificateUtils to separate
logic for DN extraction from server/client sockets. Added logic to detect
server/client mode encapsulated in exposed method. Added unit tests for DN
extraction. Corrected typo in Javadoc.
Repository: nifi
Updated Branches:
refs/heads/0.x 104224343 -> bd69f81af
NIFI-2119 Refactored CertificateUtils to separate logic for DN extraction from server/client sockets. Added logic to detect server/client mode encapsulated in exposed method.
Added unit tests for DN extraction.
Corrected typo in Javadoc.
Switched server/client socket logic for certificate extraction -- when the local socket is in client/server mode, the peer is necessarily the inverse.
Fixed unit tests.
Moved lazy-loading authentication access out of isDebugEnabled() control branch.
This closes #611
Project: http://git-wip-us.apache.org/repos/asf/nifi/repo
Commit: http://git-wip-us.apache.org/repos/asf/nifi/commit/bd69f81a
Tree: http://git-wip-us.apache.org/repos/asf/nifi/tree/bd69f81a
Diff: http://git-wip-us.apache.org/repos/asf/nifi/diff/bd69f81a
Branch: refs/heads/0.x
Commit: bd69f81af5d03d1455b48169725f57caf8d4e883
Parents: 1042243
Author: Andy LoPresto <al...@apache.org>
Authored: Mon Jul 4 21:05:58 2016 -0700
Committer: Matt Gilman <ma...@gmail.com>
Committed: Wed Jul 6 14:44:37 2016 -0400
----------------------------------------------------------------------
.../nifi/security/util/CertificateUtils.java | 72 +++++++++++++++-
.../security/util/CertificateUtilsTest.groovy | 87 +++++++++++++++++---
.../protocol/impl/NodeProtocolSenderImpl.java | 2 +-
.../protocol/impl/SocketProtocolListener.java | 2 +-
.../web/security/NiFiAuthenticationFilter.java | 5 +-
5 files changed, 150 insertions(+), 18 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/nifi/blob/bd69f81a/nifi-commons/nifi-security-utils/src/main/java/org/apache/nifi/security/util/CertificateUtils.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 b3321f7..3579493 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
@@ -166,11 +166,51 @@ public final class CertificateUtils {
return result;
}
- public static String extractClientDNFromSSLSocket(Socket socket) throws CertificateException {
+ /**
+ * 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.
@@ -185,6 +225,7 @@ public final class CertificateUtils {
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)) {
@@ -198,8 +239,35 @@ public final class CertificateUtils {
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;
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/bd69f81a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy
----------------------------------------------------------------------
diff --git a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy
index 2d00a25..22fc628 100644
--- a/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy
+++ b/nifi-commons/nifi-security-utils/src/test/groovy/org/apache/nifi/security/util/CertificateUtilsTest.groovy
@@ -297,16 +297,67 @@ class CertificateUtilsTest extends GroovyTestCase {
assert noneClientAuthStatus == CertificateUtils.ClientAuth.NONE
}
+
+ @Test
+ void testShouldExtractClientCertificatesFromSSLServerSocketWithAnyClientAuth() {
+ final String EXPECTED_DN = "CN=ncm.nifi.apache.org,OU=Security,O=Apache,ST=CA,C=US"
+ Certificate[] certificateChain = generateCertificateChain(EXPECTED_DN)
+ logger.info("Expected DN: ${EXPECTED_DN}")
+ logger.info("Expected certificate chain: ${certificateChain.collect { (it as X509Certificate).getSubjectDN().name }.join(" issued by ")}")
+
+ SSLSession mockSession = [getPeerCertificates: { -> certificateChain }] as SSLSession
+
+ // This socket is in client mode, so the peer ("target") is a server
+
+ // Create mock sockets for each possible value of ClientAuth
+ SSLSocket mockNoneSocket = [
+ getUseClientMode : { -> true },
+ getNeedClientAuth: { -> false },
+ getWantClientAuth: { -> false },
+ getSession : { -> mockSession }
+ ] as SSLSocket
+
+ SSLSocket mockNeedSocket = [
+ getUseClientMode : { -> true },
+ getNeedClientAuth: { -> true },
+ getWantClientAuth: { -> false },
+ getSession : { -> mockSession }
+ ] as SSLSocket
+
+ SSLSocket mockWantSocket = [
+ getUseClientMode : { -> true },
+ getNeedClientAuth: { -> false },
+ getWantClientAuth: { -> true },
+ getSession : { -> mockSession }
+ ] as SSLSocket
+
+ // Act
+ def resolvedServerDNs = [mockNeedSocket, mockWantSocket, mockNoneSocket].collect { SSLSocket mockSocket ->
+ logger.info("Running test with socket ClientAuth setting: ${CertificateUtils.getClientAuthStatus(mockSocket)}")
+ String serverDN = CertificateUtils.extractPeerDNFromSSLSocket(mockNoneSocket)
+ logger.info("Extracted server DN: ${serverDN}")
+ serverDN
+ }
+
+ // Assert
+ assert resolvedServerDNs.every { String serverDN ->
+ CertificateUtils.compareDNs(serverDN, EXPECTED_DN)
+ }
+ }
+
@Test
- void testShouldNotExtractClientCertificatesFromSSLSocketWithClientAuthNone() {
+ void testShouldNotExtractClientCertificatesFromSSLClientSocketWithClientAuthNone() {
// Arrange
+
+ // This socket is in server mode, so the peer ("target") is a client
SSLSocket mockSocket = [
+ getUseClientMode : { -> false },
getNeedClientAuth: { -> false },
getWantClientAuth: { -> false }
] as SSLSocket
// Act
- String clientDN = CertificateUtils.extractClientDNFromSSLSocket(mockSocket)
+ String clientDN = CertificateUtils.extractPeerDNFromSSLSocket(mockSocket)
logger.info("Extracted client DN: ${clientDN}")
// Assert
@@ -314,7 +365,7 @@ class CertificateUtilsTest extends GroovyTestCase {
}
@Test
- void testShouldExtractClientCertificatesFromSSLSocketWithClientAuthWant() {
+ void testShouldExtractClientCertificatesFromSSLClientSocketWithClientAuthWant() {
// Arrange
final String EXPECTED_DN = "CN=client.nifi.apache.org,OU=Security,O=Apache,ST=CA,C=US"
Certificate[] certificateChain = generateCertificateChain(EXPECTED_DN)
@@ -323,14 +374,16 @@ class CertificateUtilsTest extends GroovyTestCase {
SSLSession mockSession = [getPeerCertificates: { -> certificateChain }] as SSLSession
+ // This socket is in server mode, so the peer ("target") is a client
SSLSocket mockSocket = [
+ getUseClientMode : { -> false },
getNeedClientAuth: { -> false },
getWantClientAuth: { -> true },
getSession : { -> mockSession }
] as SSLSocket
// Act
- String clientDN = CertificateUtils.extractClientDNFromSSLSocket(mockSocket)
+ String clientDN = CertificateUtils.extractPeerDNFromSSLSocket(mockSocket)
logger.info("Extracted client DN: ${clientDN}")
// Assert
@@ -338,18 +391,22 @@ class CertificateUtilsTest extends GroovyTestCase {
}
@Test
- void testShouldHandleFailureToExtractClientCertificatesFromSSLSocketWithClientAuthWant() {
+ void testShouldHandleFailureToExtractClientCertificatesFromSSLClientSocketWithClientAuthWant() {
// Arrange
- SSLSession mockSession = [getPeerCertificates: { -> throw new SSLPeerUnverifiedException("peer not authenticated") }] as SSLSession
+ SSLSession mockSession = [getPeerCertificates: { ->
+ throw new SSLPeerUnverifiedException("peer not authenticated")
+ }] as SSLSession
+ // This socket is in server mode, so the peer ("target") is a client
SSLSocket mockSocket = [
+ getUseClientMode : { -> false },
getNeedClientAuth: { -> false },
getWantClientAuth: { -> true },
getSession : { -> mockSession }
] as SSLSocket
// Act
- String clientDN = CertificateUtils.extractClientDNFromSSLSocket(mockSocket)
+ String clientDN = CertificateUtils.extractPeerDNFromSSLSocket(mockSocket)
logger.info("Extracted client DN: ${clientDN}")
// Assert
@@ -358,7 +415,7 @@ class CertificateUtilsTest extends GroovyTestCase {
@Test
- void testShouldExtractClientCertificatesFromSSLSocketWithClientAuthNeed() {
+ void testShouldExtractClientCertificatesFromSSLClientSocketWithClientAuthNeed() {
// Arrange
final String EXPECTED_DN = "CN=client.nifi.apache.org,OU=Security,O=Apache,ST=CA,C=US"
Certificate[] certificateChain = generateCertificateChain(EXPECTED_DN)
@@ -367,14 +424,16 @@ class CertificateUtilsTest extends GroovyTestCase {
SSLSession mockSession = [getPeerCertificates: { -> certificateChain }] as SSLSession
+ // This socket is in server mode, so the peer ("target") is a client
SSLSocket mockSocket = [
+ getUseClientMode : { -> false },
getNeedClientAuth: { -> true },
getWantClientAuth: { -> false },
getSession : { -> mockSession }
] as SSLSocket
// Act
- String clientDN = CertificateUtils.extractClientDNFromSSLSocket(mockSocket)
+ String clientDN = CertificateUtils.extractPeerDNFromSSLSocket(mockSocket)
logger.info("Extracted client DN: ${clientDN}")
// Assert
@@ -382,11 +441,15 @@ class CertificateUtilsTest extends GroovyTestCase {
}
@Test
- void testShouldHandleFailureToExtractClientCertificatesFromSSLSocketWithClientAuthNeed() {
+ void testShouldHandleFailureToExtractClientCertificatesFromSSLClientSocketWithClientAuthNeed() {
// Arrange
- SSLSession mockSession = [getPeerCertificates: { -> throw new SSLPeerUnverifiedException("peer not authenticated") }] as SSLSession
+ SSLSession mockSession = [getPeerCertificates: { ->
+ throw new SSLPeerUnverifiedException("peer not authenticated")
+ }] as SSLSession
+ // This socket is in server mode, so the peer ("target") is a client
SSLSocket mockSocket = [
+ getUseClientMode : { -> false },
getNeedClientAuth: { -> true },
getWantClientAuth: { -> false },
getSession : { -> mockSession }
@@ -394,7 +457,7 @@ class CertificateUtilsTest extends GroovyTestCase {
// Act
def msg = shouldFail(CertificateException) {
- String clientDN = CertificateUtils.extractClientDNFromSSLSocket(mockSocket)
+ String clientDN = CertificateUtils.extractPeerDNFromSSLSocket(mockSocket)
logger.info("Extracted client DN: ${clientDN}")
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/bd69f81a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-protocol/src/main/java/org/apache/nifi/cluster/protocol/impl/NodeProtocolSenderImpl.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-protocol/src/main/java/org/apache/nifi/cluster/protocol/impl/NodeProtocolSenderImpl.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-protocol/src/main/java/org/apache/nifi/cluster/protocol/impl/NodeProtocolSenderImpl.java
index 0457d7a..d747495 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-protocol/src/main/java/org/apache/nifi/cluster/protocol/impl/NodeProtocolSenderImpl.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-protocol/src/main/java/org/apache/nifi/cluster/protocol/impl/NodeProtocolSenderImpl.java
@@ -98,7 +98,7 @@ public class NodeProtocolSenderImpl implements NodeProtocolSender {
private String getNCMDN(Socket socket) {
try {
- return CertificateUtils.extractClientDNFromSSLSocket(socket);
+ return CertificateUtils.extractPeerDNFromSSLSocket(socket);
} catch (CertificateException e) {
throw new ProtocolException(e);
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/bd69f81a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-protocol/src/main/java/org/apache/nifi/cluster/protocol/impl/SocketProtocolListener.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-protocol/src/main/java/org/apache/nifi/cluster/protocol/impl/SocketProtocolListener.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-protocol/src/main/java/org/apache/nifi/cluster/protocol/impl/SocketProtocolListener.java
index 1345df3..15161d9 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-protocol/src/main/java/org/apache/nifi/cluster/protocol/impl/SocketProtocolListener.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-framework-cluster-protocol/src/main/java/org/apache/nifi/cluster/protocol/impl/SocketProtocolListener.java
@@ -182,7 +182,7 @@ public class SocketProtocolListener extends SocketListener implements ProtocolLi
private String getRequestorDN(Socket socket) {
try {
- return CertificateUtils.extractClientDNFromSSLSocket(socket);
+ return CertificateUtils.extractPeerDNFromSSLSocket(socket);
} catch (CertificateException e) {
throw new ProtocolException(e);
}
http://git-wip-us.apache.org/repos/asf/nifi/blob/bd69f81a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
----------------------------------------------------------------------
diff --git a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
index 0520ac8..4b4c66a 100644
--- a/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
+++ b/nifi-nar-bundles/nifi-framework-bundle/nifi-framework/nifi-web/nifi-web-security/src/main/java/org/apache/nifi/web/security/NiFiAuthenticationFilter.java
@@ -52,8 +52,9 @@ public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
@Override
public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain) throws IOException, ServletException {
+ final Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (log.isDebugEnabled()) {
- log.debug("Checking secure context token: " + SecurityContextHolder.getContext().getAuthentication());
+ log.debug("Checking secure context token: " + authentication);
}
if (requiresAuthentication((HttpServletRequest) request)) {
@@ -122,7 +123,7 @@ public abstract class NiFiAuthenticationFilter extends GenericFilterBean {
* the request contains an authentication request but it could not be authenticated.
*
* @param request The request
- * @return The NiFiAutorizationRequestToken used to later authorized the client
+ * @return The NiFiAuthorizationRequestToken used to later authorized the client
* @throws InvalidAuthenticationException If the request contained an authentication attempt, but could not authenticate
*/
public abstract NiFiAuthorizationRequestToken attemptAuthentication(HttpServletRequest request);