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 java.io.IOException; 021import java.security.GeneralSecurityException; 022import java.util.ArrayList; 023import java.util.List; 024import org.apache.hadoop.conf.Configuration; 025import org.apache.hadoop.hbase.HBaseClassTestRule; 026import org.apache.hadoop.hbase.io.crypto.tls.X509KeyType; 027import org.apache.hadoop.hbase.io.crypto.tls.X509Util; 028import org.apache.hadoop.hbase.testclassification.MediumTests; 029import org.apache.hadoop.hbase.testclassification.RPCTests; 030import org.bouncycastle.operator.OperatorCreationException; 031import org.junit.ClassRule; 032import org.junit.experimental.categories.Category; 033import org.junit.runner.RunWith; 034import org.junit.runners.Parameterized; 035 036/** 037 * Comprehensively tests all permutations of ClientAuth modes and host verification 038 * enabled/disabled. Tests each permutation of that against each relevant value of 039 * {@link CertConfig}, i.e. passing no cert, a bad cert, etc. See inline comments in {@link #data()} 040 * below for what the expectations are 041 */ 042@RunWith(Parameterized.class) 043@Category({ RPCTests.class, MediumTests.class }) 044public class TestMutualTlsServerSide extends AbstractTestMutualTls { 045 046 @ClassRule 047 public static final HBaseClassTestRule CLASS_RULE = 048 HBaseClassTestRule.forClass(TestMutualTlsServerSide.class); 049 @Parameterized.Parameter(6) 050 public X509Util.ClientAuth clientAuthMode; 051 052 @Parameterized.Parameters(name = "{index}: caKeyType={0}, certKeyType={1}, keyPassword={2}, " 053 + "validateClientHostnames={3}, testCase={4}, clientAuthMode={5}") 054 public static List<Object[]> data() { 055 List<Object[]> params = new ArrayList<>(); 056 for (X509KeyType caKeyType : X509KeyType.values()) { 057 for (X509KeyType certKeyType : X509KeyType.values()) { 058 for (String keyPassword : new String[] { "", "pa$$w0rd" }) { 059 // we want to run with and without validating hostnames. we encode the expected success 060 // criteria 061 // in the TestCase config. See below. 062 for (boolean validateClientHostnames : new Boolean[] { true, false }) { 063 // ClientAuth.NONE should succeed in all cases, because it never requests the 064 // certificate for verification 065 params.add(new Object[] { caKeyType, certKeyType, keyPassword, true, 066 validateClientHostnames, CertConfig.NO_CLIENT_CERT, X509Util.ClientAuth.NONE }); 067 params.add(new Object[] { caKeyType, certKeyType, keyPassword, true, 068 validateClientHostnames, CertConfig.NON_VERIFIABLE_CERT, X509Util.ClientAuth.NONE }); 069 params.add( 070 new Object[] { caKeyType, certKeyType, keyPassword, true, validateClientHostnames, 071 CertConfig.VERIFIABLE_CERT_WITH_BAD_HOST, X509Util.ClientAuth.NONE }); 072 073 // ClientAuth.WANT should succeed if no cert, but if the cert is provided it is 074 // validated. So should fail on bad cert or good cert with bad host when host 075 // verification is enabled 076 params.add(new Object[] { caKeyType, certKeyType, keyPassword, true, 077 validateClientHostnames, CertConfig.NO_CLIENT_CERT, X509Util.ClientAuth.WANT }); 078 params.add(new Object[] { caKeyType, certKeyType, keyPassword, false, 079 validateClientHostnames, CertConfig.NON_VERIFIABLE_CERT, X509Util.ClientAuth.WANT }); 080 params.add(new Object[] { caKeyType, certKeyType, keyPassword, !validateClientHostnames, 081 validateClientHostnames, CertConfig.VERIFIABLE_CERT_WITH_BAD_HOST, 082 X509Util.ClientAuth.WANT }); 083 084 // ClientAuth.NEED is most restrictive, failing in all cases except "good cert/bad host" 085 // when host verification is disabled 086 params.add(new Object[] { caKeyType, certKeyType, keyPassword, false, 087 validateClientHostnames, CertConfig.NO_CLIENT_CERT, X509Util.ClientAuth.NEED }); 088 params.add(new Object[] { caKeyType, certKeyType, keyPassword, false, 089 validateClientHostnames, CertConfig.NON_VERIFIABLE_CERT, X509Util.ClientAuth.NEED }); 090 params.add(new Object[] { caKeyType, certKeyType, keyPassword, !validateClientHostnames, 091 validateClientHostnames, CertConfig.VERIFIABLE_CERT_WITH_BAD_HOST, 092 X509Util.ClientAuth.NEED }); 093 094 // additionally ensure that all modes succeed when a good cert is presented 095 for (X509Util.ClientAuth mode : X509Util.ClientAuth.values()) { 096 params.add(new Object[] { caKeyType, certKeyType, keyPassword, true, 097 validateClientHostnames, CertConfig.GOOD_CERT, mode }); 098 } 099 } 100 } 101 } 102 } 103 return params; 104 } 105 106 @Override 107 protected void initialize(Configuration serverConf, Configuration clientConf) 108 throws IOException, GeneralSecurityException, OperatorCreationException { 109 // server enables client auth mode and verifies client host names 110 // inject bad certs into client side 111 serverConf.set(X509Util.HBASE_SERVER_NETTY_TLS_CLIENT_AUTH_MODE, clientAuthMode.name()); 112 serverConf.setBoolean(X509Util.HBASE_SERVER_NETTY_TLS_VERIFY_CLIENT_HOSTNAME, 113 validateHostnames); 114 handleCertConfig(clientConf); 115 } 116}