From 868e9b64319f8a880ad9df4da791d2eb69477852 Mon Sep 17 00:00:00 2001 From: Arpit Agarwal Date: Thu, 8 May 2014 00:46:14 +0000 Subject: [PATCH] HADOOP-10467: Merging r1593162 from trunk to branch-2. git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/branches/branch-2@1593164 13f79535-47bb-0310-9956-ffa450edef68 --- .../hadoop-common/CHANGES.txt | 3 + .../hadoop/security/authorize/ProxyUsers.java | 82 ++++++++++++++----- .../src/site/apt/SecureMode.apt.vm | 18 +++- .../security/authorize/TestProxyUsers.java | 71 +++++++++++++++- 4 files changed, 151 insertions(+), 23 deletions(-) diff --git a/hadoop-common-project/hadoop-common/CHANGES.txt b/hadoop-common-project/hadoop-common/CHANGES.txt index 14f26ce378a..2814b8e209d 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -42,6 +42,9 @@ Release 2.5.0 - UNRELEASED HADOOP-10556. Add toLowerCase support to auth_to_local rules for service name. (tucu) + HADOOP-10467. Enable proxyuser specification to support list of users in + addition to list of groups (Benoy Antony via Arpit Agarwal) + OPTIMIZATIONS BUG FIXES diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/authorize/ProxyUsers.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/authorize/ProxyUsers.java index a448f4a6de1..4d14ece3e64 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/authorize/ProxyUsers.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/authorize/ProxyUsers.java @@ -38,13 +38,16 @@ import com.google.common.annotations.VisibleForTesting; public class ProxyUsers { private static final String CONF_HOSTS = ".hosts"; + private static final String CONF_USERS = ".users"; private static final String CONF_GROUPS = ".groups"; private static final String CONF_HADOOP_PROXYUSER = "hadoop.proxyuser."; private static final String CONF_HADOOP_PROXYUSER_RE = "hadoop\\.proxyuser\\."; public static final String CONF_HADOOP_PROXYSERVERS = "hadoop.proxyservers"; private static boolean init = false; - // list of groups and hosts per proxyuser + //list of users, groups and hosts per proxyuser + private static Map> proxyUsers = + new HashMap>(); private static Map> proxyGroups = new HashMap>(); private static Map> proxyHosts = @@ -53,7 +56,7 @@ public class ProxyUsers { new HashSet(); /** - * reread the conf and get new values for "hadoop.proxyuser.*.groups/hosts" + * reread the conf and get new values for "hadoop.proxyuser.*.groups/users/hosts" */ public static void refreshSuperUserGroupsConfiguration() { //load server side configuration; @@ -69,11 +72,20 @@ public class ProxyUsers { // remove all existing stuff proxyGroups.clear(); proxyHosts.clear(); + proxyUsers.clear(); proxyServers.clear(); + + // get all the new keys for users + String regex = CONF_HADOOP_PROXYUSER_RE+"[^.]*\\"+CONF_USERS; + Map allMatchKeys = conf.getValByRegex(regex); + for(Entry entry : allMatchKeys.entrySet()) { + Collection users = StringUtils.getTrimmedStringCollection(entry.getValue()); + proxyUsers.put(entry.getKey(), users); + } // get all the new keys for groups - String regex = CONF_HADOOP_PROXYUSER_RE+"[^.]*\\"+CONF_GROUPS; - Map allMatchKeys = conf.getValByRegex(regex); + regex = CONF_HADOOP_PROXYUSER_RE+"[^.]*\\"+CONF_GROUPS; + allMatchKeys = conf.getValByRegex(regex); for(Entry entry : allMatchKeys.entrySet()) { proxyGroups.put(entry.getKey(), StringUtils.getTrimmedStringCollection(entry.getValue())); @@ -103,7 +115,17 @@ public class ProxyUsers { } return proxyServers.contains(remoteAddr); } - + + /** + * Returns configuration key for effective users allowed for a superuser + * + * @param userName name of the superuser + * @return configuration key for superuser users + */ + public static String getProxySuperuserUserConfKey(String userName) { + return ProxyUsers.CONF_HADOOP_PROXYUSER+userName+ProxyUsers.CONF_USERS; + } + /** * Returns configuration key for effective user groups allowed for a superuser * @@ -141,27 +163,40 @@ public class ProxyUsers { if (user.getRealUser() == null) { return; } - boolean groupAuthorized = false; + boolean userAuthorized = false; boolean ipAuthorized = false; UserGroupInformation superUser = user.getRealUser(); - - Collection allowedUserGroups = proxyGroups.get( - getProxySuperuserGroupConfKey(superUser.getShortUserName())); - if (isWildcardList(allowedUserGroups)) { - groupAuthorized = true; - } else if (allowedUserGroups != null && !allowedUserGroups.isEmpty()) { - for (String group : user.getGroupNames()) { - if (allowedUserGroups.contains(group)) { - groupAuthorized = true; - break; - } + Collection allowedUsers = proxyUsers.get( + getProxySuperuserUserConfKey(superUser.getShortUserName())); + + if (isWildcardList(allowedUsers)) { + userAuthorized = true; + } else if (allowedUsers != null && !allowedUsers.isEmpty()) { + if (allowedUsers.contains(user.getShortUserName())) { + userAuthorized = true; } } - if (!groupAuthorized) { - throw new AuthorizationException("User: " + superUser.getUserName() - + " is not allowed to impersonate " + user.getUserName()); + if (!userAuthorized) { + Collection allowedUserGroups = proxyGroups.get( + getProxySuperuserGroupConfKey(superUser.getShortUserName())); + + if (isWildcardList(allowedUserGroups)) { + userAuthorized = true; + } else if (allowedUserGroups != null && !allowedUserGroups.isEmpty()) { + for (String group : user.getGroupNames()) { + if (allowedUserGroups.contains(group)) { + userAuthorized = true; + break; + } + } + } + + if (!userAuthorized) { + throw new AuthorizationException("User: " + superUser.getUserName() + + " is not allowed to impersonate " + user.getUserName()); + } } Collection ipList = proxyHosts.get( @@ -183,7 +218,7 @@ public class ProxyUsers { } } } - if(!ipAuthorized) { + if (!ipAuthorized) { throw new AuthorizationException("Unauthorized connection for super-user: " + superUser.getUserName() + " from IP " + remoteAddress); } @@ -212,6 +247,11 @@ public class ProxyUsers { (list.size() == 1) && (list.contains("*")); } + + @VisibleForTesting + public static Map> getProxyUsers() { + return proxyUsers; + } @VisibleForTesting public static Map> getProxyGroups() { diff --git a/hadoop-common-project/hadoop-common/src/site/apt/SecureMode.apt.vm b/hadoop-common-project/hadoop-common/src/site/apt/SecureMode.apt.vm index fb1b262ea8d..99755e5638d 100644 --- a/hadoop-common-project/hadoop-common/src/site/apt/SecureMode.apt.vm +++ b/hadoop-common-project/hadoop-common/src/site/apt/SecureMode.apt.vm @@ -203,7 +203,9 @@ KVNO Timestamp Principal Some products such as Apache Oozie which access the services of Hadoop on behalf of end users need to be able to impersonate end users. You can configure proxy user using properties - <<>> and <<>>. + <<>> along with either or both of + <<>> + and <<>>. For example, by specifying as below in core-site.xml, user named <<>> accessing from any host @@ -220,6 +222,20 @@ KVNO Timestamp Principal ---- + User named <<>> accessing from any host + can impersonate user1 and user2 by specifying as below in core-site.xml. + +---- + + hadoop.proxyuser.oozie.hosts + * + + + hadoop.proxyuser.oozie.users + user1,user2 + +---- + ** Secure DataNode Because the data transfer protocol of DataNode diff --git a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/authorize/TestProxyUsers.java b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/authorize/TestProxyUsers.java index 869465a2228..b57bd49b385 100644 --- a/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/authorize/TestProxyUsers.java +++ b/hadoop-common-project/hadoop-common/src/test/java/org/apache/hadoop/security/authorize/TestProxyUsers.java @@ -30,6 +30,7 @@ import static org.junit.Assert.*; public class TestProxyUsers { private static final String REAL_USER_NAME = "proxier"; private static final String PROXY_USER_NAME = "proxied_user"; + private static final String AUTHORIZED_PROXY_USER_NAME = "authorized_proxied_user"; private static final String[] GROUP_NAMES = new String[] { "foo_group" }; private static final String[] OTHER_GROUP_NAMES = @@ -69,7 +70,41 @@ public class TestProxyUsers { // From bad IP assertNotAuthorized(proxyUserUgi, "1.2.3.5"); } + + @Test + public void testProxyUsersWithUserConf() throws Exception { + Configuration conf = new Configuration(); + conf.set( + ProxyUsers.getProxySuperuserUserConfKey(REAL_USER_NAME), + StringUtils.join(",", Arrays.asList(AUTHORIZED_PROXY_USER_NAME))); + conf.set( + ProxyUsers.getProxySuperuserIpConfKey(REAL_USER_NAME), + PROXY_IP); + ProxyUsers.refreshSuperUserGroupsConfiguration(conf); + + // First try proxying a user that's allowed + UserGroupInformation realUserUgi = UserGroupInformation + .createRemoteUser(REAL_USER_NAME); + UserGroupInformation proxyUserUgi = UserGroupInformation.createProxyUserForTesting( + AUTHORIZED_PROXY_USER_NAME, realUserUgi, GROUP_NAMES); + + // From good IP + assertAuthorized(proxyUserUgi, "1.2.3.4"); + // From bad IP + assertNotAuthorized(proxyUserUgi, "1.2.3.5"); + + // Now try proxying a user that's not allowed + realUserUgi = UserGroupInformation.createRemoteUser(REAL_USER_NAME); + proxyUserUgi = UserGroupInformation.createProxyUserForTesting( + PROXY_USER_NAME, realUserUgi, GROUP_NAMES); + + // From good IP + assertNotAuthorized(proxyUserUgi, "1.2.3.4"); + // From bad IP + assertNotAuthorized(proxyUserUgi, "1.2.3.5"); + } + @Test public void testWildcardGroup() { Configuration conf = new Configuration(); @@ -103,6 +138,40 @@ public class TestProxyUsers { // From bad IP assertNotAuthorized(proxyUserUgi, "1.2.3.5"); } + + @Test + public void testWildcardUser() { + Configuration conf = new Configuration(); + conf.set( + ProxyUsers.getProxySuperuserUserConfKey(REAL_USER_NAME), + "*"); + conf.set( + ProxyUsers.getProxySuperuserIpConfKey(REAL_USER_NAME), + PROXY_IP); + ProxyUsers.refreshSuperUserGroupsConfiguration(conf); + + // First try proxying a user that's allowed + UserGroupInformation realUserUgi = UserGroupInformation + .createRemoteUser(REAL_USER_NAME); + UserGroupInformation proxyUserUgi = UserGroupInformation.createProxyUserForTesting( + AUTHORIZED_PROXY_USER_NAME, realUserUgi, GROUP_NAMES); + + // From good IP + assertAuthorized(proxyUserUgi, "1.2.3.4"); + // From bad IP + assertNotAuthorized(proxyUserUgi, "1.2.3.5"); + + // Now try proxying a different user (just to make sure we aren't getting spill over + // from the other test case!) + realUserUgi = UserGroupInformation.createRemoteUser(REAL_USER_NAME); + proxyUserUgi = UserGroupInformation.createProxyUserForTesting( + PROXY_USER_NAME, realUserUgi, OTHER_GROUP_NAMES); + + // From good IP + assertAuthorized(proxyUserUgi, "1.2.3.4"); + // From bad IP + assertNotAuthorized(proxyUserUgi, "1.2.3.5"); + } @Test public void testWildcardIP() { @@ -193,7 +262,7 @@ public class TestProxyUsers { try { ProxyUsers.authorize(proxyUgi, host); } catch (AuthorizationException e) { - fail("Did not allowed authorization of " + proxyUgi + " from " + host); + fail("Did not allow authorization of " + proxyUgi + " from " + host); } } }