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;