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
This commit is contained in:
parent
c278e40239
commit
868e9b6431
|
@ -42,6 +42,9 @@ Release 2.5.0 - UNRELEASED
|
||||||
HADOOP-10556. Add toLowerCase support to auth_to_local rules
|
HADOOP-10556. Add toLowerCase support to auth_to_local rules
|
||||||
for service name. (tucu)
|
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
|
OPTIMIZATIONS
|
||||||
|
|
||||||
BUG FIXES
|
BUG FIXES
|
||||||
|
|
|
@ -38,13 +38,16 @@ import com.google.common.annotations.VisibleForTesting;
|
||||||
public class ProxyUsers {
|
public class ProxyUsers {
|
||||||
|
|
||||||
private static final String CONF_HOSTS = ".hosts";
|
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_GROUPS = ".groups";
|
||||||
private static final String CONF_HADOOP_PROXYUSER = "hadoop.proxyuser.";
|
private static final String CONF_HADOOP_PROXYUSER = "hadoop.proxyuser.";
|
||||||
private static final String CONF_HADOOP_PROXYUSER_RE = "hadoop\\.proxyuser\\.";
|
private static final String CONF_HADOOP_PROXYUSER_RE = "hadoop\\.proxyuser\\.";
|
||||||
public static final String CONF_HADOOP_PROXYSERVERS = "hadoop.proxyservers";
|
public static final String CONF_HADOOP_PROXYSERVERS = "hadoop.proxyservers";
|
||||||
|
|
||||||
private static boolean init = false;
|
private static boolean init = false;
|
||||||
// list of groups and hosts per proxyuser
|
//list of users, groups and hosts per proxyuser
|
||||||
|
private static Map<String, Collection<String>> proxyUsers =
|
||||||
|
new HashMap<String, Collection<String>>();
|
||||||
private static Map<String, Collection<String>> proxyGroups =
|
private static Map<String, Collection<String>> proxyGroups =
|
||||||
new HashMap<String, Collection<String>>();
|
new HashMap<String, Collection<String>>();
|
||||||
private static Map<String, Collection<String>> proxyHosts =
|
private static Map<String, Collection<String>> proxyHosts =
|
||||||
|
@ -53,7 +56,7 @@ public class ProxyUsers {
|
||||||
new HashSet<String>();
|
new HashSet<String>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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() {
|
public static void refreshSuperUserGroupsConfiguration() {
|
||||||
//load server side configuration;
|
//load server side configuration;
|
||||||
|
@ -69,11 +72,20 @@ public class ProxyUsers {
|
||||||
// remove all existing stuff
|
// remove all existing stuff
|
||||||
proxyGroups.clear();
|
proxyGroups.clear();
|
||||||
proxyHosts.clear();
|
proxyHosts.clear();
|
||||||
|
proxyUsers.clear();
|
||||||
proxyServers.clear();
|
proxyServers.clear();
|
||||||
|
|
||||||
// get all the new keys for groups
|
// get all the new keys for users
|
||||||
String regex = CONF_HADOOP_PROXYUSER_RE+"[^.]*\\"+CONF_GROUPS;
|
String regex = CONF_HADOOP_PROXYUSER_RE+"[^.]*\\"+CONF_USERS;
|
||||||
Map<String,String> allMatchKeys = conf.getValByRegex(regex);
|
Map<String,String> allMatchKeys = conf.getValByRegex(regex);
|
||||||
|
for(Entry<String, String> entry : allMatchKeys.entrySet()) {
|
||||||
|
Collection<String> users = StringUtils.getTrimmedStringCollection(entry.getValue());
|
||||||
|
proxyUsers.put(entry.getKey(), users);
|
||||||
|
}
|
||||||
|
|
||||||
|
// get all the new keys for groups
|
||||||
|
regex = CONF_HADOOP_PROXYUSER_RE+"[^.]*\\"+CONF_GROUPS;
|
||||||
|
allMatchKeys = conf.getValByRegex(regex);
|
||||||
for(Entry<String, String> entry : allMatchKeys.entrySet()) {
|
for(Entry<String, String> entry : allMatchKeys.entrySet()) {
|
||||||
proxyGroups.put(entry.getKey(),
|
proxyGroups.put(entry.getKey(),
|
||||||
StringUtils.getTrimmedStringCollection(entry.getValue()));
|
StringUtils.getTrimmedStringCollection(entry.getValue()));
|
||||||
|
@ -104,6 +116,16 @@ public class ProxyUsers {
|
||||||
return proxyServers.contains(remoteAddr);
|
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
|
* Returns configuration key for effective user groups allowed for a superuser
|
||||||
*
|
*
|
||||||
|
@ -141,28 +163,41 @@ public class ProxyUsers {
|
||||||
if (user.getRealUser() == null) {
|
if (user.getRealUser() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
boolean groupAuthorized = false;
|
boolean userAuthorized = false;
|
||||||
boolean ipAuthorized = false;
|
boolean ipAuthorized = false;
|
||||||
UserGroupInformation superUser = user.getRealUser();
|
UserGroupInformation superUser = user.getRealUser();
|
||||||
|
|
||||||
|
Collection<String> 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 (!userAuthorized) {
|
||||||
Collection<String> allowedUserGroups = proxyGroups.get(
|
Collection<String> allowedUserGroups = proxyGroups.get(
|
||||||
getProxySuperuserGroupConfKey(superUser.getShortUserName()));
|
getProxySuperuserGroupConfKey(superUser.getShortUserName()));
|
||||||
|
|
||||||
if (isWildcardList(allowedUserGroups)) {
|
if (isWildcardList(allowedUserGroups)) {
|
||||||
groupAuthorized = true;
|
userAuthorized = true;
|
||||||
} else if (allowedUserGroups != null && !allowedUserGroups.isEmpty()) {
|
} else if (allowedUserGroups != null && !allowedUserGroups.isEmpty()) {
|
||||||
for (String group : user.getGroupNames()) {
|
for (String group : user.getGroupNames()) {
|
||||||
if (allowedUserGroups.contains(group)) {
|
if (allowedUserGroups.contains(group)) {
|
||||||
groupAuthorized = true;
|
userAuthorized = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!groupAuthorized) {
|
if (!userAuthorized) {
|
||||||
throw new AuthorizationException("User: " + superUser.getUserName()
|
throw new AuthorizationException("User: " + superUser.getUserName()
|
||||||
+ " is not allowed to impersonate " + user.getUserName());
|
+ " is not allowed to impersonate " + user.getUserName());
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Collection<String> ipList = proxyHosts.get(
|
Collection<String> ipList = proxyHosts.get(
|
||||||
getProxySuperuserIpConfKey(superUser.getShortUserName()));
|
getProxySuperuserIpConfKey(superUser.getShortUserName()));
|
||||||
|
@ -183,7 +218,7 @@ public class ProxyUsers {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(!ipAuthorized) {
|
if (!ipAuthorized) {
|
||||||
throw new AuthorizationException("Unauthorized connection for super-user: "
|
throw new AuthorizationException("Unauthorized connection for super-user: "
|
||||||
+ superUser.getUserName() + " from IP " + remoteAddress);
|
+ superUser.getUserName() + " from IP " + remoteAddress);
|
||||||
}
|
}
|
||||||
|
@ -213,6 +248,11 @@ public class ProxyUsers {
|
||||||
(list.contains("*"));
|
(list.contains("*"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@VisibleForTesting
|
||||||
|
public static Map<String, Collection<String>> getProxyUsers() {
|
||||||
|
return proxyUsers;
|
||||||
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
public static Map<String, Collection<String>> getProxyGroups() {
|
public static Map<String, Collection<String>> getProxyGroups() {
|
||||||
return proxyGroups;
|
return proxyGroups;
|
||||||
|
|
|
@ -203,7 +203,9 @@ KVNO Timestamp Principal
|
||||||
Some products such as Apache Oozie which access the services of Hadoop
|
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.
|
on behalf of end users need to be able to impersonate end users.
|
||||||
You can configure proxy user using properties
|
You can configure proxy user using properties
|
||||||
<<<hadoop.proxyuser.${superuser}.hosts>>> and <<<hadoop.proxyuser.${superuser}.groups>>>.
|
<<<hadoop.proxyuser.${superuser}.hosts>>> along with either or both of
|
||||||
|
<<<hadoop.proxyuser.${superuser}.groups>>>
|
||||||
|
and <<<hadoop.proxyuser.${superuser}.users>>>.
|
||||||
|
|
||||||
For example, by specifying as below in core-site.xml,
|
For example, by specifying as below in core-site.xml,
|
||||||
user named <<<oozie>>> accessing from any host
|
user named <<<oozie>>> accessing from any host
|
||||||
|
@ -220,6 +222,20 @@ KVNO Timestamp Principal
|
||||||
</property>
|
</property>
|
||||||
----
|
----
|
||||||
|
|
||||||
|
User named <<<oozie>>> accessing from any host
|
||||||
|
can impersonate user1 and user2 by specifying as below in core-site.xml.
|
||||||
|
|
||||||
|
----
|
||||||
|
<property>
|
||||||
|
<name>hadoop.proxyuser.oozie.hosts</name>
|
||||||
|
<value>*</value>
|
||||||
|
</property>
|
||||||
|
<property>
|
||||||
|
<name>hadoop.proxyuser.oozie.users</name>
|
||||||
|
<value>user1,user2</value>
|
||||||
|
</property>
|
||||||
|
----
|
||||||
|
|
||||||
** Secure DataNode
|
** Secure DataNode
|
||||||
|
|
||||||
Because the data transfer protocol of DataNode
|
Because the data transfer protocol of DataNode
|
||||||
|
|
|
@ -30,6 +30,7 @@ import static org.junit.Assert.*;
|
||||||
public class TestProxyUsers {
|
public class TestProxyUsers {
|
||||||
private static final String REAL_USER_NAME = "proxier";
|
private static final String REAL_USER_NAME = "proxier";
|
||||||
private static final String PROXY_USER_NAME = "proxied_user";
|
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 =
|
private static final String[] GROUP_NAMES =
|
||||||
new String[] { "foo_group" };
|
new String[] { "foo_group" };
|
||||||
private static final String[] OTHER_GROUP_NAMES =
|
private static final String[] OTHER_GROUP_NAMES =
|
||||||
|
@ -70,6 +71,40 @@ public class TestProxyUsers {
|
||||||
assertNotAuthorized(proxyUserUgi, "1.2.3.5");
|
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
|
@Test
|
||||||
public void testWildcardGroup() {
|
public void testWildcardGroup() {
|
||||||
Configuration conf = new Configuration();
|
Configuration conf = new Configuration();
|
||||||
|
@ -104,6 +139,40 @@ public class TestProxyUsers {
|
||||||
assertNotAuthorized(proxyUserUgi, "1.2.3.5");
|
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
|
@Test
|
||||||
public void testWildcardIP() {
|
public void testWildcardIP() {
|
||||||
Configuration conf = new Configuration();
|
Configuration conf = new Configuration();
|
||||||
|
@ -193,7 +262,7 @@ public class TestProxyUsers {
|
||||||
try {
|
try {
|
||||||
ProxyUsers.authorize(proxyUgi, host);
|
ProxyUsers.authorize(proxyUgi, host);
|
||||||
} catch (AuthorizationException e) {
|
} catch (AuthorizationException e) {
|
||||||
fail("Did not allowed authorization of " + proxyUgi + " from " + host);
|
fail("Did not allow authorization of " + proxyUgi + " from " + host);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue