From 5fe19a0f01644dee22a847ac9497e097cadc7866 Mon Sep 17 00:00:00 2001 From: Simbarashe Dzinamarira Date: Wed, 22 Feb 2023 13:58:44 -0800 Subject: [PATCH] HDFS-16901: RBF: Propagates real user's username via the caller context, when a proxy user is being used. (#5346) --- .../org/apache/hadoop/ipc/CallerContext.java | 1 + .../federation/router/RouterRpcClient.java | 11 +++-- .../federation/router/TestRouterRpc.java | 41 +++++++++++++++++++ 3 files changed, 50 insertions(+), 3 deletions(-) diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/CallerContext.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/CallerContext.java index dbd9184a2b..ba627adc2c 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/CallerContext.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/ipc/CallerContext.java @@ -47,6 +47,7 @@ public final class CallerContext { // field names public static final String CLIENT_IP_STR = "clientIp"; public static final String CLIENT_PORT_STR = "clientPort"; + public static final String REAL_USER_STR = "realUser"; /** The caller context. * diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcClient.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcClient.java index 6c55edde11..c4d408dd00 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcClient.java +++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/main/java/org/apache/hadoop/hdfs/server/federation/router/RouterRpcClient.java @@ -423,7 +423,7 @@ private Object invokeMethod( + router.getRouterId()); } - addClientIpToCallerContext(); + addClientInfoToCallerContext(ugi); Object ret = null; if (rpcMonitor != null) { @@ -541,19 +541,24 @@ private Object invokeMethod( /** * For tracking which is the actual client address. - * It adds trace info "clientIp:ip" and "clientPort:port" + * It adds trace info "clientIp:ip", "clientPort:port" and "realUser:userName" * in the caller context, removing the old values if they were * already present. */ - private void addClientIpToCallerContext() { + private void addClientInfoToCallerContext(UserGroupInformation ugi) { CallerContext ctx = CallerContext.getCurrent(); String origContext = ctx == null ? null : ctx.getContext(); byte[] origSignature = ctx == null ? null : ctx.getSignature(); + String realUser = null; + if (ugi.getRealUser() != null) { + realUser = ugi.getRealUser().getUserName(); + } CallerContext.Builder builder = new CallerContext.Builder("", contextFieldSeparator) .append(CallerContext.CLIENT_IP_STR, Server.getRemoteAddress()) .append(CallerContext.CLIENT_PORT_STR, Integer.toString(Server.getRemotePort())) + .append(CallerContext.REAL_USER_STR, realUser) .setSignature(origSignature); // Append the original caller context if (origContext != null) { diff --git a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterRpc.java b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterRpc.java index ae0908894d..48420ed416 100644 --- a/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterRpc.java +++ b/hadoop-hdfs-project/hadoop-hdfs-rbf/src/test/java/org/apache/hadoop/hdfs/server/federation/router/TestRouterRpc.java @@ -39,6 +39,7 @@ import java.io.IOException; import java.lang.reflect.Method; import java.net.URISyntaxException; +import java.security.PrivilegedExceptionAction; import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; @@ -210,6 +211,14 @@ public static void globalSetUp() throws Exception { cluster.setIndependentDNs(); Configuration conf = new Configuration(); + // Setup proxy users. + conf.set("hadoop.proxyuser.testRealUser.groups", "*"); + conf.set("hadoop.proxyuser.testRealUser.hosts", "*"); + String loginUser = UserGroupInformation.getLoginUser().getUserName(); + conf.set(String.format("hadoop.proxyuser.%s.groups", loginUser), "*"); + conf.set(String.format("hadoop.proxyuser.%s.hosts", loginUser), "*"); + // Enable IP proxy users. + conf.set(DFSConfigKeys.DFS_NAMENODE_IP_PROXY_USERS, "placeholder"); conf.setInt(DFSConfigKeys.DFS_LIST_LIMIT, 5); cluster.addNamenodeOverrides(conf); // Start NNs and DNs and wait until ready @@ -1871,6 +1880,38 @@ public void testMkdirsWithCallerContext() throws IOException { assertTrue(verifyFileExists(routerFS, dirPath)); } + @Test + public void testRealUserPropagationInCallerContext() + throws IOException, InterruptedException { + GenericTestUtils.LogCapturer auditlog = + GenericTestUtils.LogCapturer.captureLogs(FSNamesystem.auditLog); + + // Current callerContext is null + assertNull(CallerContext.getCurrent()); + + UserGroupInformation loginUser = UserGroupInformation.getLoginUser(); + UserGroupInformation realUser = UserGroupInformation + .createUserForTesting("testRealUser", new String[]{"group"}); + UserGroupInformation proxyUser = UserGroupInformation + .createProxyUser("testProxyUser", realUser); + FileSystem proxyFs = proxyUser.doAs( + (PrivilegedExceptionAction) () -> router.getFileSystem()); + proxyFs.listStatus(new Path("/")); + + + final String logOutput = auditlog.getOutput(); + // Login user, which is used as the router's user, is different from the realUser. + assertNotEquals(loginUser.getUserName(), realUser.getUserName()); + // Login user is used in the audit log's ugi field. + assertTrue("The login user is the proxyUser in the UGI field", + logOutput.contains(String.format("ugi=%s (auth:PROXY) via %s (auth:SIMPLE)", + proxyUser.getUserName(), + loginUser.getUserName()))); + // Real user is added to the caller context. + assertTrue("The audit log should contain the real user.", + logOutput.contains(String.format("realUser:%s", realUser.getUserName()))); + } + @Test public void testSetBalancerBandwidth() throws Exception { long defaultBandwidth =