Skip to content

Commit 359b03c

Browse files
authored
HDFS-16266. Add remote port information to HDFS audit log (#3538)
Reviewed-by: Akira Ajisaka <aajisaka@apache.org> Reviewed-by: Wei-Chiu Chuang <weichiu@apache.org> Signed-off-by: Takanobu Asanuma <tasanuma@apache.org>
1 parent a21895a commit 359b03c

File tree

12 files changed

+156
-20
lines changed

12 files changed

+156
-20
lines changed

hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/Server.java

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -369,11 +369,20 @@ public static int getCallRetryCount() {
369369
}
370370

371371
/** Returns the remote side ip address when invoked inside an RPC
372-
* Returns null incase of an error.
372+
* Returns null in case of an error.
373373
*/
374374
public static InetAddress getRemoteIp() {
375375
Call call = CurCall.get();
376-
return (call != null ) ? call.getHostInetAddress() : null;
376+
return (call != null) ? call.getHostInetAddress() : null;
377+
}
378+
379+
/**
380+
* Returns the remote side port when invoked inside an RPC
381+
* Returns 0 in case of an error.
382+
*/
383+
public static int getRemotePort() {
384+
Call call = CurCall.get();
385+
return (call != null) ? call.getRemotePort() : 0;
377386
}
378387

379388
/**
@@ -409,7 +418,7 @@ public static byte[] getClientId() {
409418
Call call = CurCall.get();
410419
return call != null ? call.clientId : RpcConstants.DUMMY_CLIENT_ID;
411420
}
412-
421+
413422
/** Returns remote address as a string when invoked inside an RPC.
414423
* Returns null in case of an error.
415424
*/
@@ -447,15 +456,15 @@ public static int getPriorityLevel() {
447456
return call != null? call.getPriorityLevel() : 0;
448457
}
449458

450-
private String bindAddress;
459+
private String bindAddress;
451460
private int port; // port we listen on
452461
private int handlerCount; // number of handler threads
453462
private int readThreads; // number of read threads
454463
private int readerPendingConnectionQueue; // number of connections to queue per read thread
455464
private Class<? extends Writable> rpcRequestClass; // class used for deserializing the rpc request
456465
final protected RpcMetrics rpcMetrics;
457466
final protected RpcDetailedMetrics rpcDetailedMetrics;
458-
467+
459468
private Configuration conf;
460469
private String portRangeConfig = null;
461470
private SecretManager<TokenIdentifier> secretManager;
@@ -973,6 +982,9 @@ public UserGroupInformation getRemoteUser() {
973982
public InetAddress getHostInetAddress() {
974983
return null;
975984
}
985+
public int getRemotePort() {
986+
return 0;
987+
}
976988
public String getHostAddress() {
977989
InetAddress addr = getHostInetAddress();
978990
return (addr != null) ? addr.getHostAddress() : null;
@@ -1130,6 +1142,11 @@ public InetAddress getHostInetAddress() {
11301142
return connection.getHostInetAddress();
11311143
}
11321144

1145+
@Override
1146+
public int getRemotePort() {
1147+
return connection.getRemotePort();
1148+
}
1149+
11331150
@Override
11341151
public Void run() throws Exception {
11351152
if (!connection.channel.isOpen()) {
@@ -2011,6 +2028,10 @@ public int getIngressPort() {
20112028
return ingressPort;
20122029
}
20132030

2031+
public int getRemotePort() {
2032+
return remotePort;
2033+
}
2034+
20142035
public InetAddress getHostInetAddress() {
20152036
return addr;
20162037
}

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/DFSConfigKeys.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,10 @@ public class DFSConfigKeys extends CommonConfigurationKeys {
727727
"dfs.datanode.metrics.logger.period.seconds";
728728
public static final int DFS_DATANODE_METRICS_LOGGER_PERIOD_SECONDS_DEFAULT =
729729
600;
730+
public static final String DFS_NAMENODE_AUDIT_LOG_WITH_REMOTE_PORT_KEY =
731+
"dfs.namenode.audit.log.with.remote.port";
732+
public static final boolean DFS_NAMENODE_AUDIT_LOG_WITH_REMOTE_PORT_DEFAULT =
733+
false;
730734
/**
731735
* The maximum number of getBlocks RPCs data movement utilities can make to
732736
* a NameNode per second. Values &lt;= 0 disable throttling. This affects

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/common/JspHelper.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,9 @@ public static String getRemoteAddr(HttpServletRequest request) {
198198
return remoteAddr;
199199
}
200200

201+
public static int getRemotePort(HttpServletRequest request) {
202+
return request.getRemotePort();
203+
}
201204

202205
/**
203206
* Expected user name should be a short name.

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/AuditLogger.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,13 @@
1717
*/
1818
package org.apache.hadoop.hdfs.server.namenode;
1919

20-
import java.net.InetAddress;
21-
import java.security.Principal;
22-
2320
import org.apache.hadoop.classification.InterfaceAudience;
2421
import org.apache.hadoop.classification.InterfaceStability;
2522
import org.apache.hadoop.conf.Configuration;
2623
import org.apache.hadoop.fs.FileStatus;
2724

25+
import java.net.InetAddress;
26+
2827
/**
2928
* Interface defining an audit logger.
3029
*/

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FSNamesystem.java

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,16 @@
2424
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_CALLER_CONTEXT_ENABLED_KEY;
2525
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_CALLER_CONTEXT_MAX_SIZE_DEFAULT;
2626
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_CALLER_CONTEXT_MAX_SIZE_KEY;
27+
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_CALLER_CONTEXT_SEPARATOR_DEFAULT;
28+
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_CALLER_CONTEXT_SEPARATOR_KEY;
2729
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_CALLER_CONTEXT_SIGNATURE_MAX_SIZE_DEFAULT;
2830
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_CALLER_CONTEXT_SIGNATURE_MAX_SIZE_KEY;
2931
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.IO_FILE_BUFFER_SIZE_DEFAULT;
3032
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.IO_FILE_BUFFER_SIZE_KEY;
3133
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BLOCK_SIZE_DEFAULT;
3234
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_BLOCK_SIZE_KEY;
35+
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_AUDIT_LOG_WITH_REMOTE_PORT_DEFAULT;
36+
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_AUDIT_LOG_WITH_REMOTE_PORT_KEY;
3337
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_STORAGE_POLICY_ENABLED_DEFAULT;
3438
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_STORAGE_POLICY_PERMISSIONS_SUPERUSER_ONLY_DEFAULT;
3539
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_STORAGE_POLICY_PERMISSIONS_SUPERUSER_ONLY_KEY;
@@ -397,6 +401,9 @@ public class FSNamesystem implements Namesystem, FSNamesystemMBean,
397401
@Metric final MutableRatesWithAggregation detailedLockHoldTimeMetrics =
398402
registry.newRatesWithAggregation("detailedLockHoldTimeMetrics");
399403

404+
private static final String CLIENT_PORT_STR = "clientPort";
405+
private final String contextFieldSeparator;
406+
400407
boolean isAuditEnabled() {
401408
return (!isDefaultAuditLogger || auditLog.isInfoEnabled())
402409
&& !auditLoggers.isEmpty();
@@ -411,7 +418,7 @@ private void logAuditEvent(boolean succeeded, String cmd, String src,
411418
String dst, FileStatus stat) throws IOException {
412419
if (isAuditEnabled() && isExternalInvocation()) {
413420
logAuditEvent(succeeded, Server.getRemoteUser(), Server.getRemoteIp(),
414-
cmd, src, dst, stat);
421+
cmd, src, dst, stat);
415422
}
416423
}
417424

@@ -442,6 +449,9 @@ private void logAuditEvent(boolean succeeded,
442449
for (AuditLogger logger : auditLoggers) {
443450
if (logger instanceof HdfsAuditLogger) {
444451
HdfsAuditLogger hdfsLogger = (HdfsAuditLogger) logger;
452+
if (auditLogWithRemotePort) {
453+
appendClientPortToCallerContextIfAbsent();
454+
}
445455
hdfsLogger.logAuditEvent(succeeded, ugiStr, addr, cmd, src, dst,
446456
status, CallerContext.getCurrent(), ugi, dtSecretManager);
447457
} else {
@@ -450,6 +460,25 @@ private void logAuditEvent(boolean succeeded,
450460
}
451461
}
452462

463+
private void appendClientPortToCallerContextIfAbsent() {
464+
final CallerContext ctx = CallerContext.getCurrent();
465+
if (isClientPortInfoAbsent(CLIENT_PORT_STR + ":" + Server.getRemotePort(),
466+
ctx)) {
467+
String origContext = ctx == null ? null : ctx.getContext();
468+
byte[] origSignature = ctx == null ? null : ctx.getSignature();
469+
CallerContext.setCurrent(
470+
new CallerContext.Builder(origContext, contextFieldSeparator)
471+
.append(CLIENT_PORT_STR, String.valueOf(Server.getRemotePort()))
472+
.setSignature(origSignature)
473+
.build());
474+
}
475+
}
476+
477+
private boolean isClientPortInfoAbsent(String clientPortInfo, CallerContext ctx){
478+
return ctx == null || ctx.getContext() == null
479+
|| !ctx.getContext().contains(clientPortInfo);
480+
}
481+
453482
/**
454483
* Logger for audit events, noting successful FSNamesystem operations. Emits
455484
* to FSNamesystem.audit at INFO. Each event causes a set of tab-separated
@@ -501,6 +530,7 @@ private void logAuditEvent(boolean succeeded,
501530
// underlying logger is disabled, and avoid some unnecessary work.
502531
private final boolean isDefaultAuditLogger;
503532
private final List<AuditLogger> auditLoggers;
533+
private final boolean auditLogWithRemotePort;
504534

505535
/** The namespace tree. */
506536
FSDirectory dir;
@@ -833,6 +863,12 @@ static FSNamesystem loadFromDisk(Configuration conf) throws IOException {
833863
LOG.info("Enabling async auditlog");
834864
enableAsyncAuditLog(conf);
835865
}
866+
auditLogWithRemotePort =
867+
conf.getBoolean(DFS_NAMENODE_AUDIT_LOG_WITH_REMOTE_PORT_KEY,
868+
DFS_NAMENODE_AUDIT_LOG_WITH_REMOTE_PORT_DEFAULT);
869+
this.contextFieldSeparator =
870+
conf.get(HADOOP_CALLER_CONTEXT_SEPARATOR_KEY,
871+
HADOOP_CALLER_CONTEXT_SEPARATOR_DEFAULT);
836872
fsLock = new FSNamesystemLock(conf, detailedLockHoldTimeMetrics);
837873
cond = fsLock.newWriteLockCondition();
838874
cpLock = new ReentrantLock();

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/FsckServlet.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,9 +48,9 @@ public void doGet(HttpServletRequest request, HttpServletResponse response
4848
@SuppressWarnings("unchecked")
4949
final Map<String,String[]> pmap = request.getParameterMap();
5050
final PrintWriter out = response.getWriter();
51-
final InetAddress remoteAddress =
51+
final InetAddress remoteAddress =
5252
InetAddress.getByName(request.getRemoteAddr());
53-
final ServletContext context = getServletContext();
53+
final ServletContext context = getServletContext();
5454
final Configuration conf = NameNodeHttpServer.getConfFromContext(context);
5555

5656
final UserGroupInformation ugi = getUGI(request, conf);

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/HdfsAuditLogger.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,15 @@
1717
*/
1818
package org.apache.hadoop.hdfs.server.namenode;
1919

20-
import java.net.InetAddress;
21-
2220
import org.apache.hadoop.classification.InterfaceAudience;
2321
import org.apache.hadoop.classification.InterfaceStability;
2422
import org.apache.hadoop.fs.FileStatus;
2523
import org.apache.hadoop.hdfs.security.token.delegation.DelegationTokenSecretManager;
2624
import org.apache.hadoop.ipc.CallerContext;
2725
import org.apache.hadoop.security.UserGroupInformation;
2826

27+
import java.net.InetAddress;
28+
2929
/**
3030
* Extension of {@link AuditLogger}.
3131
*/

hadoop-hdfs-project/hadoop-hdfs/src/main/java/org/apache/hadoop/hdfs/server/namenode/web/resources/NamenodeWebHdfsMethods.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ public class NamenodeWebHdfsMethods {
133133
private String scheme;
134134
private Principal userPrincipal;
135135
private String remoteAddr;
136+
private int remotePort;
136137

137138
private @Context ServletContext context;
138139
private @Context HttpServletResponse response;
@@ -147,6 +148,7 @@ public NamenodeWebHdfsMethods(@Context HttpServletRequest request) {
147148
// get the remote address, if coming in via a trusted proxy server then
148149
// the address with be that of the proxied client
149150
remoteAddr = JspHelper.getRemoteAddr(request);
151+
remotePort = JspHelper.getRemotePort(request);
150152
supportEZ =
151153
Boolean.valueOf(request.getHeader(WebHdfsFileSystem.EZ_HEADER));
152154
}
@@ -225,6 +227,10 @@ public String getHostAddress() {
225227
return getRemoteAddr();
226228
}
227229
@Override
230+
public int getRemotePort() {
231+
return getRemotePortFromJSPHelper();
232+
}
233+
@Override
228234
public InetAddress getHostInetAddress() {
229235
try {
230236
return InetAddress.getByName(getHostAddress());
@@ -255,6 +261,10 @@ protected String getRemoteAddr() {
255261
return remoteAddr;
256262
}
257263

264+
protected int getRemotePortFromJSPHelper() {
265+
return remotePort;
266+
}
267+
258268
protected void queueExternalCall(ExternalCall call)
259269
throws IOException, InterruptedException {
260270
final NameNode namenode = (NameNode)context.getAttribute("name.node");

hadoop-hdfs-project/hadoop-hdfs/src/main/resources/hdfs-default.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5056,6 +5056,14 @@
50565056
</description>
50575057
</property>
50585058

5059+
<property>
5060+
<name>dfs.namenode.audit.log.with.remote.port</name>
5061+
<value>false</value>
5062+
<description>
5063+
If true, adds a port of RPC call to callerContext for all audit log events.
5064+
</description>
5065+
</property>
5066+
50595067
<property>
50605068
<name>dfs.namenode.available-space-block-placement-policy.balanced-space-preference-fraction</name>
50615069
<value>0.6</value>

hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestAuditLogger.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
import java.net.URI;
5656
import java.net.URISyntaxException;
5757
import java.util.List;
58+
import java.util.regex.Pattern;
5859

5960
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_CALLER_CONTEXT_ENABLED_KEY;
6061
import static org.apache.hadoop.fs.CommonConfigurationKeysPublic.HADOOP_CALLER_CONTEXT_MAX_SIZE_KEY;
@@ -69,6 +70,7 @@
6970
import static org.apache.hadoop.fs.permission.FsAction.READ_EXECUTE;
7071
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_ACLS_ENABLED_KEY;
7172
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_AUDIT_LOGGERS_KEY;
73+
import static org.apache.hadoop.hdfs.DFSConfigKeys.DFS_NAMENODE_AUDIT_LOG_WITH_REMOTE_PORT_KEY;
7274
import static org.apache.hadoop.hdfs.DFSConfigKeys.NNTOP_ENABLED_KEY;
7375
import static org.junit.Assert.assertEquals;
7476
import static org.junit.Assert.assertFalse;
@@ -89,6 +91,20 @@ public class TestAuditLogger {
8991
}
9092

9193
private static final short TEST_PERMISSION = (short) 0654;
94+
private static final Pattern AUDIT_PATTERN = Pattern.compile(
95+
".*allowed=.*?\\s" +
96+
"ugi=.*?\\s" +
97+
"ip=/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\s" +
98+
"cmd=.*?\\ssrc=.*?\\sdst=null\\s" +
99+
"perm=.*?");
100+
private static final Pattern AUDIT_WITH_PORT_PATTERN = Pattern.compile(
101+
".*allowed=.*?\\s" +
102+
"ugi=.*?\\s" +
103+
"ip=/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\s" +
104+
"cmd=.*?\\ssrc=.*?\\sdst=null\\s" +
105+
"perm=.*?" +
106+
"proto=.*?" +
107+
"callerContext=.*?clientPort\\:(\\d{0,9}).*?");
92108

93109
@Before
94110
public void setup() {
@@ -544,6 +560,45 @@ public void testBrokenLogger() throws IOException {
544560
}
545561
}
546562

563+
/**
564+
* Test adding remote port to audit log.
565+
*/
566+
@Test
567+
public void testAuditLogWithRemotePort() throws Exception {
568+
// Audit log without remote port by default.
569+
Configuration conf = new HdfsConfiguration();
570+
MiniDFSCluster cluster1 = new MiniDFSCluster.Builder(conf).build();
571+
try {
572+
LogCapturer auditLog = LogCapturer.captureLogs(FSNamesystem.auditLog);
573+
cluster1.waitClusterUp();
574+
FileSystem fs = cluster1.getFileSystem();
575+
long time = System.currentTimeMillis();
576+
fs.setTimes(new Path("/"), time, time);
577+
assertTrue(AUDIT_PATTERN.matcher(auditLog.getOutput().trim()).matches());
578+
assertFalse(auditLog.getOutput().contains("clientPort"));
579+
auditLog.clearOutput();
580+
} finally {
581+
cluster1.shutdown();
582+
}
583+
584+
// Audit log with remote port.
585+
conf.setBoolean(DFS_NAMENODE_AUDIT_LOG_WITH_REMOTE_PORT_KEY, true);
586+
conf.setBoolean(HADOOP_CALLER_CONTEXT_ENABLED_KEY, true);
587+
MiniDFSCluster cluster2 = new MiniDFSCluster.Builder(conf).build();
588+
try {
589+
LogCapturer auditLog = LogCapturer.captureLogs(FSNamesystem.auditLog);
590+
cluster2.waitClusterUp();
591+
FileSystem fs = cluster2.getFileSystem();
592+
long time = System.currentTimeMillis();
593+
fs.setTimes(new Path("/"), time, time);
594+
assertTrue(AUDIT_WITH_PORT_PATTERN.matcher(
595+
auditLog.getOutput().trim()).matches());
596+
auditLog.clearOutput();
597+
} finally {
598+
cluster2.shutdown();
599+
}
600+
}
601+
547602
public static class DummyAuditLogger implements AuditLogger {
548603

549604
static boolean initialized;

hadoop-hdfs-project/hadoop-hdfs/src/test/java/org/apache/hadoop/hdfs/server/namenode/TestAuditLogs.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,9 @@ public TestAuditLogs(boolean useAsyncLog, boolean useAsyncEdits) {
9292
// allowed=(true|false) ugi=name ip=/address cmd={cmd} src={path} dst=null perm=null
9393
static final Pattern auditPattern = Pattern.compile(
9494
"allowed=.*?\\s" +
95-
"ugi=.*?\\s" +
96-
"ip=/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\s" +
97-
"cmd=.*?\\ssrc=.*?\\sdst=null\\s" +
95+
"ugi=.*?\\s" +
96+
"ip=/\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\s" +
97+
"cmd=.*?\\ssrc=.*?\\sdst=null\\s" +
9898
"perm=.*?");
9999
static final Pattern successPattern = Pattern.compile(
100100
".*allowed=true.*");

0 commit comments

Comments
 (0)