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.apache.hadoop.hbase.ipc.TestProtobufRpcServiceImpl.SERVICE; 021 022import java.io.File; 023import java.io.IOException; 024import java.net.InetAddress; 025import java.net.InetSocketAddress; 026import java.security.GeneralSecurityException; 027import java.security.Security; 028import java.util.stream.Stream; 029import org.apache.commons.io.FileUtils; 030import org.apache.hadoop.conf.Configuration; 031import org.apache.hadoop.hbase.HBaseCommonTestingUtil; 032import org.apache.hadoop.hbase.HBaseParameterizedTestTemplate; 033import org.apache.hadoop.hbase.io.crypto.tls.KeyStoreFileType; 034import org.apache.hadoop.hbase.io.crypto.tls.X509KeyType; 035import org.apache.hadoop.hbase.io.crypto.tls.X509TestContext; 036import org.apache.hadoop.hbase.io.crypto.tls.X509TestContextProvider; 037import org.apache.hadoop.hbase.io.crypto.tls.X509Util; 038import org.apache.hadoop.hbase.ipc.FifoRpcScheduler; 039import org.apache.hadoop.hbase.ipc.NettyRpcClient; 040import org.apache.hadoop.hbase.ipc.NettyRpcServer; 041import org.apache.hadoop.hbase.ipc.RpcClient; 042import org.apache.hadoop.hbase.ipc.RpcClientFactory; 043import org.apache.hadoop.hbase.ipc.RpcServer; 044import org.apache.hadoop.hbase.ipc.RpcServerFactory; 045import org.apache.hadoop.hbase.ipc.TestProtobufRpcServiceImpl; 046import org.apache.hadoop.hbase.testclassification.RPCTests; 047import org.apache.hadoop.hbase.testclassification.SmallTests; 048import org.bouncycastle.jce.provider.BouncyCastleProvider; 049import org.bouncycastle.operator.OperatorCreationException; 050import org.junit.jupiter.api.AfterAll; 051import org.junit.jupiter.api.AfterEach; 052import org.junit.jupiter.api.BeforeAll; 053import org.junit.jupiter.api.BeforeEach; 054import org.junit.jupiter.api.Tag; 055import org.junit.jupiter.api.TestTemplate; 056import org.junit.jupiter.params.provider.Arguments; 057 058import org.apache.hbase.thirdparty.com.google.common.collect.Lists; 059import org.apache.hbase.thirdparty.com.google.common.io.Closeables; 060 061import org.apache.hadoop.hbase.shaded.ipc.protobuf.generated.TestProtos; 062import org.apache.hadoop.hbase.shaded.ipc.protobuf.generated.TestRpcServiceProtos; 063 064/** 065 * Tests for client-side mTLS focusing on client hostname verification in the case when client and 066 * server are on different hosts. We try to simulate this behaviour by querying the hostname with 067 * <p> 068 * InetAddress.getLocalHost() 069 * </p> 070 * Certificates are generated with the hostname in Subject Alternative Names, server binds 071 * non-localhost interface and client connects via remote IP address. Parameter is set to verify 072 * both TLS/plaintext and TLS-only cases. 073 */ 074@Tag(RPCTests.TAG) 075@Tag(SmallTests.TAG) 076@HBaseParameterizedTestTemplate(name = "{index}: supportPlaintext={0}") 077public class TestMutualTlsClientSideNonLocalhost { 078 079 private static HBaseCommonTestingUtil UTIL; 080 081 private static File DIR; 082 083 private static X509TestContextProvider PROVIDER; 084 085 private X509TestContext x509TestContext; 086 087 private RpcServer rpcServer; 088 089 private RpcClient rpcClient; 090 private TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface stub; 091 092 private boolean supportPlaintext; 093 094 public static Stream<Arguments> parameters() { 095 return Stream.of(Arguments.of(true), Arguments.of(false)); 096 } 097 098 public TestMutualTlsClientSideNonLocalhost(boolean supportPlaintext) { 099 this.supportPlaintext = supportPlaintext; 100 } 101 102 @BeforeAll 103 public static void setUpBeforeClass() throws IOException { 104 UTIL = new HBaseCommonTestingUtil(); 105 Security.addProvider(new BouncyCastleProvider()); 106 DIR = 107 new File(UTIL.getDataTestDir(AbstractTestTlsRejectPlainText.class.getSimpleName()).toString()) 108 .getCanonicalFile(); 109 FileUtils.forceMkdir(DIR); 110 Configuration conf = UTIL.getConfiguration(); 111 conf.setClass(RpcClientFactory.CUSTOM_RPC_CLIENT_IMPL_CONF_KEY, NettyRpcClient.class, 112 RpcClient.class); 113 conf.setClass(RpcServerFactory.CUSTOM_RPC_SERVER_IMPL_CONF_KEY, NettyRpcServer.class, 114 RpcServer.class); 115 conf.setBoolean(X509Util.HBASE_SERVER_NETTY_TLS_ENABLED, true); 116 conf.setBoolean(X509Util.HBASE_CLIENT_NETTY_TLS_ENABLED, true); 117 PROVIDER = new X509TestContextProvider(conf, DIR); 118 } 119 120 @AfterAll 121 public static void cleanUp() { 122 Security.removeProvider(BouncyCastleProvider.PROVIDER_NAME); 123 UTIL.cleanupTestDir(); 124 } 125 126 @BeforeEach 127 public void setUp() throws Exception { 128 x509TestContext = PROVIDER.get(X509KeyType.RSA, X509KeyType.RSA, "keyPassword".toCharArray()); 129 x509TestContext.setConfigurations(KeyStoreFileType.JKS, KeyStoreFileType.JKS); 130 131 Configuration serverConf = new Configuration(UTIL.getConfiguration()); 132 Configuration clientConf = new Configuration(UTIL.getConfiguration()); 133 134 initialize(serverConf, clientConf); 135 136 InetSocketAddress isa = new InetSocketAddress(InetAddress.getLocalHost(), 0); 137 138 rpcServer = new NettyRpcServer(null, "testRpcServer", 139 Lists.newArrayList(new RpcServer.BlockingServiceAndInterface(SERVICE, null)), isa, serverConf, 140 new FifoRpcScheduler(serverConf, 1), true); 141 rpcServer.start(); 142 143 rpcClient = new NettyRpcClient(clientConf); 144 stub = TestProtobufRpcServiceImpl.newBlockingStub(rpcClient, rpcServer.getListenerAddress()); 145 } 146 147 private void initialize(Configuration serverConf, Configuration clientConf) 148 throws GeneralSecurityException, IOException, OperatorCreationException { 149 serverConf.setBoolean(X509Util.HBASE_SERVER_NETTY_TLS_SUPPORTPLAINTEXT, supportPlaintext); 150 clientConf.setBoolean(X509Util.HBASE_CLIENT_NETTY_TLS_VERIFY_SERVER_HOSTNAME, true); 151 x509TestContext.regenerateStores(X509KeyType.RSA, X509KeyType.RSA, KeyStoreFileType.JKS, 152 KeyStoreFileType.JKS, InetAddress.getLocalHost().getHostName()); 153 } 154 155 @AfterEach 156 public void tearDown() throws IOException { 157 if (rpcServer != null) { 158 rpcServer.stop(); 159 } 160 Closeables.close(rpcClient, true); 161 x509TestContext.clearConfigurations(); 162 x509TestContext.getConf().unset(X509Util.TLS_CONFIG_OCSP); 163 x509TestContext.getConf().unset(X509Util.TLS_CONFIG_CLR); 164 x509TestContext.getConf().unset(X509Util.TLS_CONFIG_PROTOCOL); 165 System.clearProperty("com.sun.net.ssl.checkRevocation"); 166 System.clearProperty("com.sun.security.enableCRLDP"); 167 Security.setProperty("ocsp.enable", Boolean.FALSE.toString()); 168 Security.setProperty("com.sun.security.enableCRLDP", Boolean.FALSE.toString()); 169 } 170 171 @TestTemplate 172 public void testClientAuth() throws Exception { 173 stub.echo(null, TestProtos.EchoRequestProto.newBuilder().setMessage("hello world").build()); 174 } 175}