From 1ff694081f28e1aadd955af79ac660bfd1c151e0 Mon Sep 17 00:00:00 2001 From: Arpit Agarwal Date: Thu, 8 May 2014 00:45:32 +0000 Subject: [PATCH] HADOOP-10467. Enable proxyuser specification to support list of users in addition to list of groups. (Contributed bt Benoy Antony) git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1593162 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 60cc46a8731..d2fc6bc4691 100644 --- a/hadoop-common-project/hadoop-common/CHANGES.txt +++ b/hadoop-common-project/hadoop-common/CHANGES.txt @@ -374,6 +374,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 f83560c3bdb..09fc7de7c49 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 @@ -40,13 +40,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 = @@ -55,7 +58,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; @@ -71,11 +74,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()) { Collection groups = StringUtils.getTrimmedStringCollection(entry.getValue()); proxyGroups.put(entry.getKey(), groups ); @@ -108,7 +120,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 * @@ -146,27 +168,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( @@ -188,7 +223,7 @@ public class ProxyUsers { } } } - if(!ipAuthorized) { + if (!ipAuthorized) { throw new AuthorizationException("Unauthorized connection for super-user: " + superUser.getUserName() + " from IP " + remoteAddress); } @@ -217,6 +252,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 be2364af05f..e9df899a0e8 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 @@ -39,6 +39,7 @@ public class TestProxyUsers { LogFactory.getLog(TestProxyUsers.class); 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[] NETGROUP_NAMES = @@ -158,7 +159,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(); @@ -192,6 +227,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() { @@ -282,7 +351,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); } } }