This is an automated email from the ASF dual-hosted git repository.

bbeaudreault pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hbase.git


The following commit(s) were added to refs/heads/master by this push:
     new 97574a265b3 HBASE-28317 Expose client TLS certificate on 
RpcCallContext (#5644)
97574a265b3 is described below

commit 97574a265b31a4b8ecdbad1709c0226cb3212825
Author: Charles Connell <[email protected]>
AuthorDate: Mon Jan 29 08:04:06 2024 -0500

    HBASE-28317 Expose client TLS certificate on RpcCallContext (#5644)
    
    Signed-off-by: Duo Zhang <[email protected]>
    Signed-off-by: Bryan Beaudreault <[email protected]>
---
 .../apache/hadoop/hbase/ipc/NettyRpcServer.java    | 31 +++++++++++++++++++---
 .../apache/hadoop/hbase/ipc/RpcCallContext.java    |  8 ++++++
 .../org/apache/hadoop/hbase/ipc/ServerCall.java    |  9 +++++++
 .../hadoop/hbase/ipc/ServerRpcConnection.java      |  2 ++
 .../hbase/namequeues/TestNamedQueueRecorder.java   |  6 +++++
 .../hadoop/hbase/namequeues/TestRpcLogDetails.java |  6 +++++
 .../store/region/TestRegionProcedureStore.java     |  6 +++++
 7 files changed, 65 insertions(+), 3 deletions(-)

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 ceff84a90e1..c291338e40c 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
@@ -27,9 +27,12 @@ import java.io.IOException;
 import java.io.InterruptedIOException;
 import java.net.InetSocketAddress;
 import java.net.SocketAddress;
+import java.security.cert.Certificate;
+import java.security.cert.X509Certificate;
 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.HBaseInterfaceAudience;
 import org.apache.hadoop.hbase.HBaseServerBase;
@@ -166,10 +169,10 @@ public class NettyRpcServer extends RpcServer {
           ChannelPipeline pipeline = ch.pipeline();
           FixedLengthFrameDecoder preambleDecoder = new 
FixedLengthFrameDecoder(6);
           preambleDecoder.setSingleDecode(true);
+          NettyServerRpcConnection conn = createNettyServerRpcConnection(ch);
           if (conf.getBoolean(HBASE_SERVER_NETTY_TLS_ENABLED, false)) {
-            initSSL(pipeline, 
conf.getBoolean(HBASE_SERVER_NETTY_TLS_SUPPORTPLAINTEXT, true));
+            initSSL(pipeline, conn, 
conf.getBoolean(HBASE_SERVER_NETTY_TLS_SUPPORTPLAINTEXT, true));
           }
-          NettyServerRpcConnection conn = createNettyServerRpcConnection(ch);
           pipeline.addLast(NettyRpcServerPreambleHandler.DECODER_NAME, 
preambleDecoder)
             .addLast(new NettyRpcServerPreambleHandler(NettyRpcServer.this, 
conn))
             // We need NettyRpcServerResponseEncoder here because 
NettyRpcServerPreambleHandler may
@@ -378,7 +381,7 @@ public class NettyRpcServer extends RpcServer {
     return allChannels.size();
   }
 
-  private void initSSL(ChannelPipeline p, boolean supportPlaintext)
+  private void initSSL(ChannelPipeline p, NettyServerRpcConnection conn, 
boolean supportPlaintext)
     throws X509Exception, IOException {
     SslContext nettySslContext = getSslContext();
 
@@ -413,6 +416,28 @@ public class NettyRpcServer extends RpcServer {
       sslHandler.setWrapDataSize(
         conf.getInt(HBASE_SERVER_NETTY_TLS_WRAP_SIZE, 
DEFAULT_HBASE_SERVER_NETTY_TLS_WRAP_SIZE));
 
+      sslHandler.handshakeFuture().addListener(future -> {
+        try {
+          Certificate[] certificates = 
sslHandler.engine().getSession().getPeerCertificates();
+          if (certificates != null && certificates.length > 0) {
+            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.error("Unexpected error getting peer certificate for TLS 
connection from {}",
+            remoteAddress, e);
+        }
+      });
+
       p.addLast("ssl", sslHandler);
       LOG.debug("SSL handler added for channel: {}", p.channel());
     }
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 4f299b4a85d..43432324579 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
@@ -18,6 +18,7 @@
 package org.apache.hadoop.hbase.ipc;
 
 import java.net.InetAddress;
+import java.security.cert.X509Certificate;
 import java.util.Optional;
 import org.apache.hadoop.hbase.security.User;
 import org.apache.yetus.audience.InterfaceAudience;
@@ -60,6 +61,13 @@ public interface RpcCallContext {
     return getRequestUser().map(User::getShortName);
   }
 
+  /**
+   * 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 RpcCallContext#getRequestUser()}. Both, one, or neither may be 
present.
+   */
+  Optional<X509Certificate[]> 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 a2c578fd666..25d153c068a 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
@@ -23,6 +23,7 @@ import io.opentelemetry.context.Scope;
 import java.io.IOException;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
+import java.security.cert.X509Certificate;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -95,6 +96,7 @@ public abstract class ServerCall<T extends 
ServerRpcConnection> implements RpcCa
 
   protected final User user;
   protected final InetAddress remoteAddress;
+  protected final X509Certificate[] clientCertificateChain;
   protected RpcCallback rpcCallback;
 
   private long responseCellSize = 0;
@@ -135,9 +137,11 @@ public abstract class ServerCall<T extends 
ServerRpcConnection> implements RpcCa
     if (connection != null) {
       this.user = connection.user;
       this.retryImmediatelySupported = connection.retryImmediatelySupported;
+      this.clientCertificateChain = connection.clientCertificateChain;
     } else {
       this.user = null;
       this.retryImmediatelySupported = false;
+      this.clientCertificateChain = null;
     }
     this.remoteAddress = remoteAddress;
     this.timeout = timeout;
@@ -499,6 +503,11 @@ public abstract class ServerCall<T extends 
ServerRpcConnection> implements RpcCa
     return Optional.ofNullable(user);
   }
 
+  @Override
+  public Optional<X509Certificate[]> getClientCertificateChain() {
+    return Optional.ofNullable(clientCertificateChain);
+  }
+
   @Override
   public InetAddress getRemoteAddress() {
     return remoteAddress;
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 695f1e7050c..4c32b2b6a5f 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
@@ -31,6 +31,7 @@ import java.net.InetAddress;
 import java.net.InetSocketAddress;
 import java.nio.ByteBuffer;
 import java.security.GeneralSecurityException;
+import java.security.cert.X509Certificate;
 import java.util.Collections;
 import java.util.Map;
 import java.util.Objects;
@@ -133,6 +134,7 @@ abstract class ServerRpcConnection implements Closeable {
   protected User user = null;
   protected UserGroupInformation ugi = null;
   protected SaslServerAuthenticationProviders saslProviders = 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 00953353187..124214e46af 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
@@ -22,6 +22,7 @@ import java.lang.reflect.Constructor;
 import java.net.InetAddress;
 import java.security.PrivilegedAction;
 import java.security.PrivilegedExceptionAction;
+import java.security.cert.X509Certificate;
 import java.util.Collections;
 import java.util.List;
 import java.util.Map;
@@ -814,6 +815,11 @@ public class TestNamedQueueRecorder {
         return getUser(userName);
       }
 
+      @Override
+      public Optional<X509Certificate[]> getClientCertificateChain() {
+        return Optional.empty();
+      }
+
       @Override
       public InetAddress getRemoteAddress() {
         return null;
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 67d8a257909..1de0a0d31a3 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
@@ -24,6 +24,7 @@ import static org.junit.Assert.assertFalse;
 import java.io.IOException;
 import java.net.InetAddress;
 import java.nio.ByteBuffer;
+import java.security.cert.X509Certificate;
 import java.util.Arrays;
 import java.util.Collections;
 import java.util.Map;
@@ -213,6 +214,11 @@ public class TestRpcLogDetails {
         return null;
       }
 
+      @Override
+      public Optional<X509Certificate[]> getClientCertificateChain() {
+        return Optional.empty();
+      }
+
       @Override
       public InetAddress getRemoteAddress() {
         return null;
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 d069c2560a5..fdd5c7d5cf9 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
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue;
 
 import java.io.IOException;
 import java.net.InetAddress;
+import java.security.cert.X509Certificate;
 import java.util.HashSet;
 import java.util.Map;
 import java.util.Optional;
@@ -275,6 +276,11 @@ public class TestRegionProcedureStore extends 
RegionProcedureStoreTestBase {
         return Optional.empty();
       }
 
+      @Override
+      public Optional<X509Certificate[]> getClientCertificateChain() {
+        return Optional.empty();
+      }
+
       @Override
       public InetAddress getRemoteAddress() {
         return null;

Reply via email to