HDFS-3680. Allow customized audit logging in HDFS FSNamesystem. Contributed by Marcelo Vanzin.

git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1415794 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Aaron Myers 2012-11-30 19:11:24 +00:00
parent bf7d772b79
commit a85a0293c7
6 changed files with 341 additions and 55 deletions

View File

@ -490,6 +490,9 @@ Release 2.0.3-alpha - Unreleased
HDFS-4214. OfflineEditsViewer should print out the offset at which it HDFS-4214. OfflineEditsViewer should print out the offset at which it
encountered an error. (Colin Patrick McCabe via atm) encountered an error. (Colin Patrick McCabe via atm)
HDFS-3680. Allow customized audit logging in HDFS FSNamesystem. (Marcelo
Vanzin via atm)
OPTIMIZATIONS OPTIMIZATIONS
BUG FIXES BUG FIXES

View File

@ -246,6 +246,8 @@ public class DFSConfigKeys extends CommonConfigurationKeys {
public static final String DFS_HOSTS = "dfs.hosts"; public static final String DFS_HOSTS = "dfs.hosts";
public static final String DFS_HOSTS_EXCLUDE = "dfs.hosts.exclude"; public static final String DFS_HOSTS_EXCLUDE = "dfs.hosts.exclude";
public static final String DFS_CLIENT_LOCAL_INTERFACES = "dfs.client.local.interfaces"; public static final String DFS_CLIENT_LOCAL_INTERFACES = "dfs.client.local.interfaces";
public static final String DFS_NAMENODE_AUDIT_LOGGERS_KEY = "dfs.namenode.audit.loggers";
public static final String DFS_NAMENODE_DEFAULT_AUDIT_LOGGER_NAME = "default";
// Much code in hdfs is not yet updated to use these keys. // Much code in hdfs is not yet updated to use these keys.
public static final String DFS_CLIENT_BLOCK_WRITE_LOCATEFOLLOWINGBLOCK_RETRIES_KEY = "dfs.client.block.write.locateFollowingBlock.retries"; public static final String DFS_CLIENT_BLOCK_WRITE_LOCATEFOLLOWINGBLOCK_RETRIES_KEY = "dfs.client.block.write.locateFollowingBlock.retries";

View File

@ -0,0 +1,61 @@
/**
* 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.hdfs.server.namenode;
import java.net.InetAddress;
import java.security.Principal;
import org.apache.hadoop.classification.InterfaceAudience;
import org.apache.hadoop.classification.InterfaceStability;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
/**
* Interface defining an audit logger.
*/
@InterfaceAudience.Public
@InterfaceStability.Evolving
public interface AuditLogger {
/**
* Called during initialization of the logger.
*
* @param conf The configuration object.
*/
void initialize(Configuration conf);
/**
* Called to log an audit event.
* <p>
* This method must return as quickly as possible, since it's called
* in a critical section of the NameNode's operation.
*
* @param succeeded Whether authorization succeeded.
* @param userName Name of the user executing the request.
* @param addr Remote address of the request.
* @param cmd The requested command.
* @param src Path of affected source file.
* @param dst Path of affected destination file (if any).
* @param stat File information for operations that change the file's
* metadata (permissions, owner, times, etc).
*/
void logAuditEvent(boolean succeeded, String userName,
InetAddress addr, String cmd, String src, String dst,
FileStatus stat);
}

View File

@ -34,6 +34,8 @@
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_HA_STANDBY_CHECKPOINTS_DEFAULT; import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_HA_STANDBY_CHECKPOINTS_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_HA_STANDBY_CHECKPOINTS_KEY; import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_HA_STANDBY_CHECKPOINTS_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_ACCESSTIME_PRECISION_KEY; import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_ACCESSTIME_PRECISION_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_AUDIT_LOGGERS_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_DEFAULT_AUDIT_LOGGER_NAME;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_DELEGATION_KEY_UPDATE_INTERVAL_DEFAULT; import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_DELEGATION_KEY_UPDATE_INTERVAL_DEFAULT;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_DELEGATION_KEY_UPDATE_INTERVAL_KEY; import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_DELEGATION_KEY_UPDATE_INTERVAL_KEY;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_DELEGATION_TOKEN_ALWAYS_USE_DEFAULT; import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_DELEGATION_TOKEN_ALWAYS_USE_DEFAULT;
@ -111,6 +113,7 @@
import org.apache.hadoop.fs.ContentSummary; import org.apache.hadoop.fs.ContentSummary;
import org.apache.hadoop.fs.CreateFlag; import org.apache.hadoop.fs.CreateFlag;
import org.apache.hadoop.fs.FileAlreadyExistsException; import org.apache.hadoop.fs.FileAlreadyExistsException;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FsServerDefaults; import org.apache.hadoop.fs.FsServerDefaults;
import org.apache.hadoop.fs.InvalidPathException; import org.apache.hadoop.fs.InvalidPathException;
import org.apache.hadoop.fs.Options; import org.apache.hadoop.fs.Options;
@ -246,32 +249,32 @@ protected StringBuilder initialValue() {
} }
}; };
private static final void logAuditEvent(UserGroupInformation ugi, private boolean isAuditEnabled() {
return !isDefaultAuditLogger || auditLog.isInfoEnabled();
}
private void logAuditEvent(UserGroupInformation ugi,
InetAddress addr, String cmd, String src, String dst, InetAddress addr, String cmd, String src, String dst,
HdfsFileStatus stat) { HdfsFileStatus stat) {
logAuditEvent(true, ugi, addr, cmd, src, dst, stat); logAuditEvent(true, ugi, addr, cmd, src, dst, stat);
} }
private static final void logAuditEvent(boolean succeeded, private void logAuditEvent(boolean succeeded,
UserGroupInformation ugi, InetAddress addr, String cmd, String src, UserGroupInformation ugi, InetAddress addr, String cmd, String src,
String dst, HdfsFileStatus stat) { String dst, HdfsFileStatus stat) {
final StringBuilder sb = auditBuffer.get(); FileStatus status = null;
sb.setLength(0); if (stat != null) {
sb.append("allowed=").append(succeeded).append("\t"); Path symlink = stat.isSymlink() ? new Path(stat.getSymlink()) : null;
sb.append("ugi=").append(ugi).append("\t"); Path path = dst != null ? new Path(dst) : new Path(src);
sb.append("ip=").append(addr).append("\t"); status = new FileStatus(stat.getLen(), stat.isDir(),
sb.append("cmd=").append(cmd).append("\t"); stat.getReplication(), stat.getBlockSize(), stat.getModificationTime(),
sb.append("src=").append(src).append("\t"); stat.getAccessTime(), stat.getPermission(), stat.getOwner(),
sb.append("dst=").append(dst).append("\t"); stat.getGroup(), symlink, path);
if (null == stat) { }
sb.append("perm=null"); for (AuditLogger logger : auditLoggers) {
} else { logger.logAuditEvent(succeeded, ugi.toString(), addr,
sb.append("perm="); cmd, src, dst, status);
sb.append(stat.getOwner()).append(":");
sb.append(stat.getGroup()).append(":");
sb.append(stat.getPermission());
} }
auditLog.info(sb);
} }
/** /**
@ -304,6 +307,11 @@ private static final void logAuditEvent(boolean succeeded,
final DelegationTokenSecretManager dtSecretManager; final DelegationTokenSecretManager dtSecretManager;
private final boolean alwaysUseDelegationTokensForTests; private final boolean alwaysUseDelegationTokensForTests;
// Tracks whether the default audit logger is the only configured audit
// logger; this allows isAuditEnabled() to return false in case the
// underlying logger is disabled, and avoid some unnecessary work.
private final boolean isDefaultAuditLogger;
private final List<AuditLogger> auditLoggers;
/** The namespace tree. */ /** The namespace tree. */
FSDirectory dir; FSDirectory dir;
@ -536,14 +544,50 @@ public static FSNamesystem loadFromDisk(Configuration conf,
this.dtSecretManager = createDelegationTokenSecretManager(conf); this.dtSecretManager = createDelegationTokenSecretManager(conf);
this.dir = new FSDirectory(fsImage, this, conf); this.dir = new FSDirectory(fsImage, this, conf);
this.safeMode = new SafeModeInfo(conf); this.safeMode = new SafeModeInfo(conf);
this.auditLoggers = initAuditLoggers(conf);
this.isDefaultAuditLogger = auditLoggers.size() == 1 &&
auditLoggers.get(0) instanceof DefaultAuditLogger;
} catch(IOException e) { } catch(IOException e) {
LOG.error(getClass().getSimpleName() + " initialization failed.", e); LOG.error(getClass().getSimpleName() + " initialization failed.", e);
close(); close();
throw e; throw e;
} catch (RuntimeException re) {
LOG.error(getClass().getSimpleName() + " initialization failed.", re);
close();
throw re;
} }
} }
private List<AuditLogger> initAuditLoggers(Configuration conf) {
// Initialize the custom access loggers if configured.
Collection<String> alClasses = conf.getStringCollection(DFS_NAMENODE_AUDIT_LOGGERS_KEY);
List<AuditLogger> auditLoggers = Lists.newArrayList();
if (alClasses != null && !alClasses.isEmpty()) {
for (String className : alClasses) {
try {
AuditLogger logger;
if (DFS_NAMENODE_DEFAULT_AUDIT_LOGGER_NAME.equals(className)) {
logger = new DefaultAuditLogger();
} else {
logger = (AuditLogger) Class.forName(className).newInstance();
}
logger.initialize(conf);
auditLoggers.add(logger);
} catch (RuntimeException re) {
throw re;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
// Make sure there is at least one logger installed.
if (auditLoggers.isEmpty()) {
auditLoggers.add(new DefaultAuditLogger());
}
return auditLoggers;
}
void loadFSImage(StartupOption startOpt, FSImage fsImage, boolean haEnabled) void loadFSImage(StartupOption startOpt, FSImage fsImage, boolean haEnabled)
throws IOException { throws IOException {
// format before starting up if requested // format before starting up if requested
@ -1076,7 +1120,7 @@ void setPermission(String src, FsPermission permission)
try { try {
setPermissionInt(src, permission); setPermissionInt(src, permission);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getCurrentUser(), logAuditEvent(false, UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"setPermission", src, null, null); "setPermission", src, null, null);
@ -1098,14 +1142,14 @@ private void setPermissionInt(String src, FsPermission permission)
} }
checkOwner(src); checkOwner(src);
dir.setPermission(src, permission); dir.setPermission(src, permission);
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
resultingStat = dir.getFileInfo(src, false); resultingStat = dir.getFileInfo(src, false);
} }
} finally { } finally {
writeUnlock(); writeUnlock();
} }
getEditLog().logSync(); getEditLog().logSync();
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"setPermission", src, null, resultingStat); "setPermission", src, null, resultingStat);
@ -1122,7 +1166,7 @@ void setOwner(String src, String username, String group)
try { try {
setOwnerInt(src, username, group); setOwnerInt(src, username, group);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getCurrentUser(), logAuditEvent(false, UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"setOwner", src, null, null); "setOwner", src, null, null);
@ -1153,14 +1197,14 @@ private void setOwnerInt(String src, String username, String group)
} }
} }
dir.setOwner(src, username, group); dir.setOwner(src, username, group);
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
resultingStat = dir.getFileInfo(src, false); resultingStat = dir.getFileInfo(src, false);
} }
} finally { } finally {
writeUnlock(); writeUnlock();
} }
getEditLog().logSync(); getEditLog().logSync();
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"setOwner", src, null, resultingStat); "setOwner", src, null, resultingStat);
@ -1203,7 +1247,7 @@ LocatedBlocks getBlockLocations(String src, long offset, long length,
return getBlockLocationsInt(src, offset, length, doAccessTime, return getBlockLocationsInt(src, offset, length, doAccessTime,
needBlockToken, checkSafeMode); needBlockToken, checkSafeMode);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getCurrentUser(), logAuditEvent(false, UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"open", src, null, null); "open", src, null, null);
@ -1229,7 +1273,7 @@ private LocatedBlocks getBlockLocationsInt(String src, long offset, long length,
} }
final LocatedBlocks ret = getBlockLocationsUpdateTimes(src, final LocatedBlocks ret = getBlockLocationsUpdateTimes(src,
offset, length, doAccessTime, needBlockToken); offset, length, doAccessTime, needBlockToken);
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"open", src, null, null); "open", src, null, null);
@ -1310,7 +1354,7 @@ void concat(String target, String [] srcs)
try { try {
concatInt(target, srcs); concatInt(target, srcs);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getLoginUser(), logAuditEvent(false, UserGroupInformation.getLoginUser(),
getRemoteIp(), getRemoteIp(),
"concat", Arrays.toString(srcs), target, null); "concat", Arrays.toString(srcs), target, null);
@ -1353,14 +1397,14 @@ private void concatInt(String target, String [] srcs)
throw new SafeModeException("Cannot concat " + target, safeMode); throw new SafeModeException("Cannot concat " + target, safeMode);
} }
concatInternal(target, srcs); concatInternal(target, srcs);
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
resultingStat = dir.getFileInfo(target, false); resultingStat = dir.getFileInfo(target, false);
} }
} finally { } finally {
writeUnlock(); writeUnlock();
} }
getEditLog().logSync(); getEditLog().logSync();
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(UserGroupInformation.getLoginUser(), logAuditEvent(UserGroupInformation.getLoginUser(),
getRemoteIp(), getRemoteIp(),
"concat", Arrays.toString(srcs), target, resultingStat); "concat", Arrays.toString(srcs), target, resultingStat);
@ -1481,7 +1525,7 @@ void setTimes(String src, long mtime, long atime)
try { try {
setTimesInt(src, mtime, atime); setTimesInt(src, mtime, atime);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getCurrentUser(), logAuditEvent(false, UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"setTimes", src, null, null); "setTimes", src, null, null);
@ -1507,7 +1551,7 @@ private void setTimesInt(String src, long mtime, long atime)
INode inode = dir.getINode(src); INode inode = dir.getINode(src);
if (inode != null) { if (inode != null) {
dir.setTimes(src, inode, mtime, atime, true); dir.setTimes(src, inode, mtime, atime, true);
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
final HdfsFileStatus stat = dir.getFileInfo(src, false); final HdfsFileStatus stat = dir.getFileInfo(src, false);
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
@ -1530,7 +1574,7 @@ void createSymlink(String target, String link,
try { try {
createSymlinkInt(target, link, dirPerms, createParent); createSymlinkInt(target, link, dirPerms, createParent);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getCurrentUser(), logAuditEvent(false, UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"createSymlink", link, target, null); "createSymlink", link, target, null);
@ -1551,14 +1595,14 @@ private void createSymlinkInt(String target, String link,
verifyParentDir(link); verifyParentDir(link);
} }
createSymlinkInternal(target, link, dirPerms, createParent); createSymlinkInternal(target, link, dirPerms, createParent);
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
resultingStat = dir.getFileInfo(link, false); resultingStat = dir.getFileInfo(link, false);
} }
} finally { } finally {
writeUnlock(); writeUnlock();
} }
getEditLog().logSync(); getEditLog().logSync();
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"createSymlink", link, target, resultingStat); "createSymlink", link, target, resultingStat);
@ -1614,7 +1658,7 @@ boolean setReplication(final String src, final short replication)
try { try {
return setReplicationInt(src, replication); return setReplicationInt(src, replication);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getCurrentUser(), logAuditEvent(false, UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"setReplication", src, null, null); "setReplication", src, null, null);
@ -1650,7 +1694,7 @@ private boolean setReplicationInt(final String src, final short replication)
} }
getEditLog().logSync(); getEditLog().logSync();
if (isFile && auditLog.isInfoEnabled() && isExternalInvocation()) { if (isFile && isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"setReplication", src, null, null); "setReplication", src, null, null);
@ -1706,7 +1750,7 @@ void startFile(String src, PermissionStatus permissions, String holder,
startFileInt(src, permissions, holder, clientMachine, flag, createParent, startFileInt(src, permissions, holder, clientMachine, flag, createParent,
replication, blockSize); replication, blockSize);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getCurrentUser(), logAuditEvent(false, UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"create", src, null, null); "create", src, null, null);
@ -1739,7 +1783,7 @@ private void startFileInt(String src, PermissionStatus permissions, String holde
} }
} }
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
final HdfsFileStatus stat = dir.getFileInfo(src, false); final HdfsFileStatus stat = dir.getFileInfo(src, false);
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
@ -2040,7 +2084,7 @@ LocatedBlock appendFile(String src, String holder, String clientMachine)
try { try {
return appendFileInt(src, holder, clientMachine); return appendFileInt(src, holder, clientMachine);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getCurrentUser(), logAuditEvent(false, UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"append", src, null, null); "append", src, null, null);
@ -2086,7 +2130,7 @@ private LocatedBlock appendFileInt(String src, String holder, String clientMachi
+" block size " + lb.getBlock().getNumBytes()); +" block size " + lb.getBlock().getNumBytes());
} }
} }
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"append", src, null, null); "append", src, null, null);
@ -2532,7 +2576,7 @@ boolean renameTo(String src, String dst)
try { try {
return renameToInt(src, dst); return renameToInt(src, dst);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getCurrentUser(), logAuditEvent(false, UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"rename", src, dst, null); "rename", src, dst, null);
@ -2554,14 +2598,14 @@ private boolean renameToInt(String src, String dst)
checkOperation(OperationCategory.WRITE); checkOperation(OperationCategory.WRITE);
status = renameToInternal(src, dst); status = renameToInternal(src, dst);
if (status && auditLog.isInfoEnabled() && isExternalInvocation()) { if (status && isAuditEnabled() && isExternalInvocation()) {
resultingStat = dir.getFileInfo(dst, false); resultingStat = dir.getFileInfo(dst, false);
} }
} finally { } finally {
writeUnlock(); writeUnlock();
} }
getEditLog().logSync(); getEditLog().logSync();
if (status && auditLog.isInfoEnabled() && isExternalInvocation()) { if (status && isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"rename", src, dst, resultingStat); "rename", src, dst, resultingStat);
@ -2611,14 +2655,14 @@ void renameTo(String src, String dst, Options.Rename... options)
checkOperation(OperationCategory.WRITE); checkOperation(OperationCategory.WRITE);
renameToInternal(src, dst, options); renameToInternal(src, dst, options);
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
resultingStat = dir.getFileInfo(dst, false); resultingStat = dir.getFileInfo(dst, false);
} }
} finally { } finally {
writeUnlock(); writeUnlock();
} }
getEditLog().logSync(); getEditLog().logSync();
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
StringBuilder cmd = new StringBuilder("rename options="); StringBuilder cmd = new StringBuilder("rename options=");
for (Rename option : options) { for (Rename option : options) {
cmd.append(option.value()).append(" "); cmd.append(option.value()).append(" ");
@ -2659,7 +2703,7 @@ boolean delete(String src, boolean recursive)
try { try {
return deleteInt(src, recursive); return deleteInt(src, recursive);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getCurrentUser(), logAuditEvent(false, UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"delete", src, null, null); "delete", src, null, null);
@ -2675,7 +2719,7 @@ private boolean deleteInt(String src, boolean recursive)
NameNode.stateChangeLog.debug("DIR* NameSystem.delete: " + src); NameNode.stateChangeLog.debug("DIR* NameSystem.delete: " + src);
} }
boolean status = deleteInternal(src, recursive, true); boolean status = deleteInternal(src, recursive, true);
if (status && auditLog.isInfoEnabled() && isExternalInvocation()) { if (status && isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"delete", src, null, null); "delete", src, null, null);
@ -2841,7 +2885,7 @@ HdfsFileStatus getFileInfo(String src, boolean resolveLink)
} }
stat = dir.getFileInfo(src, resolveLink); stat = dir.getFileInfo(src, resolveLink);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getCurrentUser(), logAuditEvent(false, UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"getfileinfo", src, null, null); "getfileinfo", src, null, null);
@ -2850,7 +2894,7 @@ HdfsFileStatus getFileInfo(String src, boolean resolveLink)
} finally { } finally {
readUnlock(); readUnlock();
} }
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"getfileinfo", src, null, null); "getfileinfo", src, null, null);
@ -2866,7 +2910,7 @@ boolean mkdirs(String src, PermissionStatus permissions,
try { try {
return mkdirsInt(src, permissions, createParent); return mkdirsInt(src, permissions, createParent);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getCurrentUser(), logAuditEvent(false, UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"mkdirs", src, null, null); "mkdirs", src, null, null);
@ -2890,7 +2934,7 @@ private boolean mkdirsInt(String src, PermissionStatus permissions,
writeUnlock(); writeUnlock();
} }
getEditLog().logSync(); getEditLog().logSync();
if (status && auditLog.isInfoEnabled() && isExternalInvocation()) { if (status && isAuditEnabled() && isExternalInvocation()) {
final HdfsFileStatus stat = dir.getFileInfo(src, false); final HdfsFileStatus stat = dir.getFileInfo(src, false);
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
@ -3319,7 +3363,7 @@ DirectoryListing getListing(String src, byte[] startAfter,
try { try {
return getListingInt(src, startAfter, needLocation); return getListingInt(src, startAfter, needLocation);
} catch (AccessControlException e) { } catch (AccessControlException e) {
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(false, UserGroupInformation.getCurrentUser(), logAuditEvent(false, UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"listStatus", src, null, null); "listStatus", src, null, null);
@ -3343,7 +3387,7 @@ private DirectoryListing getListingInt(String src, byte[] startAfter,
checkTraverse(src); checkTraverse(src);
} }
} }
if (auditLog.isInfoEnabled() && isExternalInvocation()) { if (isAuditEnabled() && isExternalInvocation()) {
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
getRemoteIp(), getRemoteIp(),
"listStatus", src, null, null); "listStatus", src, null, null);
@ -5286,7 +5330,7 @@ private static InetAddress getRemoteIp() {
* Log fsck event in the audit log * Log fsck event in the audit log
*/ */
void logFsckEvent(String src, InetAddress remoteAddress) throws IOException { void logFsckEvent(String src, InetAddress remoteAddress) throws IOException {
if (auditLog.isInfoEnabled()) { if (isAuditEnabled()) {
logAuditEvent(UserGroupInformation.getCurrentUser(), logAuditEvent(UserGroupInformation.getCurrentUser(),
remoteAddress, remoteAddress,
"fsck", src, null, null); "fsck", src, null, null);
@ -5541,4 +5585,44 @@ public boolean isAvoidingStaleDataNodesForWrite() {
return this.blockManager.getDatanodeManager() return this.blockManager.getDatanodeManager()
.isAvoidingStaleDataNodesForWrite(); .isAvoidingStaleDataNodesForWrite();
} }
/**
* Default AuditLogger implementation; used when no access logger is
* defined in the config file. It can also be explicitly listed in the
* config file.
*/
private static class DefaultAuditLogger implements AuditLogger {
@Override
public void initialize(Configuration conf) {
// Nothing to do.
}
@Override
public void logAuditEvent(boolean succeeded, String userName,
InetAddress addr, String cmd, String src, String dst,
FileStatus status) {
if (auditLog.isInfoEnabled()) {
final StringBuilder sb = auditBuffer.get();
sb.setLength(0);
sb.append("allowed=").append(succeeded).append("\t");
sb.append("ugi=").append(userName).append("\t");
sb.append("ip=").append(addr).append("\t");
sb.append("cmd=").append(cmd).append("\t");
sb.append("src=").append(src).append("\t");
sb.append("dst=").append(dst).append("\t");
if (null == status) {
sb.append("perm=null");
} else {
sb.append("perm=");
sb.append(status.getOwner()).append(":");
sb.append(status.getGroup()).append(":");
sb.append(status.getPermission());
}
auditLog.info(sb);
}
}
}
} }

View File

@ -1184,4 +1184,17 @@
</description> </description>
</property> </property>
<property>
<name>dfs.namenode.audit.loggers</name>
<value>default</value>
<description>
List of classes implementing audit loggers that will receive audit events.
These should be implementations of org.apache.hadoop.hdfs.server.namenode.AuditLogger.
The special value "default" can be used to reference the default audit
logger, which uses the configured log system. Installing custom audit loggers
may affect the performance and stability of the NameNode. Refer to the custom
logger's documentation for more details.
</description>
</property>
</configuration> </configuration>

View File

@ -0,0 +1,123 @@
/**
* 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.hdfs.server.namenode;
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_AUDIT_LOGGERS_KEY;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.net.InetAddress;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileStatus;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.hadoop.hdfs.HdfsConfiguration;
import org.apache.hadoop.hdfs.MiniDFSCluster;
import org.apache.hadoop.hdfs.protocol.HdfsFileStatus;
import org.apache.hadoop.ipc.RemoteException;
import org.apache.hadoop.security.UserGroupInformation;
import org.junit.Test;
/**
* Tests for the {@link AuditLogger} custom audit logging interface.
*/
public class TestAuditLogger {
/**
* Tests that AuditLogger works as expected.
*/
@Test
public void testAuditLogger() throws IOException {
Configuration conf = new HdfsConfiguration();
conf.set(DFS_NAMENODE_AUDIT_LOGGERS_KEY,
DummyAuditLogger.class.getName());
MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf).build();
try {
cluster.waitClusterUp();
assertTrue(DummyAuditLogger.initialized);
FileSystem fs = cluster.getFileSystem();
long time = System.currentTimeMillis();
fs.setTimes(new Path("/"), time, time);
assertEquals(1, DummyAuditLogger.logCount);
} finally {
cluster.shutdown();
}
}
/**
* Tests that a broken audit logger causes requests to fail.
*/
@Test
public void testBrokenLogger() throws IOException {
Configuration conf = new HdfsConfiguration();
conf.set(DFS_NAMENODE_AUDIT_LOGGERS_KEY,
BrokenAuditLogger.class.getName());
MiniDFSCluster cluster = new MiniDFSCluster.Builder(conf).build();
try {
cluster.waitClusterUp();
FileSystem fs = cluster.getFileSystem();
long time = System.currentTimeMillis();
fs.setTimes(new Path("/"), time, time);
fail("Expected exception due to broken audit logger.");
} catch (RemoteException re) {
// Expected.
} finally {
cluster.shutdown();
}
}
public static class DummyAuditLogger implements AuditLogger {
static boolean initialized;
static int logCount;
public void initialize(Configuration conf) {
initialized = true;
}
public void logAuditEvent(boolean succeeded, String userName,
InetAddress addr, String cmd, String src, String dst,
FileStatus stat) {
logCount++;
}
}
public static class BrokenAuditLogger implements AuditLogger {
public void initialize(Configuration conf) {
// No op.
}
public void logAuditEvent(boolean succeeded, String userName,
InetAddress addr, String cmd, String src, String dst,
FileStatus stat) {
throw new RuntimeException("uh oh");
}
}
}