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.security.HBaseKerberosUtils.getClientKeytabForTesting;
021import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.getClientPrincipalForTesting;
022import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.getKeytabFileForTesting;
023import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.getPrincipalForTesting;
024import static org.apache.hadoop.hbase.security.HBaseKerberosUtils.getSecuredConfiguration;
025import static org.junit.jupiter.api.Assertions.assertEquals;
026import static org.junit.jupiter.api.Assertions.assertFalse;
027import static org.junit.jupiter.api.Assertions.assertNotNull;
028import static org.junit.jupiter.api.Assertions.assertTrue;
029
030import java.io.File;
031import java.io.IOException;
032import org.apache.hadoop.conf.Configuration;
033import org.apache.hadoop.hbase.AuthUtil;
034import org.apache.hadoop.hbase.HBaseTestingUtil;
035import org.apache.hadoop.hbase.testclassification.SecurityTests;
036import org.apache.hadoop.hbase.testclassification.SmallTests;
037import org.apache.hadoop.minikdc.MiniKdc;
038import org.apache.hadoop.security.UserGroupInformation;
039import org.junit.jupiter.api.AfterAll;
040import org.junit.jupiter.api.BeforeAll;
041import org.junit.jupiter.api.Tag;
042import org.junit.jupiter.api.Test;
043
044@Tag(SecurityTests.TAG)
045@Tag(SmallTests.TAG)
046public class TestUsersOperationsWithSecureHadoop {
047
048  private static final HBaseTestingUtil TEST_UTIL = new HBaseTestingUtil();
049  private static final File KEYTAB_FILE =
050    new File(TEST_UTIL.getDataTestDir("keytab").toUri().getPath());
051
052  private static MiniKdc KDC;
053
054  private static String HOST = "localhost";
055
056  private static String PRINCIPAL;
057
058  private static String CLIENT_NAME;
059
060  private static String OTHER_CLIENT_NAME;
061
062  @BeforeAll
063  public static void setUp() throws Exception {
064    KDC = TEST_UTIL.setupMiniKdc(KEYTAB_FILE);
065    PRINCIPAL = "hbase/" + HOST;
066    CLIENT_NAME = "foo";
067    OTHER_CLIENT_NAME = "bar";
068    KDC.createPrincipal(KEYTAB_FILE, PRINCIPAL, CLIENT_NAME, OTHER_CLIENT_NAME);
069    HBaseKerberosUtils.setPrincipalForTesting(PRINCIPAL + "@" + KDC.getRealm());
070    HBaseKerberosUtils.setKeytabFileForTesting(KEYTAB_FILE.getAbsolutePath());
071    HBaseKerberosUtils.setClientPrincipalForTesting(CLIENT_NAME + "@" + KDC.getRealm());
072    HBaseKerberosUtils.setClientKeytabForTesting(KEYTAB_FILE.getAbsolutePath());
073  }
074
075  @AfterAll
076  public static void tearDown() throws IOException {
077    if (KDC != null) {
078      KDC.stop();
079    }
080    TEST_UTIL.cleanupTestDir();
081  }
082
083  /**
084   * test login with security enabled configuration To run this test, we must specify the following
085   * system properties:
086   * <p>
087   * <b> hbase.regionserver.kerberos.principal </b>
088   * <p>
089   * <b> hbase.regionserver.keytab.file </b>
090   */
091  @Test
092  public void testUserLoginInSecureHadoop() throws Exception {
093    // Default login is system user.
094    UserGroupInformation defaultLogin = UserGroupInformation.getCurrentUser();
095
096    String nnKeyTab = getKeytabFileForTesting();
097    String dnPrincipal = getPrincipalForTesting();
098
099    assertNotNull("KerberosKeytab was not specified", nnKeyTab);
100    assertNotNull("KerberosPrincipal was not specified", dnPrincipal);
101
102    Configuration conf = getSecuredConfiguration();
103    UserGroupInformation.setConfiguration(conf);
104
105    User.login(conf, HBaseKerberosUtils.KRB_KEYTAB_FILE, HBaseKerberosUtils.KRB_PRINCIPAL,
106      "localhost");
107    UserGroupInformation successLogin = UserGroupInformation.getLoginUser();
108    assertFalse(defaultLogin.equals(successLogin),
109      "ugi should be different in in case success login");
110  }
111
112  @Test
113  public void testLoginWithUserKeytabAndPrincipal() throws Exception {
114    String clientKeytab = getClientKeytabForTesting();
115    String clientPrincipal = getClientPrincipalForTesting();
116    assertNotNull("Path for client keytab is not specified.", clientKeytab);
117    assertNotNull("Client principal is not specified.", clientPrincipal);
118
119    Configuration conf = getSecuredConfiguration();
120    conf.set(AuthUtil.HBASE_CLIENT_KEYTAB_FILE, clientKeytab);
121    conf.set(AuthUtil.HBASE_CLIENT_KERBEROS_PRINCIPAL, clientPrincipal);
122    UserGroupInformation.setConfiguration(conf);
123
124    UserProvider provider = UserProvider.instantiate(conf);
125    assertTrue(provider.shouldLoginFromKeytab(), "Client principal or keytab is empty");
126
127    provider.login(AuthUtil.HBASE_CLIENT_KEYTAB_FILE, AuthUtil.HBASE_CLIENT_KERBEROS_PRINCIPAL);
128    User loginUser = provider.getCurrent();
129    assertEquals(CLIENT_NAME, loginUser.getShortName());
130    assertEquals(getClientPrincipalForTesting(), loginUser.getName());
131  }
132
133  @Test
134  public void testAuthUtilLoginWithExistingLoginUser() throws Exception {
135    String clientKeytab = getClientKeytabForTesting();
136    String clientPrincipal = getClientPrincipalForTesting();
137    Configuration conf = getSecuredConfiguration();
138    conf.set(AuthUtil.HBASE_CLIENT_KEYTAB_FILE, clientKeytab);
139    conf.set(AuthUtil.HBASE_CLIENT_KERBEROS_PRINCIPAL, clientPrincipal);
140    UserGroupInformation.setConfiguration(conf);
141
142    UserGroupInformation.loginUserFromKeytab(CLIENT_NAME, clientKeytab);
143
144    User user = AuthUtil.loginClient(conf);
145    assertTrue(user.isLoginFromKeytab());
146    assertEquals(CLIENT_NAME, user.getShortName());
147    assertEquals(getClientPrincipalForTesting(), user.getName());
148  }
149
150  @Test
151  public void testAuthUtilLoginWithDifferentExistingUser() throws Exception {
152    String clientKeytab = getClientKeytabForTesting();
153    String clientPrincipal = getClientPrincipalForTesting();
154    Configuration conf = getSecuredConfiguration();
155    conf.set(AuthUtil.HBASE_CLIENT_KEYTAB_FILE, clientKeytab);
156    conf.set(AuthUtil.HBASE_CLIENT_KERBEROS_PRINCIPAL, clientPrincipal);
157    UserGroupInformation.setConfiguration(conf);
158
159    // Login with other principal first
160    String otherPrincipal = OTHER_CLIENT_NAME + "@" + KDC.getRealm();
161    UserGroupInformation.loginUserFromKeytab(otherPrincipal, clientKeytab);
162
163    User user = AuthUtil.loginClient(conf);
164    assertTrue(user.isLoginFromKeytab());
165    // The existing login user (bar) doesn't match the principal configured in
166    // HBASE_CLIENT_KERBEROS_PRINCIPAL (foo), so loginClient should re-login
167    // with the configured principal.
168    assertEquals(CLIENT_NAME, user.getShortName());
169    assertEquals(getClientPrincipalForTesting(), user.getName());
170
171    conf.set(AuthUtil.HBASE_CLIENT_KERBEROS_PRINCIPAL, otherPrincipal);
172
173    user = AuthUtil.loginClient(conf);
174    assertTrue(user.isLoginFromKeytab());
175    // After updating HBASE_CLIENT_KERBEROS_PRINCIPAL to bar, loginClient should re-login with bar.
176    assertEquals(OTHER_CLIENT_NAME, user.getShortName());
177    assertEquals(otherPrincipal, user.getName());
178  }
179}