HADOOP-13903. Improvements to KMS logging to help debug authorization errors. (Tristan Stevens via asuresh)

(cherry picked from commit be529dade1)
This commit is contained in:
Arun Suresh 2017-01-11 00:26:02 -08:00
parent b6258e2b15
commit 8e5de45e08
3 changed files with 80 additions and 7 deletions

View File

@ -236,9 +236,26 @@ public class KMSACLs implements Runnable, KeyACLs {
*/ */
public boolean hasAccess(Type type, UserGroupInformation ugi) { public boolean hasAccess(Type type, UserGroupInformation ugi) {
boolean access = acls.get(type).isUserAllowed(ugi); boolean access = acls.get(type).isUserAllowed(ugi);
if (LOG.isDebugEnabled()) {
LOG.debug("Checking user [{}] for: {} {} ", ugi.getShortUserName(),
type.toString(), acls.get(type).getAclString());
}
if (access) { if (access) {
AccessControlList blacklist = blacklistedAcls.get(type); AccessControlList blacklist = blacklistedAcls.get(type);
access = (blacklist == null) || !blacklist.isUserInList(ugi); access = (blacklist == null) || !blacklist.isUserInList(ugi);
if (LOG.isDebugEnabled()) {
if (blacklist == null) {
LOG.debug("No blacklist for {}", type.toString());
} else if (access) {
LOG.debug("user is in {}" , blacklist.getAclString());
} else {
LOG.debug("user is not in {}" , blacklist.getAclString());
}
}
}
if (LOG.isDebugEnabled()) {
LOG.debug("User: [{}], Type: {} Result: {}", ugi.getShortUserName(),
type.toString(), access);
} }
return access; return access;
} }
@ -259,8 +276,12 @@ public class KMSACLs implements Runnable, KeyACLs {
@Override @Override
public boolean hasAccessToKey(String keyName, UserGroupInformation ugi, public boolean hasAccessToKey(String keyName, UserGroupInformation ugi,
KeyOpType opType) { KeyOpType opType) {
return checkKeyAccess(keyName, ugi, opType) boolean access = checkKeyAccess(keyName, ugi, opType)
|| checkKeyAccess(whitelistKeyAcls, ugi, opType); || checkKeyAccess(whitelistKeyAcls, ugi, opType);
if (!access) {
KMSWebApp.getKMSAudit().unauthorized(ugi, opType, keyName);
}
return access;
} }
private boolean checkKeyAccess(String keyName, UserGroupInformation ugi, private boolean checkKeyAccess(String keyName, UserGroupInformation ugi,
@ -269,9 +290,15 @@ public class KMSACLs implements Runnable, KeyACLs {
if (keyAcl == null) { if (keyAcl == null) {
// If No key acl defined for this key, check to see if // If No key acl defined for this key, check to see if
// there are key defaults configured for this operation // there are key defaults configured for this operation
LOG.debug("Key: {} has no ACLs defined, using defaults.", keyName);
keyAcl = defaultKeyAcls; keyAcl = defaultKeyAcls;
} }
return checkKeyAccess(keyAcl, ugi, opType); boolean access = checkKeyAccess(keyAcl, ugi, opType);
if (LOG.isDebugEnabled()) {
LOG.debug("User: [{}], OpType: {}, KeyName: {} Result: {}",
ugi.getShortUserName(), opType.toString(), keyName, access);
}
return access;
} }
private boolean checkKeyAccess(Map<KeyOpType, AccessControlList> keyAcl, private boolean checkKeyAccess(Map<KeyOpType, AccessControlList> keyAcl,
@ -280,8 +307,13 @@ public class KMSACLs implements Runnable, KeyACLs {
if (acl == null) { if (acl == null) {
// If no acl is specified for this operation, // If no acl is specified for this operation,
// deny access // deny access
LOG.debug("No ACL available for key, denying access for {}", opType);
return false; return false;
} else { } else {
if (LOG.isDebugEnabled()) {
LOG.debug("Checking user [{}] for: {}: {}" + ugi.getShortUserName(),
opType.toString(), acl.getAclString());
}
return acl.isUserAllowed(ugi); return acl.isUserAllowed(ugi);
} }
} }

View File

@ -23,6 +23,8 @@ import static org.apache.hadoop.crypto.key.kms.server.KMSAuditLogger.OpStatus;
import com.google.common.annotations.VisibleForTesting; import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions; import com.google.common.base.Preconditions;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.crypto.key.kms.server.KMSACLs.Type;
import org.apache.hadoop.crypto.key.kms.server.KeyAuthorizationKeyProvider.KeyOpType;
import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.UserGroupInformation;
import org.apache.hadoop.util.ReflectionUtils; import org.apache.hadoop.util.ReflectionUtils;
import org.apache.hadoop.util.Time; import org.apache.hadoop.util.Time;
@ -168,7 +170,25 @@ public class KMSAudit {
} }
} }
private void op(final OpStatus opStatus, final KMS.KMSOp op, /**
* Logs to the audit service a single operation on the KMS or on a key.
*
* @param opStatus
* The outcome of the audited event
* @param op
* The operation being audited (either {@link KMS.KMSOp} or
* {@link Type} N.B this is passed as an {@link Object} to allow
* either enum to be passed in.
* @param ugi
* The user's security context
* @param key
* The String name of the key if applicable
* @param remoteHost
* The hostname of the requesting service
* @param extraMsg
* Any extra details for auditing
*/
private void op(final OpStatus opStatus, final Object op,
final UserGroupInformation ugi, final String key, final String remoteHost, final UserGroupInformation ugi, final String key, final String remoteHost,
final String extraMsg) { final String extraMsg) {
final String user = ugi == null ? null: ugi.getShortUserName(); final String user = ugi == null ? null: ugi.getShortUserName();
@ -215,6 +235,12 @@ public class KMSAudit {
op(OpStatus.UNAUTHORIZED, op, user, key, "Unknown", ""); op(OpStatus.UNAUTHORIZED, op, user, key, "Unknown", "");
} }
public void unauthorized(UserGroupInformation user, KeyOpType op,
String key) {
op(OpStatus.UNAUTHORIZED, op, user, key, "Unknown", "");
}
public void error(UserGroupInformation user, String method, String url, public void error(UserGroupInformation user, String method, String url,
String extraMsg) { String extraMsg) {
op(OpStatus.ERROR, null, user, null, "Unknown", "Method:'" + method op(OpStatus.ERROR, null, user, null, "Unknown", "Method:'" + method
@ -228,7 +254,7 @@ public class KMSAudit {
+ " URL:" + url + " ErrorMsg:'" + extraMsg + "'"); + " URL:" + url + " ErrorMsg:'" + extraMsg + "'");
} }
private static String createCacheKey(String user, String key, KMS.KMSOp op) { private static String createCacheKey(String user, String key, Object op) {
return user + "#" + key + "#" + op; return user + "#" + key + "#" + op;
} }

View File

@ -23,6 +23,7 @@ import java.util.concurrent.atomic.AtomicLong;
import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.crypto.key.kms.server.KMSACLs.Type;
import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.UserGroupInformation;
/** /**
@ -46,7 +47,7 @@ interface KMSAuditLogger {
*/ */
class AuditEvent { class AuditEvent {
private final AtomicLong accessCount = new AtomicLong(-1); private final AtomicLong accessCount = new AtomicLong(-1);
private final KMS.KMSOp op; private final Object op;
private final String keyName; private final String keyName;
private final String user; private final String user;
private final String impersonator; private final String impersonator;
@ -55,7 +56,21 @@ interface KMSAuditLogger {
private final long startTime = System.currentTimeMillis(); private final long startTime = System.currentTimeMillis();
private long endTime = startTime; private long endTime = startTime;
AuditEvent(KMS.KMSOp op, UserGroupInformation ugi, String keyName, /**
* @param op
* The operation being audited (either {@link KMS.KMSOp} or
* {@link Type} N.B this is passed as an {@link Object} to allow
* either enum to be passed in.
* @param ugi
* The user's security context
* @param keyName
* The String name of the key if applicable
* @param remoteHost
* The hostname of the requesting service
* @param msg
* Any extra details for auditing
*/
AuditEvent(Object op, UserGroupInformation ugi, String keyName,
String remoteHost, String msg) { String remoteHost, String msg) {
this.keyName = keyName; this.keyName = keyName;
if (ugi == null) { if (ugi == null) {
@ -79,7 +94,7 @@ interface KMSAuditLogger {
return accessCount; return accessCount;
} }
public KMS.KMSOp getOp() { public Object getOp() {
return op; return op;
} }