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