HADOOP-13627. Have an explicit KerberosAuthException for UGI to throw, text from public constants. Contributed by Xiao Chen.
This commit is contained in:
parent
3565c9af17
commit
2e853be657
|
@ -0,0 +1,118 @@
|
||||||
|
/**
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.hadoop.security;
|
||||||
|
|
||||||
|
import static org.apache.hadoop.security.UGIExceptionMessages.*;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import org.apache.hadoop.classification.InterfaceAudience;
|
||||||
|
import org.apache.hadoop.classification.InterfaceStability;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Thrown when {@link UserGroupInformation} failed with an unrecoverable error,
|
||||||
|
* such as failure in kerberos login/logout, invalid subject etc.
|
||||||
|
*
|
||||||
|
* Caller should not retry when catching this exception.
|
||||||
|
*/
|
||||||
|
@InterfaceAudience.Public
|
||||||
|
@InterfaceStability.Unstable
|
||||||
|
public class KerberosAuthException extends IOException {
|
||||||
|
static final long serialVersionUID = 31L;
|
||||||
|
|
||||||
|
private String user;
|
||||||
|
private String principal;
|
||||||
|
private String keytabFile;
|
||||||
|
private String ticketCacheFile;
|
||||||
|
private String initialMessage;
|
||||||
|
|
||||||
|
public KerberosAuthException(String msg) {
|
||||||
|
super(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KerberosAuthException(Throwable cause) {
|
||||||
|
super(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public KerberosAuthException(String initialMsg, Throwable cause) {
|
||||||
|
this(cause);
|
||||||
|
initialMessage = initialMsg;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUser(final String u) {
|
||||||
|
user = u;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setPrincipal(final String p) {
|
||||||
|
principal = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setKeytabFile(final String k) {
|
||||||
|
keytabFile = k;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTicketCacheFile(final String t) {
|
||||||
|
ticketCacheFile = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return The initial message, or null if not set. */
|
||||||
|
public String getInitialMessage() {
|
||||||
|
return initialMessage;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return The keytab file path, or null if not set. */
|
||||||
|
public String getKeytabFile() {
|
||||||
|
return keytabFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return The principal, or null if not set. */
|
||||||
|
public String getPrincipal() {
|
||||||
|
return principal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return The ticket cache file path, or null if not set. */
|
||||||
|
public String getTicketCacheFile() {
|
||||||
|
return ticketCacheFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return The user, or null if not set. */
|
||||||
|
public String getUser() {
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
final StringBuilder sb = new StringBuilder();
|
||||||
|
if (initialMessage != null) {
|
||||||
|
sb.append(initialMessage);
|
||||||
|
}
|
||||||
|
if (user != null) {
|
||||||
|
sb.append(FOR_USER + user);
|
||||||
|
}
|
||||||
|
if (principal != null) {
|
||||||
|
sb.append(FOR_PRINCIPAL + principal);
|
||||||
|
}
|
||||||
|
if (keytabFile != null) {
|
||||||
|
sb.append(FROM_KEYTAB + keytabFile);
|
||||||
|
}
|
||||||
|
if (ticketCacheFile != null) {
|
||||||
|
sb.append(USING_TICKET_CACHE_FILE+ ticketCacheFile);
|
||||||
|
}
|
||||||
|
sb.append(" " + super.getMessage());
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you under the Apache License, Version 2.0 (the
|
||||||
|
* "License"); you may not use this file except in compliance
|
||||||
|
* with the License. You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package org.apache.hadoop.security;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Standard strings to use in exception messages
|
||||||
|
* in {@link KerberosAuthException} when throwing.
|
||||||
|
*/
|
||||||
|
final class UGIExceptionMessages {
|
||||||
|
|
||||||
|
public static final String FAILURE_TO_LOGIN = "failure to login:";
|
||||||
|
public static final String FOR_USER = " for user: ";
|
||||||
|
public static final String FOR_PRINCIPAL = " for principal: ";
|
||||||
|
public static final String FROM_KEYTAB = " from keytab ";
|
||||||
|
public static final String LOGIN_FAILURE = "Login failure";
|
||||||
|
public static final String LOGOUT_FAILURE = "Logout failure";
|
||||||
|
public static final String MUST_FIRST_LOGIN =
|
||||||
|
"login must be done first";
|
||||||
|
public static final String MUST_FIRST_LOGIN_FROM_KEYTAB =
|
||||||
|
"loginUserFromKeyTab must be done first";
|
||||||
|
public static final String SUBJECT_MUST_CONTAIN_PRINCIPAL =
|
||||||
|
"Provided Subject must contain a KerberosPrincipal";
|
||||||
|
public static final String SUBJECT_MUST_NOT_BE_NULL =
|
||||||
|
"Subject must not be null";
|
||||||
|
public static final String USING_TICKET_CACHE_FILE =
|
||||||
|
" using ticket cache file: ";
|
||||||
|
|
||||||
|
//checkstyle: Utility classes should not have a public or default constructor.
|
||||||
|
private UGIExceptionMessages() {
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ import static org.apache.hadoop.fs.CommonConfigurationKeys.HADOOP_USER_GROUP_MET
|
||||||
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_KERBEROS_MIN_SECONDS_BEFORE_RELOGIN;
|
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_KERBEROS_MIN_SECONDS_BEFORE_RELOGIN;
|
||||||
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_KERBEROS_MIN_SECONDS_BEFORE_RELOGIN_DEFAULT;
|
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_KERBEROS_MIN_SECONDS_BEFORE_RELOGIN_DEFAULT;
|
||||||
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_TOKEN_FILES;
|
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_TOKEN_FILES;
|
||||||
|
import static org.apache.hadoop.security.UGIExceptionMessages.*;
|
||||||
import static org.apache.hadoop.util.PlatformName.IBM_JAVA;
|
import static org.apache.hadoop.util.PlatformName.IBM_JAVA;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -755,8 +756,11 @@ public class UserGroupInformation {
|
||||||
ugi.setAuthenticationMethod(AuthenticationMethod.KERBEROS);
|
ugi.setAuthenticationMethod(AuthenticationMethod.KERBEROS);
|
||||||
return ugi;
|
return ugi;
|
||||||
} catch (LoginException le) {
|
} catch (LoginException le) {
|
||||||
throw new IOException("failure to login using ticket cache file " +
|
KerberosAuthException kae =
|
||||||
ticketCache, le);
|
new KerberosAuthException(FAILURE_TO_LOGIN, le);
|
||||||
|
kae.setUser(user);
|
||||||
|
kae.setTicketCacheFile(ticketCache);
|
||||||
|
throw kae;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -765,16 +769,17 @@ public class UserGroupInformation {
|
||||||
*
|
*
|
||||||
* @param subject The KerberosPrincipal to use in UGI
|
* @param subject The KerberosPrincipal to use in UGI
|
||||||
*
|
*
|
||||||
* @throws IOException if the kerberos login fails
|
* @throws IOException
|
||||||
|
* @throws KerberosAuthException if the kerberos login fails
|
||||||
*/
|
*/
|
||||||
public static UserGroupInformation getUGIFromSubject(Subject subject)
|
public static UserGroupInformation getUGIFromSubject(Subject subject)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
if (subject == null) {
|
if (subject == null) {
|
||||||
throw new IOException("Subject must not be null");
|
throw new KerberosAuthException(SUBJECT_MUST_NOT_BE_NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (subject.getPrincipals(KerberosPrincipal.class).isEmpty()) {
|
if (subject.getPrincipals(KerberosPrincipal.class).isEmpty()) {
|
||||||
throw new IOException("Provided Subject must contain a KerberosPrincipal");
|
throw new KerberosAuthException(SUBJECT_MUST_CONTAIN_PRINCIPAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
KerberosPrincipal principal =
|
KerberosPrincipal principal =
|
||||||
|
@ -894,7 +899,7 @@ public class UserGroupInformation {
|
||||||
loginUser.spawnAutoRenewalThreadForUserCreds();
|
loginUser.spawnAutoRenewalThreadForUserCreds();
|
||||||
} catch (LoginException le) {
|
} catch (LoginException le) {
|
||||||
LOG.debug("failure to login", le);
|
LOG.debug("failure to login", le);
|
||||||
throw new IOException("failure to login: " + le, le);
|
throw new KerberosAuthException(FAILURE_TO_LOGIN, le);
|
||||||
}
|
}
|
||||||
if (LOG.isDebugEnabled()) {
|
if (LOG.isDebugEnabled()) {
|
||||||
LOG.debug("UGI loginUser:"+loginUser);
|
LOG.debug("UGI loginUser:"+loginUser);
|
||||||
|
@ -1001,7 +1006,8 @@ public class UserGroupInformation {
|
||||||
* file and logs them in. They become the currently logged-in user.
|
* file and logs them in. They become the currently logged-in user.
|
||||||
* @param user the principal name to load from the keytab
|
* @param user the principal name to load from the keytab
|
||||||
* @param path the path to the keytab file
|
* @param path the path to the keytab file
|
||||||
* @throws IOException if the keytab file can't be read
|
* @throws IOException
|
||||||
|
* @throws KerberosAuthException if it's a kerberos login exception.
|
||||||
*/
|
*/
|
||||||
@InterfaceAudience.Public
|
@InterfaceAudience.Public
|
||||||
@InterfaceStability.Evolving
|
@InterfaceStability.Evolving
|
||||||
|
@ -1030,8 +1036,10 @@ public class UserGroupInformation {
|
||||||
if (start > 0) {
|
if (start > 0) {
|
||||||
metrics.loginFailure.add(Time.now() - start);
|
metrics.loginFailure.add(Time.now() - start);
|
||||||
}
|
}
|
||||||
throw new IOException("Login failure for " + user + " from keytab " +
|
KerberosAuthException kae = new KerberosAuthException(LOGIN_FAILURE, le);
|
||||||
path+ ": " + le, le);
|
kae.setUser(user);
|
||||||
|
kae.setKeytabFile(path);
|
||||||
|
throw kae;
|
||||||
}
|
}
|
||||||
LOG.info("Login successful for user " + keytabPrincipal
|
LOG.info("Login successful for user " + keytabPrincipal
|
||||||
+ " using keytab file " + keytabFile);
|
+ " using keytab file " + keytabFile);
|
||||||
|
@ -1042,8 +1050,9 @@ public class UserGroupInformation {
|
||||||
* This method assumes that the user logged in by calling
|
* This method assumes that the user logged in by calling
|
||||||
* {@link #loginUserFromKeytab(String, String)}.
|
* {@link #loginUserFromKeytab(String, String)}.
|
||||||
*
|
*
|
||||||
* @throws IOException if a failure occurred in logout, or if the user did
|
* @throws IOException
|
||||||
* not log in by invoking loginUserFromKeyTab() before.
|
* @throws KerberosAuthException if a failure occurred in logout,
|
||||||
|
* or if the user did not log in by invoking loginUserFromKeyTab() before.
|
||||||
*/
|
*/
|
||||||
@InterfaceAudience.Public
|
@InterfaceAudience.Public
|
||||||
@InterfaceStability.Evolving
|
@InterfaceStability.Evolving
|
||||||
|
@ -1054,7 +1063,7 @@ public class UserGroupInformation {
|
||||||
}
|
}
|
||||||
LoginContext login = getLogin();
|
LoginContext login = getLogin();
|
||||||
if (login == null || keytabFile == null) {
|
if (login == null || keytabFile == null) {
|
||||||
throw new IOException("loginUserFromKeytab must be done first");
|
throw new KerberosAuthException(MUST_FIRST_LOGIN_FROM_KEYTAB);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -1065,9 +1074,10 @@ public class UserGroupInformation {
|
||||||
login.logout();
|
login.logout();
|
||||||
}
|
}
|
||||||
} catch (LoginException le) {
|
} catch (LoginException le) {
|
||||||
throw new IOException("Logout failure for " + user + " from keytab " +
|
KerberosAuthException kae = new KerberosAuthException(LOGOUT_FAILURE, le);
|
||||||
keytabFile + ": " + le,
|
kae.setUser(user.toString());
|
||||||
le);
|
kae.setKeytabFile(keytabFile);
|
||||||
|
throw kae;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG.info("Logout successful for user " + keytabPrincipal
|
LOG.info("Logout successful for user " + keytabPrincipal
|
||||||
|
@ -1078,6 +1088,7 @@ public class UserGroupInformation {
|
||||||
* Re-login a user from keytab if TGT is expired or is close to expiry.
|
* Re-login a user from keytab if TGT is expired or is close to expiry.
|
||||||
*
|
*
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
|
* @throws KerberosAuthException if it's a kerberos login exception.
|
||||||
*/
|
*/
|
||||||
public synchronized void checkTGTAndReloginFromKeytab() throws IOException {
|
public synchronized void checkTGTAndReloginFromKeytab() throws IOException {
|
||||||
if (!isSecurityEnabled()
|
if (!isSecurityEnabled()
|
||||||
|
@ -1099,12 +1110,12 @@ public class UserGroupInformation {
|
||||||
* happened already.
|
* happened already.
|
||||||
* The Subject field of this UserGroupInformation object is updated to have
|
* The Subject field of this UserGroupInformation object is updated to have
|
||||||
* the new credentials.
|
* the new credentials.
|
||||||
* @throws IOException on a failure
|
* @throws IOException
|
||||||
|
* @throws KerberosAuthException on a failure
|
||||||
*/
|
*/
|
||||||
@InterfaceAudience.Public
|
@InterfaceAudience.Public
|
||||||
@InterfaceStability.Evolving
|
@InterfaceStability.Evolving
|
||||||
public synchronized void reloginFromKeytab()
|
public synchronized void reloginFromKeytab() throws IOException {
|
||||||
throws IOException {
|
|
||||||
if (!isSecurityEnabled() ||
|
if (!isSecurityEnabled() ||
|
||||||
user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
|
user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
|
||||||
!isKeytab)
|
!isKeytab)
|
||||||
|
@ -1124,7 +1135,7 @@ public class UserGroupInformation {
|
||||||
|
|
||||||
LoginContext login = getLogin();
|
LoginContext login = getLogin();
|
||||||
if (login == null || keytabFile == null) {
|
if (login == null || keytabFile == null) {
|
||||||
throw new IOException("loginUserFromKeyTab must be done first");
|
throw new KerberosAuthException(MUST_FIRST_LOGIN_FROM_KEYTAB);
|
||||||
}
|
}
|
||||||
|
|
||||||
long start = 0;
|
long start = 0;
|
||||||
|
@ -1156,8 +1167,10 @@ public class UserGroupInformation {
|
||||||
if (start > 0) {
|
if (start > 0) {
|
||||||
metrics.loginFailure.add(Time.now() - start);
|
metrics.loginFailure.add(Time.now() - start);
|
||||||
}
|
}
|
||||||
throw new IOException("Login failure for " + keytabPrincipal +
|
KerberosAuthException kae = new KerberosAuthException(LOGIN_FAILURE, le);
|
||||||
" from keytab " + keytabFile + ": " + le, le);
|
kae.setPrincipal(keytabPrincipal);
|
||||||
|
kae.setKeytabFile(keytabFile);
|
||||||
|
throw kae;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1166,19 +1179,19 @@ public class UserGroupInformation {
|
||||||
* method assumes that login had happened already.
|
* method assumes that login had happened already.
|
||||||
* The Subject field of this UserGroupInformation object is updated to have
|
* The Subject field of this UserGroupInformation object is updated to have
|
||||||
* the new credentials.
|
* the new credentials.
|
||||||
* @throws IOException on a failure
|
* @throws IOException
|
||||||
|
* @throws KerberosAuthException on a failure
|
||||||
*/
|
*/
|
||||||
@InterfaceAudience.Public
|
@InterfaceAudience.Public
|
||||||
@InterfaceStability.Evolving
|
@InterfaceStability.Evolving
|
||||||
public synchronized void reloginFromTicketCache()
|
public synchronized void reloginFromTicketCache() throws IOException {
|
||||||
throws IOException {
|
|
||||||
if (!isSecurityEnabled() ||
|
if (!isSecurityEnabled() ||
|
||||||
user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
|
user.getAuthenticationMethod() != AuthenticationMethod.KERBEROS ||
|
||||||
!isKrbTkt)
|
!isKrbTkt)
|
||||||
return;
|
return;
|
||||||
LoginContext login = getLogin();
|
LoginContext login = getLogin();
|
||||||
if (login == null) {
|
if (login == null) {
|
||||||
throw new IOException("login must be done first");
|
throw new KerberosAuthException(MUST_FIRST_LOGIN);
|
||||||
}
|
}
|
||||||
long now = Time.now();
|
long now = Time.now();
|
||||||
if (!hasSufficientTimeElapsed(now)) {
|
if (!hasSufficientTimeElapsed(now)) {
|
||||||
|
@ -1205,8 +1218,9 @@ public class UserGroupInformation {
|
||||||
login.login();
|
login.login();
|
||||||
setLogin(login);
|
setLogin(login);
|
||||||
} catch (LoginException le) {
|
} catch (LoginException le) {
|
||||||
throw new IOException("Login failure for " + getUserName() + ": " + le,
|
KerberosAuthException kae = new KerberosAuthException(LOGIN_FAILURE, le);
|
||||||
le);
|
kae.setUser(getUserName());
|
||||||
|
throw kae;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1252,8 +1266,10 @@ public class UserGroupInformation {
|
||||||
if (start > 0) {
|
if (start > 0) {
|
||||||
metrics.loginFailure.add(Time.now() - start);
|
metrics.loginFailure.add(Time.now() - start);
|
||||||
}
|
}
|
||||||
throw new IOException("Login failure for " + user + " from keytab " +
|
KerberosAuthException kae = new KerberosAuthException(LOGIN_FAILURE, le);
|
||||||
path + ": " + le, le);
|
kae.setUser(user);
|
||||||
|
kae.setKeytabFile(path);
|
||||||
|
throw kae;
|
||||||
} finally {
|
} finally {
|
||||||
if(oldKeytabFile != null) keytabFile = oldKeytabFile;
|
if(oldKeytabFile != null) keytabFile = oldKeytabFile;
|
||||||
if(oldKeytabPrincipal != null) keytabPrincipal = oldKeytabPrincipal;
|
if(oldKeytabPrincipal != null) keytabPrincipal = oldKeytabPrincipal;
|
||||||
|
|
Loading…
Reference in New Issue