001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase.security; 019 020import static org.hamcrest.MatcherAssert.assertThat; 021import static org.hamcrest.Matchers.instanceOf; 022import static org.junit.Assert.assertEquals; 023import static org.junit.Assert.assertThrows; 024 025import java.io.File; 026import java.io.IOException; 027import java.lang.reflect.UndeclaredThrowableException; 028import java.net.InetSocketAddress; 029import java.security.PrivilegedExceptionAction; 030import java.util.Arrays; 031import java.util.List; 032import org.apache.hadoop.conf.Configuration; 033import org.apache.hadoop.fs.CommonConfigurationKeys; 034import org.apache.hadoop.hbase.HBaseClassTestRule; 035import org.apache.hadoop.hbase.HBaseTestingUtility; 036import org.apache.hadoop.hbase.HConstants; 037import org.apache.hadoop.hbase.ServerName; 038import org.apache.hadoop.hbase.ipc.BlockingRpcClient; 039import org.apache.hadoop.hbase.ipc.FallbackDisallowedException; 040import org.apache.hadoop.hbase.ipc.FifoRpcScheduler; 041import org.apache.hadoop.hbase.ipc.NettyRpcClient; 042import org.apache.hadoop.hbase.ipc.RpcClient; 043import org.apache.hadoop.hbase.ipc.RpcClientFactory; 044import org.apache.hadoop.hbase.ipc.RpcServer; 045import org.apache.hadoop.hbase.ipc.RpcServerFactory; 046import org.apache.hadoop.hbase.ipc.TestProtobufRpcServiceImpl; 047import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos.TokenIdentifier.Kind; 048import org.apache.hadoop.hbase.testclassification.MediumTests; 049import org.apache.hadoop.hbase.testclassification.SecurityTests; 050import org.apache.hadoop.minikdc.MiniKdc; 051import org.apache.hadoop.security.UserGroupInformation; 052import org.junit.After; 053import org.junit.Before; 054import org.junit.BeforeClass; 055import org.junit.ClassRule; 056import org.junit.Test; 057import org.junit.experimental.categories.Category; 058import org.junit.runner.RunWith; 059import org.junit.runners.Parameterized; 060import org.junit.runners.Parameterized.Parameter; 061import org.junit.runners.Parameterized.Parameters; 062 063import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 064import org.apache.hbase.thirdparty.com.google.common.io.Closeables; 065import org.apache.hbase.thirdparty.com.google.protobuf.BlockingRpcChannel; 066 067import org.apache.hadoop.hbase.shaded.ipc.protobuf.generated.TestProtos; 068import org.apache.hadoop.hbase.shaded.ipc.protobuf.generated.TestRpcServiceProtos.TestProtobufRpcProto; 069 070/** 071 * Test secure client connecting to a non secure server, where we have multiple server principal 072 * candidates for a rpc service. See HBASE-28321. 073 */ 074@RunWith(Parameterized.class) 075@Category({ SecurityTests.class, MediumTests.class }) 076public class TestMultipleServerPrincipalsFallbackToSimple { 077 078 @ClassRule 079 public static final HBaseClassTestRule CLASS_RULE = 080 HBaseClassTestRule.forClass(TestMultipleServerPrincipalsFallbackToSimple.class); 081 082 private static final HBaseTestingUtility TEST_UTIL = new HBaseTestingUtility(); 083 084 private static final File KEYTAB_FILE = 085 new File(TEST_UTIL.getDataTestDir("keytab").toUri().getPath()); 086 087 private static MiniKdc KDC; 088 private static String HOST = "localhost"; 089 private static String SERVER_PRINCIPAL; 090 private static String SERVER_PRINCIPAL2; 091 private static String CLIENT_PRINCIPAL; 092 093 @Parameter 094 public Class<? extends RpcClient> rpcClientImpl; 095 096 private Configuration clientConf; 097 private UserGroupInformation clientUGI; 098 private RpcServer rpcServer; 099 private RpcClient rpcClient; 100 101 @Parameters(name = "{index}: rpcClientImpl={0}") 102 public static List<Object[]> params() { 103 return Arrays.asList(new Object[] { NettyRpcClient.class }, 104 new Object[] { BlockingRpcClient.class }); 105 } 106 107 private static void setSecuredConfiguration(Configuration conf) { 108 conf.set(CommonConfigurationKeys.HADOOP_SECURITY_AUTHENTICATION, "kerberos"); 109 conf.set(User.HBASE_SECURITY_CONF_KEY, "kerberos"); 110 conf.setBoolean(User.HBASE_SECURITY_AUTHORIZATION_CONF_KEY, true); 111 } 112 113 @BeforeClass 114 public static void setUpBeforeClass() throws Exception { 115 KDC = TEST_UTIL.setupMiniKdc(KEYTAB_FILE); 116 SERVER_PRINCIPAL = "server/" + HOST; 117 SERVER_PRINCIPAL2 = "server2/" + HOST; 118 CLIENT_PRINCIPAL = "client"; 119 KDC.createPrincipal(KEYTAB_FILE, CLIENT_PRINCIPAL, SERVER_PRINCIPAL, SERVER_PRINCIPAL2); 120 TEST_UTIL.getConfiguration().setInt("hbase.security.relogin.maxbackoff", 1); 121 TEST_UTIL.getConfiguration().setInt("hbase.security.relogin.maxretries", 0); 122 TEST_UTIL.getConfiguration().setInt(RpcClient.FAILED_SERVER_EXPIRY_KEY, 10); 123 } 124 125 @Before 126 public void setUp() throws Exception { 127 clientConf = new Configuration(TEST_UTIL.getConfiguration()); 128 setSecuredConfiguration(clientConf); 129 clientConf.setClass(RpcClientFactory.CUSTOM_RPC_CLIENT_IMPL_CONF_KEY, rpcClientImpl, 130 RpcClient.class); 131 String serverPrincipalConfigName = "hbase.test.multiple.principal.first"; 132 String serverPrincipalConfigName2 = "hbase.test.multiple.principal.second"; 133 clientConf.set(serverPrincipalConfigName, "server/localhost@" + KDC.getRealm()); 134 clientConf.set(serverPrincipalConfigName2, "server2/localhost@" + KDC.getRealm()); 135 SecurityInfo securityInfo = new SecurityInfo(Kind.HBASE_AUTH_TOKEN, serverPrincipalConfigName2, 136 serverPrincipalConfigName); 137 SecurityInfo.addInfo(TestProtobufRpcProto.getDescriptor().getName(), securityInfo); 138 139 UserGroupInformation.setConfiguration(clientConf); 140 clientUGI = UserGroupInformation.loginUserFromKeytabAndReturnUGI(CLIENT_PRINCIPAL, 141 KEYTAB_FILE.getCanonicalPath()); 142 143 rpcServer = RpcServerFactory.createRpcServer(null, getClass().getSimpleName(), 144 Lists.newArrayList( 145 new RpcServer.BlockingServiceAndInterface(TestProtobufRpcServiceImpl.SERVICE, null)), 146 new InetSocketAddress(HOST, 0), TEST_UTIL.getConfiguration(), 147 new FifoRpcScheduler(TEST_UTIL.getConfiguration(), 1)); 148 rpcServer.start(); 149 } 150 151 @After 152 public void tearDown() throws IOException { 153 Closeables.close(rpcClient, true); 154 rpcServer.stop(); 155 } 156 157 private RpcClient createClient() throws Exception { 158 return clientUGI.doAs((PrivilegedExceptionAction<RpcClient>) () -> RpcClientFactory 159 .createClient(clientConf, HConstants.DEFAULT_CLUSTER_ID.toString())); 160 } 161 162 private String echo(String msg) throws Exception { 163 return clientUGI.doAs((PrivilegedExceptionAction<String>) () -> { 164 BlockingRpcChannel channel = rpcClient.createBlockingRpcChannel( 165 ServerName.valueOf(HOST, rpcServer.getListenerAddress().getPort(), -1), User.getCurrent(), 166 10000); 167 TestProtobufRpcProto.BlockingInterface stub = TestProtobufRpcProto.newBlockingStub(channel); 168 return stub.echo(null, TestProtos.EchoRequestProto.newBuilder().setMessage(msg).build()) 169 .getMessage(); 170 }); 171 } 172 173 @Test 174 public void testAllowFallbackToSimple() throws Exception { 175 clientConf.setBoolean(RpcClient.IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_KEY, true); 176 rpcClient = createClient(); 177 assertEquals("allow", echo("allow")); 178 } 179 180 @Test 181 public void testDisallowFallbackToSimple() throws Exception { 182 clientConf.setBoolean(RpcClient.IPC_CLIENT_FALLBACK_TO_SIMPLE_AUTH_ALLOWED_KEY, false); 183 rpcClient = createClient(); 184 UndeclaredThrowableException error = 185 assertThrows(UndeclaredThrowableException.class, () -> echo("disallow")); 186 Throwable cause = error.getCause().getCause().getCause(); 187 assertThat(cause, instanceOf(FallbackDisallowedException.class)); 188 } 189}