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.replication;
019
020import static org.junit.jupiter.api.Assertions.assertEquals;
021
022import java.io.File;
023import java.io.IOException;
024import java.util.Arrays;
025import java.util.Collection;
026import java.util.function.Supplier;
027import org.apache.hadoop.conf.Configuration;
028import org.apache.hadoop.hbase.HBaseTestingUtil;
029import org.apache.hadoop.hbase.client.Admin;
030import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
031import org.apache.hadoop.hbase.mapreduce.replication.VerifyReplication;
032import org.apache.hadoop.hbase.security.HBaseKerberosUtils;
033import org.apache.hadoop.hbase.security.access.AccessController;
034import org.apache.hadoop.hbase.security.access.SecureTestUtil;
035import org.apache.hadoop.hbase.security.token.AuthenticationTokenIdentifier;
036import org.apache.hadoop.hbase.security.token.TokenProvider;
037import org.apache.hadoop.hbase.security.visibility.VisibilityTestUtil;
038import org.apache.hadoop.hbase.testclassification.LargeTests;
039import org.apache.hadoop.hbase.testclassification.ReplicationTests;
040import org.apache.hadoop.hbase.zookeeper.ZKClusterId;
041import org.apache.hadoop.hbase.zookeeper.ZKConfig;
042import org.apache.hadoop.io.Text;
043import org.apache.hadoop.mapreduce.Job;
044import org.apache.hadoop.minikdc.MiniKdc;
045import org.apache.hadoop.security.Credentials;
046import org.apache.hadoop.security.UserGroupInformation;
047import org.apache.hadoop.security.token.Token;
048import org.apache.hadoop.security.token.TokenIdentifier;
049import org.junit.jupiter.api.AfterAll;
050import org.junit.jupiter.api.BeforeAll;
051import org.junit.jupiter.api.Tag;
052import org.junit.jupiter.params.ParameterizedTest;
053import org.junit.jupiter.params.provider.MethodSource;
054
055@Tag(ReplicationTests.TAG)
056@Tag(LargeTests.TAG)
057public class TestVerifyReplicationSecureClusterCredentials {
058
059  private static MiniKdc KDC;
060  private static final HBaseTestingUtil UTIL1 = new HBaseTestingUtil();
061  private static final HBaseTestingUtil UTIL2 = new HBaseTestingUtil();
062
063  private static final File KEYTAB_FILE =
064    new File(UTIL1.getDataTestDir("keytab").toUri().getPath());
065
066  private static final String LOCALHOST = "localhost";
067  private static String CLUSTER_PRINCIPAL;
068  private static String FULL_USER_PRINCIPAL;
069  private static String HTTP_PRINCIPAL;
070
071  private static void setUpKdcServer() throws Exception {
072    KDC = UTIL1.setupMiniKdc(KEYTAB_FILE);
073    String username = UserGroupInformation.getLoginUser().getShortUserName();
074    String userPrincipal = username + '/' + LOCALHOST;
075    CLUSTER_PRINCIPAL = userPrincipal;
076    FULL_USER_PRINCIPAL = userPrincipal + '@' + KDC.getRealm();
077    HTTP_PRINCIPAL = "HTTP/" + LOCALHOST;
078    KDC.createPrincipal(KEYTAB_FILE, CLUSTER_PRINCIPAL, HTTP_PRINCIPAL);
079  }
080
081  private static void setupCluster(HBaseTestingUtil util) throws Exception {
082    Configuration conf = util.getConfiguration();
083
084    SecureTestUtil.enableSecurity(conf);
085    VisibilityTestUtil.enableVisiblityLabels(conf);
086    SecureTestUtil.verifyConfiguration(conf);
087
088    conf.set(CoprocessorHost.REGION_COPROCESSOR_CONF_KEY,
089      AccessController.class.getName() + ',' + TokenProvider.class.getName());
090
091    HBaseKerberosUtils.setSecuredConfiguration(conf, CLUSTER_PRINCIPAL + '@' + KDC.getRealm(),
092      HTTP_PRINCIPAL + '@' + KDC.getRealm());
093
094    util.startMiniCluster();
095  }
096
097  /**
098   * Sets the security firstly for getting the correct default realm.
099   */
100  @BeforeAll
101  public static void beforeClass() throws Exception {
102    setUpKdcServer();
103    setupCluster(UTIL1);
104    setupCluster(UTIL2);
105
106    try (Admin admin = UTIL1.getAdmin()) {
107      admin.addReplicationPeer("1",
108        ReplicationPeerConfig.newBuilder()
109          .setClusterKey(ZKConfig.getZooKeeperClusterKey(UTIL2.getConfiguration()))
110          .putConfiguration(HBaseKerberosUtils.KRB_PRINCIPAL,
111            UTIL2.getConfiguration().get(HBaseKerberosUtils.KRB_PRINCIPAL))
112          .putConfiguration(HBaseKerberosUtils.MASTER_KRB_PRINCIPAL,
113            UTIL2.getConfiguration().get(HBaseKerberosUtils.MASTER_KRB_PRINCIPAL))
114          .build());
115    }
116  }
117
118  @AfterAll
119  public static void cleanup() throws IOException {
120    UTIL1.shutdownMiniCluster();
121    UTIL2.shutdownMiniCluster();
122  }
123
124  public static Collection<Supplier<String>> peer() {
125    return Arrays.asList(() -> "1",
126      () -> ZKConfig.getZooKeeperClusterKey(UTIL2.getConfiguration()));
127  }
128
129  @ParameterizedTest
130  @MethodSource("peer")
131  @SuppressWarnings("unchecked")
132  public void testJobCredentials(Supplier<String> peer) throws Exception {
133    Job job = new VerifyReplication().createSubmittableJob(
134      new Configuration(UTIL1.getConfiguration()), new String[] { peer.get(), "table" });
135
136    Credentials credentials = job.getCredentials();
137    Collection<Token<? extends TokenIdentifier>> tokens = credentials.getAllTokens();
138    assertEquals(2, tokens.size());
139
140    String clusterId1 = ZKClusterId.readClusterIdZNode(UTIL1.getZooKeeperWatcher());
141    Token<AuthenticationTokenIdentifier> tokenForCluster1 =
142      (Token<AuthenticationTokenIdentifier>) credentials.getToken(new Text(clusterId1));
143    assertEquals(FULL_USER_PRINCIPAL, tokenForCluster1.decodeIdentifier().getUsername());
144
145    String clusterId2 = ZKClusterId.readClusterIdZNode(UTIL2.getZooKeeperWatcher());
146    Token<AuthenticationTokenIdentifier> tokenForCluster2 =
147      (Token<AuthenticationTokenIdentifier>) credentials.getToken(new Text(clusterId2));
148    assertEquals(FULL_USER_PRINCIPAL, tokenForCluster2.decodeIdentifier().getUsername());
149  }
150}