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