Skip to content

Commit 5bbb300

Browse files
committed
HBASE-26666 Another approach on not modifying NettyRpcConnection too much
1 parent a3eeab8 commit 5bbb300

File tree

17 files changed

+2508
-13
lines changed

17 files changed

+2508
-13
lines changed

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
@InterfaceAudience.Private
3434
class BufferCallBeforeInitHandler extends ChannelDuplexHandler {
3535

36+
static final String NAME = "BufferCall";
37+
3638
private enum BufferCallAction {
3739
FLUSH,
3840
FAIL
@@ -77,6 +79,11 @@ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
7779
}
7880
}
7981

82+
@Override
83+
public void flush(ChannelHandlerContext ctx) throws Exception {
84+
// do not flush anything out
85+
}
86+
8087
@Override
8188
public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
8289
if (evt instanceof BufferCallEvent) {

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

Lines changed: 57 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,9 @@
3030
import java.util.concurrent.ScheduledExecutorService;
3131
import java.util.concurrent.ThreadLocalRandom;
3232
import java.util.concurrent.TimeUnit;
33+
import javax.net.ssl.SSLContext;
34+
import javax.net.ssl.SSLEngine;
35+
import org.apache.hadoop.hbase.io.crypto.tls.X509Util;
3336
import org.apache.hadoop.hbase.ipc.BufferCallBeforeInitHandler.BufferCallEvent;
3437
import org.apache.hadoop.hbase.ipc.HBaseRpcController.CancellationCallback;
3538
import org.apache.hadoop.hbase.security.NettyHBaseRpcConnectionHeaderHandler;
@@ -51,10 +54,12 @@
5154
import org.apache.hbase.thirdparty.io.netty.channel.ChannelFuture;
5255
import org.apache.hbase.thirdparty.io.netty.channel.ChannelFutureListener;
5356
import org.apache.hbase.thirdparty.io.netty.channel.ChannelHandler;
57+
import org.apache.hbase.thirdparty.io.netty.channel.ChannelInitializer;
5458
import org.apache.hbase.thirdparty.io.netty.channel.ChannelOption;
5559
import org.apache.hbase.thirdparty.io.netty.channel.ChannelPipeline;
5660
import org.apache.hbase.thirdparty.io.netty.channel.EventLoop;
5761
import org.apache.hbase.thirdparty.io.netty.handler.codec.LengthFieldBasedFrameDecoder;
62+
import org.apache.hbase.thirdparty.io.netty.handler.ssl.SslHandler;
5863
import org.apache.hbase.thirdparty.io.netty.handler.timeout.IdleStateHandler;
5964
import org.apache.hbase.thirdparty.io.netty.handler.timeout.ReadTimeoutHandler;
6065
import org.apache.hbase.thirdparty.io.netty.util.ReferenceCountUtil;
@@ -156,11 +161,11 @@ public void cleanupConnection() {
156161
private void established(Channel ch) throws IOException {
157162
assert eventLoop.inEventLoop();
158163
ChannelPipeline p = ch.pipeline();
159-
String addBeforeHandler = p.context(BufferCallBeforeInitHandler.class).name();
160-
p.addBefore(addBeforeHandler, null,
164+
p.addBefore(BufferCallBeforeInitHandler.NAME, null,
161165
new IdleStateHandler(0, rpcClient.minIdleTimeBeforeClose, 0, TimeUnit.MILLISECONDS));
162-
p.addBefore(addBeforeHandler, null, new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4));
163-
p.addBefore(addBeforeHandler, null,
166+
p.addBefore(BufferCallBeforeInitHandler.NAME, null,
167+
new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE, 0, 4));
168+
p.addBefore(BufferCallBeforeInitHandler.NAME, null,
164169
new NettyRpcDuplexHandler(this, rpcClient.cellBlockBuilder, codec, compressor));
165170
p.fireUserEventTriggered(BufferCallEvent.success());
166171
}
@@ -216,7 +221,8 @@ private void saslNegotiate(final Channel ch) {
216221
failInit(ch, e);
217222
return;
218223
}
219-
ch.pipeline().addFirst(new SaslChallengeDecoder(), saslHandler);
224+
ch.pipeline().addBefore(BufferCallBeforeInitHandler.NAME, null, new SaslChallengeDecoder());
225+
ch.pipeline().addBefore(BufferCallBeforeInitHandler.NAME, null, saslHandler);
220226
saslPromise.addListener(new FutureListener<Boolean>() {
221227

222228
@Override
@@ -274,17 +280,29 @@ private void connect() throws UnknownHostException {
274280
.option(ChannelOption.TCP_NODELAY, rpcClient.isTcpNoDelay())
275281
.option(ChannelOption.SO_KEEPALIVE, rpcClient.tcpKeepAlive)
276282
.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, rpcClient.connectTO)
277-
.handler(new BufferCallBeforeInitHandler()).localAddress(rpcClient.localAddr)
278-
.remoteAddress(remoteAddr).connect().addListener(new ChannelFutureListener() {
283+
.handler(new ChannelInitializer<Channel>() {
279284

280285
@Override
281-
public void operationComplete(ChannelFuture future) throws Exception {
282-
Channel ch = future.channel();
283-
if (!future.isSuccess()) {
284-
failInit(ch, toIOE(future.cause()));
285-
rpcClient.failedServers.addToFailedServers(remoteId.getAddress(), future.cause());
286-
return;
286+
protected void initChannel(Channel ch) throws Exception {
287+
if (conf.getBoolean(X509Util.HBASE_CLIENT_NETTY_TLS_ENABLED, false)) {
288+
X509Util x509Util = new X509Util(conf);
289+
SSLContext sslContext = x509Util.createSSLContextAndOptions().getSSLContext();
290+
SSLEngine sslEngine = sslContext.createSSLEngine(remoteId.address.getHostName(),
291+
remoteId.address.getPort());
292+
sslEngine.setUseClientMode(true);
293+
SslHandler sslHandler = new SslHandler(sslEngine);
294+
sslHandler.setHandshakeTimeoutMillis(
295+
conf.getInt(X509Util.HBASE_CLIENT_NETTY_TLS_HANDSHAKETIMEOUT,
296+
X509Util.DEFAULT_HANDSHAKE_DETECTION_TIMEOUT_MILLIS));
297+
ch.pipeline().addFirst(sslHandler);
287298
}
299+
ch.pipeline().addLast(BufferCallBeforeInitHandler.NAME,
300+
new BufferCallBeforeInitHandler());
301+
}
302+
}).localAddress(rpcClient.localAddr).remoteAddress(remoteAddr).connect()
303+
.addListener(new ChannelFutureListener() {
304+
305+
private void succeed(Channel ch) throws IOException {
288306
ch.writeAndFlush(connectionHeaderPreamble.retainedDuplicate());
289307
if (useSasl) {
290308
saslNegotiate(ch);
@@ -294,6 +312,32 @@ public void operationComplete(ChannelFuture future) throws Exception {
294312
established(ch);
295313
}
296314
}
315+
316+
private void fail(Channel ch, Throwable error) {
317+
failInit(ch, toIOE(error));
318+
rpcClient.failedServers.addToFailedServers(remoteId.getAddress(), error);
319+
}
320+
321+
@Override
322+
public void operationComplete(ChannelFuture future) throws Exception {
323+
Channel ch = future.channel();
324+
if (!future.isSuccess()) {
325+
fail(ch, future.cause());
326+
return;
327+
}
328+
SslHandler sslHandler = ch.pipeline().get(SslHandler.class);
329+
if (sslHandler != null) {
330+
sslHandler.handshakeFuture().addListener(f -> {
331+
if (f.isSuccess()) {
332+
succeed(ch);
333+
} else {
334+
fail(ch, f.cause());
335+
}
336+
});
337+
} else {
338+
succeed(ch);
339+
}
340+
}
297341
}).channel();
298342
}
299343

hbase-common/pom.xml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,16 @@
152152
<artifactId>kerb-simplekdc</artifactId>
153153
<scope>test</scope>
154154
</dependency>
155+
<dependency>
156+
<groupId>org.bouncycastle</groupId>
157+
<artifactId>bcprov-jdk15on</artifactId>
158+
<scope>test</scope>
159+
</dependency>
160+
<dependency>
161+
<groupId>org.bouncycastle</groupId>
162+
<artifactId>bcpkix-jdk15on</artifactId>
163+
<scope>test</scope>
164+
</dependency>
155165
</dependencies>
156166

157167
<build>
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hbase.exceptions;
19+
20+
import org.apache.yetus.audience.InterfaceAudience;
21+
22+
/**
23+
* This file has been copied from the Apache ZooKeeper project.
24+
* @see <a href=
25+
* "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/X509Exception.java">Base
26+
* revision</a>
27+
*/
28+
@SuppressWarnings("serial")
29+
@InterfaceAudience.Private
30+
public class X509Exception extends Exception {
31+
32+
public X509Exception(String message) {
33+
super(message);
34+
}
35+
36+
public X509Exception(Throwable cause) {
37+
super(cause);
38+
}
39+
40+
public X509Exception(String message, Throwable cause) {
41+
super(message, cause);
42+
}
43+
44+
public static class KeyManagerException extends X509Exception {
45+
46+
public KeyManagerException(String message) {
47+
super(message);
48+
}
49+
50+
public KeyManagerException(Throwable cause) {
51+
super(cause);
52+
}
53+
54+
}
55+
56+
public static class TrustManagerException extends X509Exception {
57+
58+
public TrustManagerException(String message) {
59+
super(message);
60+
}
61+
62+
public TrustManagerException(Throwable cause) {
63+
super(cause);
64+
}
65+
66+
}
67+
68+
public static class SSLContextException extends X509Exception {
69+
70+
public SSLContextException(String message) {
71+
super(message);
72+
}
73+
74+
public SSLContextException(Throwable cause) {
75+
super(cause);
76+
}
77+
78+
public SSLContextException(String message, Throwable cause) {
79+
super(message, cause);
80+
}
81+
82+
}
83+
84+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
package org.apache.hadoop.hbase.io.crypto.tls;
19+
20+
import org.apache.yetus.audience.InterfaceAudience;
21+
22+
/**
23+
* This enum represents the file type of a KeyStore or TrustStore. Currently, JKS (Java keystore),
24+
* PEM, PKCS12, and BCFKS types are supported.
25+
* <p/>
26+
* This file has been copied from the Apache ZooKeeper project.
27+
* @see <a href=
28+
* "https://github.com/apache/zookeeper/blob/c74658d398cdc1d207aa296cb6e20de00faec03e/zookeeper-server/src/main/java/org/apache/zookeeper/common/KeyStoreFileType.java">Base
29+
* revision</a>
30+
*/
31+
@InterfaceAudience.Private
32+
public enum KeyStoreFileType {
33+
JKS(".jks"),
34+
PEM(".pem"),
35+
PKCS12(".p12"),
36+
BCFKS(".bcfks");
37+
38+
private final String defaultFileExtension;
39+
40+
KeyStoreFileType(String defaultFileExtension) {
41+
this.defaultFileExtension = defaultFileExtension;
42+
}
43+
44+
/**
45+
* The property string that specifies that a key store or trust store should use this store file
46+
* type.
47+
*/
48+
public String getPropertyValue() {
49+
return this.name();
50+
}
51+
52+
/**
53+
* The file extension that is associated with this file type.
54+
*/
55+
public String getDefaultFileExtension() {
56+
return defaultFileExtension;
57+
}
58+
59+
/**
60+
* Converts a property value to a StoreFileType enum. If the property value is <code>null</code>
61+
* or an empty string, returns <code>null</code>.
62+
* @param propertyValue the property value.
63+
* @return the KeyStoreFileType, or <code>null</code> if <code>propertyValue</code> is
64+
* <code>null</code> or empty.
65+
* @throws IllegalArgumentException if <code>propertyValue</code> is not one of "JKS", "PEM",
66+
* "BCFKS", "PKCS12", or empty/null.
67+
*/
68+
public static KeyStoreFileType fromPropertyValue(String propertyValue) {
69+
if (propertyValue == null || propertyValue.length() == 0) {
70+
return null;
71+
}
72+
return KeyStoreFileType.valueOf(propertyValue.toUpperCase());
73+
}
74+
75+
/**
76+
* Detects the type of KeyStore / TrustStore file from the file extension. If the file name ends
77+
* with ".jks", returns <code>StoreFileType.JKS</code>. If the file name ends with ".pem", returns
78+
* <code>StoreFileType.PEM</code>. If the file name ends with ".p12", returns
79+
* <code>StoreFileType.PKCS12</code>. If the file name ends with ".bckfs", returns
80+
* <code>StoreFileType.BCKFS</code>. Otherwise, throws an IllegalArgumentException.
81+
* @param filename the filename of the key store or trust store file.
82+
* @return a KeyStoreFileType.
83+
* @throws IllegalArgumentException if the filename does not end with ".jks", ".pem", "p12" or
84+
* "bcfks".
85+
*/
86+
public static KeyStoreFileType fromFilename(String filename) {
87+
int i = filename.lastIndexOf('.');
88+
if (i >= 0) {
89+
String extension = filename.substring(i);
90+
for (KeyStoreFileType storeFileType : KeyStoreFileType.values()) {
91+
if (storeFileType.getDefaultFileExtension().equals(extension)) {
92+
return storeFileType;
93+
}
94+
}
95+
}
96+
throw new IllegalArgumentException(
97+
"Unable to auto-detect store file type from file name: " + filename);
98+
}
99+
100+
/**
101+
* If <code>propertyValue</code> is not null or empty, returns the result of
102+
* <code>KeyStoreFileType.fromPropertyValue(propertyValue)</code>. Else, returns the result of
103+
* <code>KeyStoreFileType.fromFileName(filename)</code>.
104+
* @param propertyValue property value describing the KeyStoreFileType, or null/empty to
105+
* auto-detect the type from the file name.
106+
* @param filename file name of the key store file. The file extension is used to auto-detect
107+
* the KeyStoreFileType when <code>propertyValue</code> is null or empty.
108+
* @return a KeyStoreFileType.
109+
* @throws IllegalArgumentException if <code>propertyValue</code> is not one of "JKS", "PEM",
110+
* "PKCS12", "BCFKS", or empty/null.
111+
* @throws IllegalArgumentException if <code>propertyValue</code>is empty or null and the type
112+
* could not be determined from the file name.
113+
*/
114+
public static KeyStoreFileType fromPropertyValueOrFileName(String propertyValue,
115+
String filename) {
116+
KeyStoreFileType result = KeyStoreFileType.fromPropertyValue(propertyValue);
117+
if (result == null) {
118+
result = KeyStoreFileType.fromFilename(filename);
119+
}
120+
return result;
121+
}
122+
}

0 commit comments

Comments
 (0)