diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/NettyRpcServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/NettyRpcServer.java index 2043af523de2..1a10a0671d2f 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/NettyRpcServer.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/NettyRpcServer.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.atomic.AtomicReference; +import javax.net.ssl.SSLPeerUnverifiedException; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.hbase.CellScanner; import org.apache.hadoop.hbase.HBaseInterfaceAudience; @@ -443,20 +444,30 @@ private void initSSL(ChannelPipeline p, NettyServerRpcConnection conn, boolean s sslHandler.handshakeFuture().addListener(future -> { try { Certificate[] certificates = sslHandler.engine().getSession().getPeerCertificates(); - if (certificates.length > 0) { - X509Certificate certificate = (X509Certificate) certificates[0]; + if (certificates != null && certificates.length > 0) { // Hack to work around https://github.com/netty/netty/issues/13796, remove once HBase uses Netty 4.1.107.Final or later - if (certificate instanceof LazyX509Certificate) { - Method method = certificate.getClass().getDeclaredMethod("unwrap"); - method.setAccessible(true); - certificate = (X509Certificate) method.invoke(certificate); + for (int i=0; i < certificates.length; i++) { + if (certificates[i] instanceof LazyX509Certificate) { + Method method = LazyX509Certificate.class.getDeclaredMethod("unwrap"); + method.setAccessible(true); + certificates[i] = (X509Certificate) method.invoke(certificates[i]); + } } - conn.clientCertificate = certificate; - } else { - LOG.debug("No client certificate found for peer {}", remoteAddress); + conn.clientCertificateChain = (X509Certificate[]) certificates; + } else if (sslHandler.engine().getNeedClientAuth()) { + LOG.error( + "Could not get peer certificate on TLS connection from {}, although one is required", + remoteAddress); + } + } catch (SSLPeerUnverifiedException e) { + if (sslHandler.engine().getNeedClientAuth()) { + LOG.error( + "Could not get peer certificate on TLS connection from {}, although one is required", + remoteAddress, e); } } catch (Exception e) { - LOG.debug("Failure getting peer certificate for {}", remoteAddress, e); + LOG.error("Unexpected error getting peer certificate for TLS connection from {}", + remoteAddress, e); } }); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcCallContext.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcCallContext.java index 8d8374f7321e..43432324579b 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcCallContext.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/RpcCallContext.java @@ -62,11 +62,11 @@ default Optional getRequestUserName() { } /** - * Returns the TLS certificate that the client presented to this HBase server when making its + * Returns the TLS certificate(s) that the client presented to this HBase server when making its * connection. TLS is orthogonal to Kerberos, so this is unrelated to - * {@link this#getRequestUser()}. Both, one, or neither, may be present. + * {@link RpcCallContext#getRequestUser()}. Both, one, or neither may be present. */ - Optional getClientCertificate(); + Optional getClientCertificateChain(); /** Returns Address of remote client in this call */ InetAddress getRemoteAddress(); diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerCall.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerCall.java index 0f86eecceb23..8d8d443aa127 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerCall.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerCall.java @@ -97,7 +97,7 @@ public abstract class ServerCall implements RpcCa protected final User user; protected final InetAddress remoteAddress; - protected final X509Certificate clientCertificate; + protected final X509Certificate[] clientCertificateChain; protected RpcCallback rpcCallback; private long responseCellSize = 0; @@ -138,11 +138,11 @@ public abstract class ServerCall implements RpcCa if (connection != null) { this.user = connection.user; this.retryImmediatelySupported = connection.retryImmediatelySupported; - this.clientCertificate = connection.clientCertificate; + this.clientCertificateChain = connection.clientCertificateChain; } else { this.user = null; this.retryImmediatelySupported = false; - this.clientCertificate = null; + this.clientCertificateChain = null; } this.remoteAddress = remoteAddress; this.timeout = timeout; @@ -504,8 +504,8 @@ public Optional getRequestUser() { } @Override - public Optional getClientCertificate() { - return Optional.ofNullable(clientCertificate); + public Optional getClientCertificateChain() { + return Optional.ofNullable(clientCertificateChain); } @Override diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerRpcConnection.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerRpcConnection.java index b9bf66a64f98..4c32b2b6a5fa 100644 --- a/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerRpcConnection.java +++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/ServerRpcConnection.java @@ -134,8 +134,7 @@ abstract class ServerRpcConnection implements Closeable { protected User user = null; protected UserGroupInformation ugi = null; protected SaslServerAuthenticationProviders saslProviders = null; - // volatile because this gets set after this object is constructed, when TLS handshake finishes - protected volatile X509Certificate clientCertificate = null; + protected X509Certificate[] clientCertificateChain = null; public ServerRpcConnection(RpcServer rpcServer) { this.rpcServer = rpcServer; diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/namequeues/TestNamedQueueRecorder.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/namequeues/TestNamedQueueRecorder.java index d37bd5dc2e12..144142cc0a95 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/namequeues/TestNamedQueueRecorder.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/namequeues/TestNamedQueueRecorder.java @@ -816,7 +816,7 @@ public Optional getRequestUser() { } @Override - public Optional getClientCertificate() { + public Optional getClientCertificateChain() { return Optional.empty(); } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/namequeues/TestRpcLogDetails.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/namequeues/TestRpcLogDetails.java index ff2c914b79c9..08d082557089 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/namequeues/TestRpcLogDetails.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/namequeues/TestRpcLogDetails.java @@ -215,7 +215,7 @@ public Optional getRequestUser() { } @Override - public Optional getClientCertificate() { + public Optional getClientCertificateChain() { return Optional.empty(); } diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/procedure2/store/region/TestRegionProcedureStore.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/procedure2/store/region/TestRegionProcedureStore.java index 94ce6878788d..5cf601f45b16 100644 --- a/hbase-server/src/test/java/org/apache/hadoop/hbase/procedure2/store/region/TestRegionProcedureStore.java +++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/procedure2/store/region/TestRegionProcedureStore.java @@ -277,7 +277,7 @@ public Optional getRequestUser() { } @Override - public Optional getClientCertificate() { + public Optional getClientCertificateChain() { return Optional.empty(); }