HBASE-23892 SecureTestCluster should allow its subclasses to pass their Class referen...
[hbase.git] / hbase-common / src / main / java / org / apache / hadoop / hbase / AuthUtil.java
blobd8d4f788ab106894a4727d4e720259bd4f88f89a
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
19 package org.apache.hadoop.hbase;
21 import java.io.IOException;
22 import java.net.UnknownHostException;
24 import org.apache.hadoop.conf.Configuration;
25 import org.apache.hadoop.hbase.security.User;
26 import org.apache.hadoop.hbase.security.UserProvider;
27 import org.apache.hadoop.hbase.util.DNS;
28 import org.apache.hadoop.hbase.util.Strings;
29 import org.apache.hadoop.security.UserGroupInformation;
30 import org.apache.yetus.audience.InterfaceAudience;
31 import org.slf4j.Logger;
32 import org.slf4j.LoggerFactory;
34 /**
35 * Utility methods for helping with security tasks. Downstream users
36 * may rely on this class to handle authenticating via keytab where
37 * long running services need access to a secure HBase cluster.
39 * Callers must ensure:
41 * <ul>
42 * <li>HBase configuration files are in the Classpath
43 * <li>hbase.client.keytab.file points to a valid keytab on the local filesystem
44 * <li>hbase.client.kerberos.principal gives the Kerberos principal to use
45 * </ul>
47 * <pre>
48 * {@code
49 * ChoreService choreService = null;
50 * // Presumes HBase configuration files are on the classpath
51 * final Configuration conf = HBaseConfiguration.create();
52 * final ScheduledChore authChore = AuthUtil.getAuthChore(conf);
53 * if (authChore != null) {
54 * choreService = new ChoreService("MY_APPLICATION");
55 * choreService.scheduleChore(authChore);
56 * }
57 * try {
58 * // do application work
59 * } finally {
60 * if (choreService != null) {
61 * choreService.shutdown();
62 * }
63 * }
64 * }
65 * </pre>
67 * See the "Running Canary in a Kerberos-enabled Cluster" section of the HBase Reference Guide for
68 * an example of configuring a user of this Auth Chore to run on a secure cluster.
69 * <pre>
70 * </pre>
71 * This class will be internal used only from 2.2.0 version, and will transparently work
72 * for kerberized applications. For more, please refer
73 * <a href="http://hbase.apache.org/book.html#hbase.secure.configuration">Client-side Configuration for Secure Operation</a>
75 * @deprecated since 2.2.0, to be marked as
76 * {@link org.apache.yetus.audience.InterfaceAudience.Private} in 4.0.0.
77 * @see <a href="https://issues.apache.org/jira/browse/HBASE-20886">HBASE-20886</a>
79 @Deprecated
80 @InterfaceAudience.Public
81 public final class AuthUtil {
82 private static final Logger LOG = LoggerFactory.getLogger(AuthUtil.class);
84 /** Prefix character to denote group names */
85 private static final String GROUP_PREFIX = "@";
87 /** Client keytab file */
88 public static final String HBASE_CLIENT_KEYTAB_FILE = "hbase.client.keytab.file";
90 /** Client principal */
91 public static final String HBASE_CLIENT_KERBEROS_PRINCIPAL = "hbase.client.keytab.principal";
93 private AuthUtil() {
94 super();
97 /**
98 * For kerberized cluster, return login user (from kinit or from keytab if specified).
99 * For non-kerberized cluster, return system user.
100 * @param conf configuartion file
101 * @return user
102 * @throws IOException login exception
104 @InterfaceAudience.Private
105 public static User loginClient(Configuration conf) throws IOException {
106 UserProvider provider = UserProvider.instantiate(conf);
107 User user = provider.getCurrent();
108 boolean securityOn = provider.isHBaseSecurityEnabled() && provider.isHadoopSecurityEnabled();
110 if (securityOn) {
111 boolean fromKeytab = provider.shouldLoginFromKeytab();
112 if (user.getUGI().hasKerberosCredentials()) {
113 // There's already a login user.
114 // But we should avoid misuse credentials which is a dangerous security issue,
115 // so here check whether user specified a keytab and a principal:
116 // 1. Yes, check if user principal match.
117 // a. match, just return.
118 // b. mismatch, login using keytab.
119 // 2. No, user may login through kinit, this is the old way, also just return.
120 if (fromKeytab) {
121 return checkPrincipalMatch(conf, user.getUGI().getUserName()) ? user :
122 loginFromKeytabAndReturnUser(provider);
124 return user;
125 } else if (fromKeytab) {
126 // Kerberos is on and client specify a keytab and principal, but client doesn't login yet.
127 return loginFromKeytabAndReturnUser(provider);
130 return user;
133 private static boolean checkPrincipalMatch(Configuration conf, String loginUserName) {
134 String configuredUserName = conf.get(HBASE_CLIENT_KERBEROS_PRINCIPAL);
135 boolean match = configuredUserName.equals(loginUserName);
136 if (!match) {
137 LOG.warn("Trying to login with a different user: {}, existed user is {}.",
138 configuredUserName, loginUserName);
140 return match;
143 private static User loginFromKeytabAndReturnUser(UserProvider provider) throws IOException {
144 try {
145 provider.login(HBASE_CLIENT_KEYTAB_FILE, HBASE_CLIENT_KERBEROS_PRINCIPAL);
146 } catch (IOException ioe) {
147 LOG.error("Error while trying to login as user {} through {}, with message: {}.",
148 HBASE_CLIENT_KERBEROS_PRINCIPAL, HBASE_CLIENT_KEYTAB_FILE,
149 ioe.getMessage());
150 throw ioe;
152 return provider.getCurrent();
156 * For kerberized cluster, return login user (from kinit or from keytab).
157 * Principal should be the following format: name/fully.qualified.domain.name@REALM.
158 * For non-kerberized cluster, return system user.
159 * <p>
160 * NOT recommend to use to method unless you're sure what you're doing, it is for canary only.
161 * Please use User#loginClient.
162 * @param conf configuration file
163 * @return user
164 * @throws IOException login exception
166 private static User loginClientAsService(Configuration conf) throws IOException {
167 UserProvider provider = UserProvider.instantiate(conf);
168 if (provider.isHBaseSecurityEnabled() && provider.isHadoopSecurityEnabled()) {
169 try {
170 if (provider.shouldLoginFromKeytab()) {
171 String host = Strings.domainNamePointerToHostName(DNS.getDefaultHost(
172 conf.get("hbase.client.dns.interface", "default"),
173 conf.get("hbase.client.dns.nameserver", "default")));
174 provider.login(HBASE_CLIENT_KEYTAB_FILE, HBASE_CLIENT_KERBEROS_PRINCIPAL, host);
176 } catch (UnknownHostException e) {
177 LOG.error("Error resolving host name: " + e.getMessage(), e);
178 throw e;
179 } catch (IOException e) {
180 LOG.error("Error while trying to perform the initial login: " + e.getMessage(), e);
181 throw e;
184 return provider.getCurrent();
188 * Checks if security is enabled and if so, launches chore for refreshing kerberos ticket.
189 * @return a ScheduledChore for renewals.
191 @InterfaceAudience.Private
192 public static ScheduledChore getAuthRenewalChore(final UserGroupInformation user) {
193 if (!user.hasKerberosCredentials()) {
194 return null;
197 Stoppable stoppable = createDummyStoppable();
198 // if you're in debug mode this is useful to avoid getting spammed by the getTGT()
199 // you can increase this, keeping in mind that the default refresh window is 0.8
200 // e.g. 5min tgt * 0.8 = 4min refresh so interval is better be way less than 1min
201 final int CHECK_TGT_INTERVAL = 30 * 1000; // 30sec
202 return new ScheduledChore("RefreshCredentials", stoppable, CHECK_TGT_INTERVAL) {
203 @Override
204 protected void chore() {
205 try {
206 user.checkTGTAndReloginFromKeytab();
207 } catch (IOException e) {
208 LOG.error("Got exception while trying to refresh credentials: " + e.getMessage(), e);
215 * Checks if security is enabled and if so, launches chore for refreshing kerberos ticket.
216 * @param conf the hbase service configuration
217 * @return a ScheduledChore for renewals, if needed, and null otherwise.
218 * @deprecated Deprecated since 2.2.0, this method will be
219 * {@link org.apache.yetus.audience.InterfaceAudience.Private} use only after 4.0.0.
220 * @see <a href="https://issues.apache.org/jira/browse/HBASE-20886">HBASE-20886</a>
222 @Deprecated
223 public static ScheduledChore getAuthChore(Configuration conf) throws IOException {
224 User user = loginClientAsService(conf);
225 return getAuthRenewalChore(user.getUGI());
228 private static Stoppable createDummyStoppable() {
229 return new Stoppable() {
230 private volatile boolean isStopped = false;
232 @Override
233 public void stop(String why) {
234 isStopped = true;
237 @Override
238 public boolean isStopped() {
239 return isStopped;
245 * Returns whether or not the given name should be interpreted as a group
246 * principal. Currently this simply checks if the name starts with the
247 * special group prefix character ("@").
249 @InterfaceAudience.Private
250 public static boolean isGroupPrincipal(String name) {
251 return name != null && name.startsWith(GROUP_PREFIX);
255 * Returns the actual name for a group principal (stripped of the
256 * group prefix).
258 @InterfaceAudience.Private
259 public static String getGroupName(String aclKey) {
260 if (!isGroupPrincipal(aclKey)) {
261 return aclKey;
264 return aclKey.substring(GROUP_PREFIX.length());
268 * Returns the group entry with the group prefix for a group principal.
270 @InterfaceAudience.Private
271 public static String toGroupEntry(String name) {
272 return GROUP_PREFIX + name;