Skip to content

Commit 51bdd16

Browse files
authored
Backport native TLS to hubspot-2.5 (#49)
* HBASE-27185 Rewrite NettyRpcServer to decode rpc request with netty handler (apache#4624) * HBASE-27185 Addendum fix TestShadeSaslAuthenticationProvider * HBASE-27271 BufferCallBeforeInitHandler should ignore the flush request (apache#4676) * HBASE-26666 Add native TLS encryption support to RPC server/client (apache#4666) * HBASE-27278 Improve TestTlsIPC to reuse existing IPC test code (apache#4682) * HBASE-27279 Make SslHandler work with SaslWrapHandler/SaslUnwrapHandler (apache#4705) * HBASE-27342 Use Hadoop Credentials API to retrieve passwords of TLS key/trust stores (apache#4751) * HBASE-27346 Autodetect key/truststore file type from file extension (apache#4757) * HBASE-27280 Add mutual authentication support to TLS (apache#4796) * HBASE-27673 Fix mTLS client hostname verification (apache#5066) * HBASE-27347 Port FileWatcher from ZK to autodetect keystore/truststore changes in TLS connections (branch-2) (apache#4897) * HBASE-27779 Make X509Util config constants public * HBASE-27578 Upgrade hbase-thirdparty to 4.1.4 (apache#4985)
1 parent b01e8d5 commit 51bdd16

File tree

76 files changed

+8049
-944
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

76 files changed

+8049
-944
lines changed

bin/hbase

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ show_usage() {
107107
echo " cellcounter Run CellCounter tool"
108108
echo " pre-upgrade Run Pre-Upgrade validator tool"
109109
echo " hbtop Run HBTop tool"
110+
echo " credential Run the Hadoop Credential Shell"
110111
echo " CLASSNAME Run the class named CLASSNAME"
111112
}
112113

@@ -759,6 +760,8 @@ elif [ "$COMMAND" = "hbtop" ] ; then
759760
HBASE_HBTOP_OPTS="${HBASE_HBTOP_OPTS} -Dlog4j2.configurationFile=file:${HBASE_HOME}/conf/log4j2-hbtop.properties"
760761
fi
761762
HBASE_OPTS="${HBASE_OPTS} ${HBASE_HBTOP_OPTS}"
763+
elif [ "$COMMAND" = "credential" ] ; then
764+
CLASS='org.apache.hadoop.security.alias.CredentialShell'
762765
else
763766
CLASS=$COMMAND
764767
if [[ "$CLASS" =~ .*IntegrationTest.* ]] ; then

hbase-asyncfs/src/test/java/org/apache/hadoop/hbase/security/HBaseKerberosUtils.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,4 +188,13 @@ public static UserGroupInformation loginAndReturnUGI(Configuration conf, String
188188
UserGroupInformation.loginUserFromKeytabAndReturnUGI(principal, keyTabFileLocation);
189189
return ugi;
190190
}
191+
192+
public static UserGroupInformation loginKerberosPrincipal(String krbKeytab, String krbPrincipal)
193+
throws Exception {
194+
Configuration conf = new Configuration();
195+
conf.set(CommonConfigurationKeys.HADOOP_SECURITY_AUTHENTICATION, "kerberos");
196+
UserGroupInformation.setConfiguration(conf);
197+
UserGroupInformation.loginUserFromKeytab(krbPrincipal, krbKeytab);
198+
return UserGroupInformation.getLoginUser();
199+
}
191200
}

hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/AbstractRpcClient.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ public static String getDefaultCodec(final Configuration c) {
230230
* Encapsulate the ugly casting and RuntimeException conversion in private method.
231231
* @return Codec to use on this client.
232232
*/
233-
Codec getCodec() {
233+
protected Codec getCodec() {
234234
// For NO CODEC, "hbase.client.rpc.codec" must be configured with empty string AND
235235
// "hbase.client.default.rpc.codec" also -- because default is to do cell block encoding.
236236
String className = conf.get(HConstants.RPC_CODEC_CONF_KEY, getDefaultCodec(this.conf));
@@ -251,7 +251,7 @@ public boolean hasCellBlockSupport() {
251251
}
252252

253253
// for writing tests that want to throw exception when connecting.
254-
boolean isTcpNoDelay() {
254+
protected boolean isTcpNoDelay() {
255255
return tcpNoDelay;
256256
}
257257

hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/NettyRpcClient.java

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,14 @@
1919

2020
import java.io.IOException;
2121
import java.net.SocketAddress;
22+
import java.util.concurrent.atomic.AtomicReference;
2223
import org.apache.hadoop.conf.Configuration;
2324
import org.apache.hadoop.hbase.HBaseInterfaceAudience;
2425
import org.apache.hadoop.hbase.HConstants;
2526
import org.apache.hadoop.hbase.client.MetricsConnection;
27+
import org.apache.hadoop.hbase.exceptions.X509Exception;
28+
import org.apache.hadoop.hbase.io.FileChangeWatcher;
29+
import org.apache.hadoop.hbase.io.crypto.tls.X509Util;
2630
import org.apache.hadoop.hbase.util.NettyFutureUtils;
2731
import org.apache.hadoop.hbase.util.Pair;
2832
import org.apache.yetus.audience.InterfaceAudience;
@@ -31,6 +35,7 @@
3135
import org.apache.hbase.thirdparty.io.netty.channel.EventLoopGroup;
3236
import org.apache.hbase.thirdparty.io.netty.channel.nio.NioEventLoopGroup;
3337
import org.apache.hbase.thirdparty.io.netty.channel.socket.nio.NioSocketChannel;
38+
import org.apache.hbase.thirdparty.io.netty.handler.ssl.SslContext;
3439
import org.apache.hbase.thirdparty.io.netty.util.concurrent.DefaultThreadFactory;
3540

3641
/**
@@ -45,6 +50,9 @@ public class NettyRpcClient extends AbstractRpcClient<NettyRpcConnection> {
4550
final Class<? extends Channel> channelClass;
4651

4752
private final boolean shutdownGroupWhenClose;
53+
private final AtomicReference<SslContext> sslContextForClient = new AtomicReference<>();
54+
private final AtomicReference<FileChangeWatcher> keyStoreWatcher = new AtomicReference<>();
55+
private final AtomicReference<FileChangeWatcher> trustStoreWatcher = new AtomicReference<>();
4856

4957
public NettyRpcClient(Configuration configuration, String clusterId, SocketAddress localAddress,
5058
MetricsConnection metrics) {
@@ -67,7 +75,7 @@ public NettyRpcClient(Configuration configuration, String clusterId, SocketAddre
6775
}
6876

6977
/** Used in test only. */
70-
NettyRpcClient(Configuration configuration) {
78+
public NettyRpcClient(Configuration configuration) {
7179
this(configuration, HConstants.CLUSTER_ID_DEFAULT, null, null);
7280
}
7381

@@ -81,5 +89,31 @@ protected void closeInternal() {
8189
if (shutdownGroupWhenClose) {
8290
NettyFutureUtils.consume(group.shutdownGracefully());
8391
}
92+
FileChangeWatcher ks = keyStoreWatcher.getAndSet(null);
93+
if (ks != null) {
94+
ks.stop();
95+
}
96+
FileChangeWatcher ts = trustStoreWatcher.getAndSet(null);
97+
if (ts != null) {
98+
ts.stop();
99+
}
100+
}
101+
102+
SslContext getSslContext() throws X509Exception, IOException {
103+
SslContext result = sslContextForClient.get();
104+
if (result == null) {
105+
result = X509Util.createSslContextForClient(conf);
106+
if (!sslContextForClient.compareAndSet(null, result)) {
107+
// lost the race, another thread already set the value
108+
result = sslContextForClient.get();
109+
} else if (
110+
keyStoreWatcher.get() == null && trustStoreWatcher.get() == null
111+
&& conf.getBoolean(X509Util.TLS_CERT_RELOAD, false)
112+
) {
113+
X509Util.enableCertFileReloading(conf, keyStoreWatcher, trustStoreWatcher,
114+
() -> sslContextForClient.set(null));
115+
}
116+
}
117+
return result;
84118
}
85119
}

hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/NettyRpcConnection.java

Lines changed: 47 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.util.concurrent.ScheduledExecutorService;
3131
import java.util.concurrent.ThreadLocalRandom;
3232
import java.util.concurrent.TimeUnit;
33+
import org.apache.hadoop.hbase.io.crypto.tls.X509Util;
3334
import org.apache.hadoop.hbase.ipc.BufferCallBeforeInitHandler.BufferCallEvent;
3435
import org.apache.hadoop.hbase.ipc.HBaseRpcController.CancellationCallback;
3536
import org.apache.hadoop.hbase.security.NettyHBaseRpcConnectionHeaderHandler;
@@ -56,6 +57,8 @@
5657
import org.apache.hbase.thirdparty.io.netty.channel.ChannelPipeline;
5758
import org.apache.hbase.thirdparty.io.netty.channel.EventLoop;
5859
import org.apache.hbase.thirdparty.io.netty.handler.codec.LengthFieldBasedFrameDecoder;
60+
import org.apache.hbase.thirdparty.io.netty.handler.ssl.SslContext;
61+
import org.apache.hbase.thirdparty.io.netty.handler.ssl.SslHandler;
5962
import org.apache.hbase.thirdparty.io.netty.handler.timeout.IdleStateHandler;
6063
import org.apache.hbase.thirdparty.io.netty.handler.timeout.ReadTimeoutHandler;
6164
import org.apache.hbase.thirdparty.io.netty.util.ReferenceCountUtil;
@@ -219,16 +222,14 @@ private void saslNegotiate(final Channel ch) {
219222
return;
220223
}
221224
ch.pipeline().addBefore(BufferCallBeforeInitHandler.NAME, null, new SaslChallengeDecoder())
222-
.addBefore(BufferCallBeforeInitHandler.NAME, null, saslHandler);
225+
.addBefore(BufferCallBeforeInitHandler.NAME, NettyHBaseSaslRpcClientHandler.HANDLER_NAME,
226+
saslHandler);
223227
NettyFutureUtils.addListener(saslPromise, new FutureListener<Boolean>() {
224228

225229
@Override
226230
public void operationComplete(Future<Boolean> future) throws Exception {
227231
if (future.isSuccess()) {
228232
ChannelPipeline p = ch.pipeline();
229-
p.remove(SaslChallengeDecoder.class);
230-
p.remove(NettyHBaseSaslRpcClientHandler.class);
231-
232233
// check if negotiate with server for connection header is necessary
233234
if (saslHandler.isNeedProcessConnectionHeader()) {
234235
Promise<Boolean> connectionHeaderPromise = ch.eventLoop().newPromise();
@@ -250,7 +251,7 @@ public void operationComplete(Future<Boolean> future) throws Exception {
250251
ChannelPipeline p = ch.pipeline();
251252
p.remove(readTimeoutHandlerName);
252253
p.remove(NettyHBaseRpcConnectionHeaderHandler.class);
253-
// don't send connection header, NettyHBaseRpcConnectionHeaderHandler
254+
// don't send connection header, NettyHbaseRpcConnectionHeaderHandler
254255
// sent it already
255256
established(ch);
256257
} else {
@@ -283,26 +284,26 @@ private void connect() throws UnknownHostException {
283284
.option(ChannelOption.SO_KEEPALIVE, rpcClient.tcpKeepAlive)
284285
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, rpcClient.connectTO)
285286
.handler(new ChannelInitializer<Channel>() {
286-
287287
@Override
288288
protected void initChannel(Channel ch) throws Exception {
289+
if (conf.getBoolean(X509Util.HBASE_CLIENT_NETTY_TLS_ENABLED, false)) {
290+
SslContext sslContext = rpcClient.getSslContext();
291+
SslHandler sslHandler = sslContext.newHandler(ch.alloc(),
292+
remoteId.address.getHostName(), remoteId.address.getPort());
293+
sslHandler.setHandshakeTimeoutMillis(
294+
conf.getInt(X509Util.HBASE_CLIENT_NETTY_TLS_HANDSHAKETIMEOUT,
295+
X509Util.DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS));
296+
ch.pipeline().addFirst(sslHandler);
297+
LOG.info("SSL handler added with handshake timeout {} ms",
298+
sslHandler.getHandshakeTimeoutMillis());
299+
}
289300
ch.pipeline().addLast(BufferCallBeforeInitHandler.NAME,
290301
new BufferCallBeforeInitHandler());
291302
}
292303
}).localAddress(rpcClient.localAddr).remoteAddress(remoteAddr).connect()
293304
.addListener(new ChannelFutureListener() {
294305

295-
@Override
296-
public void operationComplete(ChannelFuture future) throws Exception {
297-
Channel ch = future.channel();
298-
if (!future.isSuccess()) {
299-
IOException ex = toIOE(future.cause());
300-
LOG.warn(
301-
"Exception encountered while connecting to the server " + remoteId.getAddress(), ex);
302-
failInit(ch, ex);
303-
rpcClient.failedServers.addToFailedServers(remoteId.getAddress(), future.cause());
304-
return;
305-
}
306+
private void succeed(Channel ch) throws IOException {
306307
NettyFutureUtils.safeWriteAndFlush(ch, connectionHeaderPreamble.retainedDuplicate());
307308
if (useSasl) {
308309
saslNegotiate(ch);
@@ -312,6 +313,35 @@ public void operationComplete(ChannelFuture future) throws Exception {
312313
established(ch);
313314
}
314315
}
316+
317+
private void fail(Channel ch, Throwable error) {
318+
IOException ex = toIOE(error);
319+
LOG.warn("Exception encountered while connecting to the server " + remoteId.getAddress(),
320+
ex);
321+
failInit(ch, ex);
322+
rpcClient.failedServers.addToFailedServers(remoteId.getAddress(), error);
323+
}
324+
325+
@Override
326+
public void operationComplete(ChannelFuture future) throws Exception {
327+
Channel ch = future.channel();
328+
if (!future.isSuccess()) {
329+
fail(ch, future.cause());
330+
return;
331+
}
332+
SslHandler sslHandler = ch.pipeline().get(SslHandler.class);
333+
if (sslHandler != null) {
334+
NettyFutureUtils.addListener(sslHandler.handshakeFuture(), f -> {
335+
if (f.isSuccess()) {
336+
succeed(ch);
337+
} else {
338+
fail(ch, f.cause());
339+
}
340+
});
341+
} else {
342+
succeed(ch);
343+
}
344+
}
315345
}).channel();
316346
}
317347

hbase-client/src/main/java/org/apache/hadoop/hbase/security/CryptoAESUnwrapHandler.java

Lines changed: 0 additions & 47 deletions
This file was deleted.

hbase-client/src/main/java/org/apache/hadoop/hbase/security/CryptoAESWrapHandler.java

Lines changed: 0 additions & 48 deletions
This file was deleted.

hbase-client/src/main/java/org/apache/hadoop/hbase/security/NettyHBaseRpcConnectionHeaderHandler.java

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import org.apache.hbase.thirdparty.io.netty.channel.ChannelHandlerContext;
2727
import org.apache.hbase.thirdparty.io.netty.channel.ChannelPipeline;
2828
import org.apache.hbase.thirdparty.io.netty.channel.SimpleChannelInboundHandler;
29-
import org.apache.hbase.thirdparty.io.netty.handler.codec.LengthFieldBasedFrameDecoder;
3029
import org.apache.hbase.thirdparty.io.netty.util.concurrent.Promise;
3130

3231
import org.apache.hadoop.hbase.shaded.protobuf.generated.RPCProtos;
@@ -92,10 +91,7 @@ public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
9291
* Remove handlers for sasl encryption and add handlers for Crypto AES encryption
9392
*/
9493
private void setupCryptoAESHandler(ChannelPipeline p, CryptoAES cryptoAES) {
95-
p.remove(SaslWrapHandler.class);
96-
p.remove(SaslUnwrapHandler.class);
97-
String lengthDecoder = p.context(LengthFieldBasedFrameDecoder.class).name();
98-
p.addAfter(lengthDecoder, null, new CryptoAESUnwrapHandler(cryptoAES));
99-
p.addAfter(lengthDecoder, null, new CryptoAESWrapHandler(cryptoAES));
94+
p.replace(SaslWrapHandler.class, null, new SaslWrapHandler(cryptoAES::wrap));
95+
p.replace(SaslUnwrapHandler.class, null, new SaslUnwrapHandler(cryptoAES::unwrap));
10096
}
10197
}

hbase-client/src/main/java/org/apache/hadoop/hbase/security/NettyHBaseSaslRpcClient.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,16 @@ public NettyHBaseSaslRpcClient(Configuration conf, SaslClientAuthenticationProvi
4545
super(conf, provider, token, serverAddr, securityInfo, fallbackAllowed, rpcProtection);
4646
}
4747

48-
public void setupSaslHandler(ChannelPipeline p) {
48+
public void setupSaslHandler(ChannelPipeline p, String addAfter) {
4949
String qop = (String) saslClient.getNegotiatedProperty(Sasl.QOP);
5050
LOG.trace("SASL client context established. Negotiated QoP {}", qop);
5151
if (qop == null || "auth".equalsIgnoreCase(qop)) {
5252
return;
5353
}
5454
// add wrap and unwrap handlers to pipeline.
55-
p.addFirst(new SaslWrapHandler(saslClient),
56-
new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4),
57-
new SaslUnwrapHandler(saslClient));
55+
p.addAfter(addAfter, null, new SaslUnwrapHandler(saslClient::unwrap))
56+
.addAfter(addAfter, null, new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4, 0, 4))
57+
.addAfter(addAfter, null, new SaslWrapHandler(saslClient::wrap));
5858
}
5959

6060
public String getSaslQOP() {

0 commit comments

Comments
 (0)