HBASE-26921 Rewrite the counting cells part in TestMultiVersions (#4316)
[hbase.git] / hbase-common / src / main / java / org / apache / hadoop / hbase / AuthUtil.java
blob95dfdd206ec8c946d07cc624fa283886e1353809
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 /** Configuration to automatically try to renew keytab-based logins */
94 public static final String HBASE_CLIENT_AUTOMATIC_KEYTAB_RENEWAL_KEY = "hbase.client.keytab.automatic.renewal";
95 public static final boolean HBASE_CLIENT_AUTOMATIC_KEYTAB_RENEWAL_DEFAULT = true;
97 private AuthUtil() {
98 super();
102 * For kerberized cluster, return login user (from kinit or from keytab if specified).
103 * For non-kerberized cluster, return system user.
104 * @param conf configuartion file
105 * @return user
106 * @throws IOException login exception
108 @InterfaceAudience.Private
109 public static User loginClient(Configuration conf) throws IOException {
110 UserProvider provider = UserProvider.instantiate(conf);
111 User user = provider.getCurrent();
112 boolean securityOn = provider.isHBaseSecurityEnabled() && provider.isHadoopSecurityEnabled();
114 if (securityOn) {
115 boolean fromKeytab = provider.shouldLoginFromKeytab();
116 if (user.getUGI().hasKerberosCredentials()) {
117 // There's already a login user.
118 // But we should avoid misuse credentials which is a dangerous security issue,
119 // so here check whether user specified a keytab and a principal:
120 // 1. Yes, check if user principal match.
121 // a. match, just return.
122 // b. mismatch, login using keytab.
123 // 2. No, user may login through kinit, this is the old way, also just return.
124 if (fromKeytab) {
125 return checkPrincipalMatch(conf, user.getUGI().getUserName()) ? user :
126 loginFromKeytabAndReturnUser(provider);
128 return user;
129 } else if (fromKeytab) {
130 // Kerberos is on and client specify a keytab and principal, but client doesn't login yet.
131 return loginFromKeytabAndReturnUser(provider);
134 return user;
137 private static boolean checkPrincipalMatch(Configuration conf, String loginUserName) {
138 String configuredUserName = conf.get(HBASE_CLIENT_KERBEROS_PRINCIPAL);
139 boolean match = configuredUserName.equals(loginUserName);
140 if (!match) {
141 LOG.warn("Trying to login with a different user: {}, existed user is {}.",
142 configuredUserName, loginUserName);
144 return match;
147 private static User loginFromKeytabAndReturnUser(UserProvider provider) throws IOException {
148 try {
149 provider.login(HBASE_CLIENT_KEYTAB_FILE, HBASE_CLIENT_KERBEROS_PRINCIPAL);
150 } catch (IOException ioe) {
151 LOG.error("Error while trying to login as user {} through {}, with message: {}.",
152 HBASE_CLIENT_KERBEROS_PRINCIPAL, HBASE_CLIENT_KEYTAB_FILE,
153 ioe.getMessage());
154 throw ioe;
156 return provider.getCurrent();
160 * For kerberized cluster, return login user (from kinit or from keytab).
161 * Principal should be the following format: name/fully.qualified.domain.name@REALM.
162 * For non-kerberized cluster, return system user.
163 * <p>
164 * NOT recommend to use to method unless you're sure what you're doing, it is for canary only.
165 * Please use User#loginClient.
166 * @param conf configuration file
167 * @return user
168 * @throws IOException login exception
170 private static User loginClientAsService(Configuration conf) throws IOException {
171 UserProvider provider = UserProvider.instantiate(conf);
172 if (provider.isHBaseSecurityEnabled() && provider.isHadoopSecurityEnabled()) {
173 try {
174 if (provider.shouldLoginFromKeytab()) {
175 String host = Strings.domainNamePointerToHostName(DNS.getDefaultHost(
176 conf.get("hbase.client.dns.interface", "default"),
177 conf.get("hbase.client.dns.nameserver", "default")));
178 provider.login(HBASE_CLIENT_KEYTAB_FILE, HBASE_CLIENT_KERBEROS_PRINCIPAL, host);
180 } catch (UnknownHostException e) {
181 LOG.error("Error resolving host name: " + e.getMessage(), e);
182 throw e;
183 } catch (IOException e) {
184 LOG.error("Error while trying to perform the initial login: " + e.getMessage(), e);
185 throw e;
188 return provider.getCurrent();
192 * Checks if security is enabled and if so, launches chore for refreshing kerberos ticket.
193 * @return a ScheduledChore for renewals.
195 @InterfaceAudience.Private
196 public static ScheduledChore getAuthRenewalChore(final UserGroupInformation user, Configuration conf) {
197 if (!user.hasKerberosCredentials() || !isAuthRenewalChoreEnabled(conf)) {
198 return null;
201 Stoppable stoppable = createDummyStoppable();
202 // if you're in debug mode this is useful to avoid getting spammed by the getTGT()
203 // you can increase this, keeping in mind that the default refresh window is 0.8
204 // e.g. 5min tgt * 0.8 = 4min refresh so interval is better be way less than 1min
205 final int CHECK_TGT_INTERVAL = 30 * 1000; // 30sec
206 return new ScheduledChore("RefreshCredentials", stoppable, CHECK_TGT_INTERVAL) {
207 @Override
208 protected void chore() {
209 try {
210 user.checkTGTAndReloginFromKeytab();
211 } catch (IOException e) {
212 LOG.error("Got exception while trying to refresh credentials: " + e.getMessage(), e);
219 * Checks if security is enabled and if so, launches chore for refreshing kerberos ticket.
220 * @param conf the hbase service configuration
221 * @return a ScheduledChore for renewals, if needed, and null otherwise.
222 * @deprecated Deprecated since 2.2.0, this method will be
223 * {@link org.apache.yetus.audience.InterfaceAudience.Private} use only after 4.0.0.
224 * @see <a href="https://issues.apache.org/jira/browse/HBASE-20886">HBASE-20886</a>
226 @Deprecated
227 public static ScheduledChore getAuthChore(Configuration conf) throws IOException {
228 if (!isAuthRenewalChoreEnabled(conf)) {
229 return null;
231 User user = loginClientAsService(conf);
232 return getAuthRenewalChore(user.getUGI(), conf);
235 private static Stoppable createDummyStoppable() {
236 return new Stoppable() {
237 private volatile boolean isStopped = false;
239 @Override
240 public void stop(String why) {
241 isStopped = true;
244 @Override
245 public boolean isStopped() {
246 return isStopped;
252 * Returns whether or not the given name should be interpreted as a group
253 * principal. Currently this simply checks if the name starts with the
254 * special group prefix character ("@").
256 @InterfaceAudience.Private
257 public static boolean isGroupPrincipal(String name) {
258 return name != null && name.startsWith(GROUP_PREFIX);
262 * Returns the actual name for a group principal (stripped of the
263 * group prefix).
265 @InterfaceAudience.Private
266 public static String getGroupName(String aclKey) {
267 if (!isGroupPrincipal(aclKey)) {
268 return aclKey;
271 return aclKey.substring(GROUP_PREFIX.length());
275 * Returns the group entry with the group prefix for a group principal.
277 @InterfaceAudience.Private
278 public static String toGroupEntry(String name) {
279 return GROUP_PREFIX + name;
283 * Returns true if the chore to automatically renew Kerberos tickets (from
284 * keytabs) should be started. The default is true.
286 static boolean isAuthRenewalChoreEnabled(Configuration conf) {
287 return conf.getBoolean(HBASE_CLIENT_AUTOMATIC_KEYTAB_RENEWAL_KEY,
288 HBASE_CLIENT_AUTOMATIC_KEYTAB_RENEWAL_DEFAULT);