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}