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.ipc.TestProtobufRpcServiceImpl.SERVICE;
22 import static org.apache.hadoop.hbase.ipc.TestProtobufRpcServiceImpl.newBlockingStub;
23 import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.getKeytabFileForTesting;
24 import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.getPrincipalForTesting;
25 import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.getSecuredConfiguration;
26 import static org.junit.Assert.assertEquals;
27 import static org.junit.Assert.assertNotSame;
28 import static org.junit.Assert.assertSame;
29
30 import com.google.common.collect.Lists;
31 import com.google.protobuf.BlockingService;
32 import com.google.protobuf.ServiceException;
33
34 import java.io.File;
35 import java.io.IOException;
36 import java.net.InetSocketAddress;
37 import java.util.ArrayList;
38 import java.util.Arrays;
39 import java.util.Collection;
40 import java.util.Collections;
41
42 import javax.security.sasl.SaslException;
43
44 import org.apache.commons.lang.RandomStringUtils;
45 import org.apache.hadoop.conf.Configuration;
46 import org.apache.hadoop.fs.CommonConfigurationKeys;
47 import org.apache.hadoop.hbase.HBaseTestingUtility;
48 import org.apache.hadoop.hbase.HConstants;
49 import org.apache.hadoop.hbase.ipc.BlockingRpcClient;
50 import org.apache.hadoop.hbase.ipc.FifoRpcScheduler;
51 import org.apache.hadoop.hbase.ipc.NettyRpcClient;
52 import org.apache.hadoop.hbase.ipc.RpcClient;
53 import org.apache.hadoop.hbase.ipc.RpcClientFactory;
54 import org.apache.hadoop.hbase.ipc.RpcServer;
55 import org.apache.hadoop.hbase.ipc.RpcServerInterface;
56 import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos;
57 import org.apache.hadoop.hbase.ipc.protobuf.generated.TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface;
58 import org.apache.hadoop.hbase.testclassification.SecurityTests;
59 import org.apache.hadoop.hbase.testclassification.SmallTests;
60 import org.apache.hadoop.minikdc.MiniKdc;
61 import org.apache.hadoop.security.UserGroupInformation;
62 import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
63 import org.junit.AfterClass;
64 import org.junit.Before;
65 import org.junit.BeforeClass;
66 import org.junit.Ignore;
67 import org.junit.Rule;
68 import org.junit.Test;
69 import org.junit.experimental.categories.Category;
70 import org.junit.rules.ExpectedException;
71 import org.junit.runner.RunWith;
72 import org.junit.runners.Parameterized;
73 import org.junit.runners.Parameterized.Parameter;
74 import org.junit.runners.Parameterized.Parameters;
75 import org.mockito.Mockito;
76
77 @RunWith(Parameterized.class)
78 @Category({ SecurityTests.class, SmallTests.class })
79 public class TestSecureIPC {
80
81 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility();
82
83 private static final File KEYTAB_FILE =
84 new File(TEST_UTIL.getDataTestDir("keytab").toUri().getPath());
85
86 private static MiniKdc KDC;
87 private static String HOST = "localhost";
88 private static String PRINCIPAL;
89
90 String krbKeytab;
91 String krbPrincipal;
92 UserGroupInformation ugi;
93 Configuration clientConf;
94 Configuration serverConf;
95
96 @Rule
97 public ExpectedException exception = ExpectedException.none();
98
99 @Parameters(name = "{index}: rpcClientImpl={0}")
100 public static Collection<Object[]> parameters() {
101 return Arrays.asList(new Object[] { BlockingRpcClient.class.getName() },
102 new Object[] { NettyRpcClient.class.getName() });
103 }
104
105 @Parameter
106 public String rpcClientImpl;
107
108 @BeforeClass
109 public static void setUp() throws Exception {
110 KDC = TEST_UTIL.setupMiniKdc(KEYTAB_FILE);
111 PRINCIPAL = "hbase/" + HOST;
112 KDC.createPrincipal(KEYTAB_FILE, PRINCIPAL);
113 HBaseKerberosUtils.setPrincipalForTesting(PRINCIPAL + "@" + KDC.getRealm());
114 }
115
116 @AfterClass
117 public static void tearDown() throws IOException {
118 if (KDC != null) {
119 KDC.stop();
120 }
121 TEST_UTIL.cleanupTestDir();
122 }
123
124 @Before
125 public void setUpTest() throws Exception {
126 krbKeytab = getKeytabFileForTesting();
127 krbPrincipal = getPrincipalForTesting();
128 ugi = loginKerberosPrincipal(krbKeytab, krbPrincipal);
129 clientConf = getSecuredConfiguration();
130 clientConf.set(RpcClientFactory.CUSTOM_RPC_CLIENT_IMPL_CONF_KEY, rpcClientImpl);
131 serverConf = getSecuredConfiguration();
132 }
133
134 @Test
135 public void testRpcCallWithEnabledKerberosSaslAuth() throws Exception {
136 UserGroupInformation ugi2 = UserGroupInformation.getCurrentUser();
137
138
139 assertSame(ugi, ugi2);
140 assertEquals(AuthenticationMethod.KERBEROS, ugi.getAuthenticationMethod());
141 assertEquals(krbPrincipal, ugi.getUserName());
142
143 callRpcService(User.create(ugi2));
144 }
145
146 @Test
147 public void testRpcFallbackToSimpleAuth() throws Exception {
148 String clientUsername = "testuser";
149 UserGroupInformation clientUgi =
150 UserGroupInformation.createUserForTesting(clientUsername, new String[] { clientUsername });
151
152
153 assertNotSame(ugi, clientUgi);
154 assertEquals(AuthenticationMethod.SIMPLE, clientUgi.getAuthenticationMethod());
155 assertEquals(clientUsername, clientUgi.getUserName());
156
157 clientConf.set(User.HBASE_SECURITY_CONF_KEY, "simple");
158 serverConf.setBoolean(RpcServer.FALLBACK_TO_INSECURE_CLIENT_AUTH, true);
159 callRpcService(User.create(clientUgi));
160 }
161
162 void setRpcProtection(String clientProtection, String serverProtection) {
163 clientConf.set("hbase.rpc.protection", clientProtection);
164 serverConf.set("hbase.rpc.protection", serverProtection);
165 }
166
167
168
169
170
171 @Test
172 public void testSaslWithCommonQop() throws Exception {
173 setRpcProtection("privacy,authentication", "authentication");
174 callRpcService(User.create(ugi));
175
176 setRpcProtection("authentication", "privacy,authentication");
177 callRpcService(User.create(ugi));
178
179 setRpcProtection("integrity,authentication", "privacy,authentication");
180 callRpcService(User.create(ugi));
181
182 setRpcProtection("integrity,authentication", "integrity,authentication");
183 callRpcService(User.create(ugi));
184
185 setRpcProtection("privacy,authentication", "privacy,authentication");
186 callRpcService(User.create(ugi));
187 }
188
189 @Ignore
190 @Test
191 public void testSaslNoCommonQop() throws Exception {
192 exception.expect(SaslException.class);
193 exception.expectMessage("No common protection layer between client and server");
194 setRpcProtection("integrity", "privacy");
195 callRpcService(User.create(ugi));
196 }
197
198 private UserGroupInformation loginKerberosPrincipal(String krbKeytab, String krbPrincipal)
199 throws Exception {
200 Configuration cnf = new Configuration();
201 cnf.set(CommonConfigurationKeys.HADOOP_SECURITY_AUTHENTICATION, "kerberos");
202 UserGroupInformation.setConfiguration(cnf);
203 UserGroupInformation.loginUserFromKeytab(krbPrincipal, krbKeytab);
204 return UserGroupInformation.getLoginUser();
205 }
206
207
208
209
210
211 private void callRpcService(User clientUser) throws Exception {
212 SecurityInfo securityInfoMock = Mockito.mock(SecurityInfo.class);
213 Mockito.when(securityInfoMock.getServerPrincipal())
214 .thenReturn(HBaseKerberosUtils.KRB_PRINCIPAL);
215 SecurityInfo.addInfo("TestProtobufRpcProto", securityInfoMock);
216
217 InetSocketAddress isa = new InetSocketAddress(HOST, 0);
218
219 RpcServerInterface rpcServer = new RpcServer(null, "AbstractTestSecureIPC",
220 Lists.newArrayList(
221 new RpcServer.BlockingServiceAndInterface((BlockingService) SERVICE, null)),
222 isa, serverConf, new FifoRpcScheduler(serverConf, 1));
223 rpcServer.start();
224 try (RpcClient rpcClient =
225 RpcClientFactory.createClient(clientConf, HConstants.DEFAULT_CLUSTER_ID.toString())) {
226 BlockingInterface stub =
227 newBlockingStub(rpcClient, rpcServer.getListenerAddress(), clientUser);
228 TestThread th1 = new TestThread(stub);
229 final Throwable exception[] = new Throwable[1];
230 Collections.synchronizedList(new ArrayList<Throwable>());
231 Thread.UncaughtExceptionHandler exceptionHandler = new Thread.UncaughtExceptionHandler() {
232 public void uncaughtException(Thread th, Throwable ex) {
233 exception[0] = ex;
234 }
235 };
236 th1.setUncaughtExceptionHandler(exceptionHandler);
237 th1.start();
238 th1.join();
239 if (exception[0] != null) {
240
241 while (exception[0].getCause() != null) {
242 exception[0] = exception[0].getCause();
243 }
244 throw (Exception) exception[0];
245 }
246 } finally {
247 rpcServer.stop();
248 }
249 }
250
251 public static class TestThread extends Thread {
252 private final BlockingInterface stub;
253
254 public TestThread(BlockingInterface stub) {
255 this.stub = stub;
256 }
257
258 @Override
259 public void run() {
260 try {
261 int[] messageSize = new int[] { 100, 1000, 10000 };
262 for (int i = 0; i < messageSize.length; i++) {
263 String input = RandomStringUtils.random(messageSize[i]);
264 String result =
265 stub.echo(null, TestProtos.EchoRequestProto.newBuilder().setMessage(input).build())
266 .getMessage();
267 assertEquals(input, result);
268 }
269 } catch (ServiceException e) {
270 throw new RuntimeException(e);
271 }
272 }
273 }
274 }