1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.hadoop.hbase.security;
20
21 import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.getKeytabFileForTesting;
22 import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.getPrincipalForTesting;
23 import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.getSecuredConfiguration;
24 import static org.junit.Assert.assertEquals;
25 import static org.junit.Assert.assertNotSame;
26 import static org.junit.Assert.assertSame;
27
28 import java.io.File;
29 import java.io.IOException;
30 import java.net.InetSocketAddress;
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.List;
34 import java.util.Properties;
35 import java.util.concurrent.ThreadLocalRandom;
36
37 import com.google.protobuf.RpcController;
38 import com.google.protobuf.ServiceException;
39 import org.apache.hadoop.conf.Configuration;
40 import org.apache.hadoop.fs.CommonConfigurationKeys;
41 import org.apache.hadoop.hbase.Cell;
42 import org.apache.hadoop.hbase.CellScanner;
43 import org.apache.hadoop.hbase.CellUtil;
44 import org.apache.hadoop.hbase.HBaseTestingUtility;
45 import org.apache.hadoop.hbase.HConstants;
46 import org.apache.hadoop.hbase.ServerName;
47 import org.apache.hadoop.hbase.ipc.FifoRpcScheduler;
48 import org.apache.hadoop.hbase.ipc.PayloadCarryingRpcController;
49 import org.apache.hadoop.hbase.ipc.RpcClient;
50 import org.apache.hadoop.hbase.ipc.RpcClientFactory;
51 import org.apache.hadoop.hbase.ipc.RpcServer;
52 import org.apache.hadoop.hbase.ipc.RpcServerInterface;
53 import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos;
54 import org.apache.hadoop.hbase.ipc.protobuf.generated.TestRpcServiceProtos;
55 import org.apache.hadoop.minikdc.MiniKdc;
56 import org.apache.hadoop.security.UserGroupInformation;
57 import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
58 import org.junit.AfterClass;
59 import org.junit.Before;
60 import org.junit.BeforeClass;
61 import org.junit.Rule;
62 import org.junit.Test;
63 import org.junit.rules.ExpectedException;
64 import org.mockito.Mockito;
65
66 import com.google.common.collect.Lists;
67 import com.google.protobuf.BlockingRpcChannel;
68 import com.google.protobuf.BlockingService;
69
70 import javax.security.sasl.SaslException;
71
72 public abstract class AbstractTestSecureIPC {
73
74 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
75
76 private static final File KEYTAB_FILE = new File(TEST_UTIL.getDataTestDir("keytab").toUri()
77 .getPath());
78
79 static final BlockingService SERVICE =
80 TestRpcServiceProtos.TestProtobufRpcProto.newReflectiveBlockingService(
81 new TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface() {
82
83 @Override
84 public TestProtos.EmptyResponseProto ping(RpcController controller,
85 TestProtos.EmptyRequestProto request)
86 throws ServiceException {
87 return null;
88 }
89
90 @Override
91 public TestProtos.EmptyResponseProto error(RpcController controller,
92 TestProtos.EmptyRequestProto request)
93 throws ServiceException {
94 return null;
95 }
96
97 @Override
98 public TestProtos.EchoResponseProto echo(RpcController controller,
99 TestProtos.EchoRequestProto request)
100 throws ServiceException {
101 if (controller instanceof PayloadCarryingRpcController) {
102 PayloadCarryingRpcController pcrc = (PayloadCarryingRpcController) controller;
103
104
105
106
107
108 CellScanner cellScanner = pcrc.cellScanner();
109 List<Cell> list = null;
110 if (cellScanner != null) {
111 list = new ArrayList<Cell>();
112 try {
113 while (cellScanner.advance()) {
114 list.add(cellScanner.current());
115 }
116 } catch (IOException e) {
117 throw new ServiceException(e);
118 }
119 }
120 cellScanner = CellUtil.createCellScanner(list);
121 ((PayloadCarryingRpcController) controller).setCellScanner(cellScanner);
122 }
123 return TestProtos.EchoResponseProto.newBuilder()
124 .setMessage(request.getMessage()).build();
125 }
126 });
127
128 private static MiniKdc KDC;
129 private static String HOST = "localhost";
130 private static String PRINCIPAL;
131
132 String krbKeytab;
133 String krbPrincipal;
134 UserGroupInformation ugi;
135 Configuration clientConf;
136 Configuration serverConf;
137
138 abstract Class<? extends RpcClient> getRpcClientClass();
139
140 @Rule
141 public ExpectedException exception = ExpectedException.none();
142
143 @BeforeClass
144 public static void setUp() throws Exception {
145 Properties conf = MiniKdc.createConf();
146 conf.put(MiniKdc.DEBUG, true);
147 KDC = new MiniKdc(conf, new File(TEST_UTIL.getDataTestDir("kdc").toUri().getPath()));
148 KDC.start();
149 PRINCIPAL = "hbase/" + HOST;
150 KDC.createPrincipal(KEYTAB_FILE, PRINCIPAL);
151 HBaseKerberosUtils.setKeytabFileForTesting(KEYTAB_FILE.getAbsolutePath());
152 HBaseKerberosUtils.setPrincipalForTesting(PRINCIPAL + "@" + KDC.getRealm());
153 }
154
155 @AfterClass
156 public static void tearDown() throws IOException {
157 if (KDC != null) {
158 KDC.stop();
159 }
160 TEST_UTIL.cleanupTestDir();
161 }
162
163 @Before
164 public void setUpTest() throws Exception {
165 krbKeytab = getKeytabFileForTesting();
166 krbPrincipal = getPrincipalForTesting();
167 ugi = loginKerberosPrincipal(krbKeytab, krbPrincipal);
168 clientConf = getSecuredConfiguration();
169 clientConf.set(RpcClientFactory.CUSTOM_RPC_CLIENT_IMPL_CONF_KEY, getRpcClientClass().getName());
170 serverConf = getSecuredConfiguration();
171 }
172
173 @Test
174 public void testRpcCallWithEnabledKerberosSaslAuth() throws Exception {
175 UserGroupInformation ugi2 = UserGroupInformation.getCurrentUser();
176
177
178 assertSame(ugi, ugi2);
179 assertEquals(AuthenticationMethod.KERBEROS, ugi.getAuthenticationMethod());
180 assertEquals(krbPrincipal, ugi.getUserName());
181
182 callRpcService(User.create(ugi2));
183 }
184
185 @Test
186 public void testRpcFallbackToSimpleAuth() throws Exception {
187 String clientUsername = "testuser";
188 UserGroupInformation clientUgi = UserGroupInformation.createUserForTesting(clientUsername,
189 new String[]{clientUsername});
190
191
192 assertNotSame(ugi, clientUgi);
193 assertEquals(AuthenticationMethod.SIMPLE, clientUgi.getAuthenticationMethod());
194 assertEquals(clientUsername, clientUgi.getUserName());
195
196 clientConf.set(User.HBASE_SECURITY_CONF_KEY, "simple");
197 serverConf.setBoolean(RpcServer.FALLBACK_TO_INSECURE_CLIENT_AUTH, true);
198 callRpcService(User.create(clientUgi));
199 }
200
201 void setRpcProtection(String clientProtection, String serverProtection) {
202 clientConf.set("hbase.rpc.protection", clientProtection);
203 serverConf.set("hbase.rpc.protection", serverProtection);
204 }
205
206
207
208
209
210 @Test
211 public void testSaslWithCommonQop() throws Exception {
212 setRpcProtection("privacy,authentication", "authentication");
213 callRpcService(User.create(ugi));
214
215 setRpcProtection("authentication", "privacy,authentication");
216 callRpcService(User.create(ugi));
217
218 setRpcProtection("integrity,authentication", "privacy,authentication");
219 callRpcService(User.create(ugi));
220 }
221
222 @Test
223 public void testSaslNoCommonQop() throws Exception {
224 exception.expect(SaslException.class);
225 exception.expectMessage("No common protection layer between client and server");
226 setRpcProtection("integrity", "privacy");
227 callRpcService(User.create(ugi));
228 }
229
230 private UserGroupInformation loginKerberosPrincipal(String krbKeytab, String krbPrincipal)
231 throws Exception {
232 Configuration cnf = new Configuration();
233 cnf.set(CommonConfigurationKeys.HADOOP_SECURITY_AUTHENTICATION, "kerberos");
234 UserGroupInformation.setConfiguration(cnf);
235 UserGroupInformation.loginUserFromKeytab(krbPrincipal, krbKeytab);
236 return UserGroupInformation.getLoginUser();
237 }
238
239
240
241
242
243 private void callRpcService(User clientUser) throws Exception {
244 SecurityInfo securityInfoMock = Mockito.mock(SecurityInfo.class);
245 Mockito.when(securityInfoMock.getServerPrincipal())
246 .thenReturn(HBaseKerberosUtils.KRB_PRINCIPAL);
247 SecurityInfo.addInfo("TestProtobufRpcProto", securityInfoMock);
248
249 InetSocketAddress isa = new InetSocketAddress(HOST, 0);
250
251 RpcServerInterface rpcServer =
252 new RpcServer(null, "AbstractTestSecureIPC",
253 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface(SERVICE, null)), isa,
254 serverConf, new FifoRpcScheduler(serverConf, 1));
255 rpcServer.start();
256 try (RpcClient rpcClient = RpcClientFactory.createClient(clientConf,
257 HConstants.DEFAULT_CLUSTER_ID.toString())) {
258 InetSocketAddress address = rpcServer.getListenerAddress();
259 if (address == null) {
260 throw new IOException("Listener channel is closed");
261 }
262 BlockingRpcChannel channel =
263 rpcClient.createBlockingRpcChannel(
264 ServerName.valueOf(address.getHostName(), address.getPort(),
265 System.currentTimeMillis()), clientUser, 0);
266 TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface stub =
267 TestRpcServiceProtos.TestProtobufRpcProto.newBlockingStub(channel);
268 List<String> results = new ArrayList<>();
269 TestThread th1 = new TestThread(stub, results);
270 final Throwable exception[] = new Throwable[1];
271 Collections.synchronizedList(new ArrayList<Throwable>());
272 Thread.UncaughtExceptionHandler exceptionHandler =
273 new Thread.UncaughtExceptionHandler() {
274 public void uncaughtException(Thread th, Throwable ex) {
275 exception[0] = ex;
276 }
277 };
278 th1.setUncaughtExceptionHandler(exceptionHandler);
279 th1.start();
280 th1.join();
281 if (exception[0] != null) {
282
283 while (exception[0].getCause() != null) {
284 exception[0] = exception[0].getCause();
285 }
286 throw (Exception) exception[0];
287 }
288 } finally {
289 rpcServer.stop();
290 }
291 }
292
293 public static class TestThread extends Thread {
294 private final TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface stub;
295
296 private final List<String> results;
297
298 public TestThread(TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface stub, List<String> results) {
299 this.stub = stub;
300 this.results = results;
301 }
302
303 @Override
304 public void run() {
305 String result;
306 try {
307 result = stub.echo(null, TestProtos.EchoRequestProto.newBuilder().setMessage(String.valueOf(
308 ThreadLocalRandom.current().nextInt())).build()).getMessage();
309 } catch (ServiceException e) {
310 throw new RuntimeException(e);
311 }
312 if (results != null) {
313 synchronized (results) {
314 results.add(result);
315 }
316 }
317 }
318 }
319 }