HADOOP-10652. Refactor Proxyusers to use AccessControlList. (Contributed by Benoy Antony)
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1605145 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
c0991d11eb
commit
b880b48b35
@ -451,6 +451,9 @@ Release 2.5.0 - UNRELEASED
|
||||
HADOOP-10665. Make Hadoop Authentication Handler loads case in-sensitive
|
||||
(Benoy Antony via vinayakumarb)
|
||||
|
||||
HADOOP-10652. Refactor Proxyusers to use AccessControlList. (Benoy
|
||||
Antony via Arpit Agarwal)
|
||||
|
||||
OPTIMIZATIONS
|
||||
|
||||
BUG FIXES
|
||||
|
@ -81,29 +81,42 @@ public AccessControlList() {
|
||||
* @param aclString String representation of the ACL
|
||||
*/
|
||||
public AccessControlList(String aclString) {
|
||||
buildACL(aclString);
|
||||
buildACL(aclString.split(" ", 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Construct a new ACL from String representation of users and groups
|
||||
*
|
||||
* The arguments are comma separated lists
|
||||
*
|
||||
* @param users comma separated list of users
|
||||
* @param groups comma separated list of groups
|
||||
*/
|
||||
public AccessControlList(String users, String groups) {
|
||||
buildACL(new String[] {users, groups});
|
||||
}
|
||||
|
||||
/**
|
||||
* Build ACL from the given string, format of the string is
|
||||
* user1,...,userN group1,...,groupN
|
||||
* Build ACL from the given two Strings.
|
||||
* The Strings contain comma separated values.
|
||||
*
|
||||
* @param aclString build ACL from this string
|
||||
* @param aclString build ACL from array of Strings
|
||||
*/
|
||||
private void buildACL(String aclString) {
|
||||
private void buildACL(String[] userGroupStrings) {
|
||||
users = new HashSet<String>();
|
||||
groups = new HashSet<String>();
|
||||
if (isWildCardACLValue(aclString)) {
|
||||
allAllowed = true;
|
||||
} else {
|
||||
allAllowed = false;
|
||||
String[] userGroupStrings = aclString.split(" ", 2);
|
||||
|
||||
if (userGroupStrings.length >= 1) {
|
||||
for (String aclPart : userGroupStrings) {
|
||||
if (aclPart != null && isWildCardACLValue(aclPart)) {
|
||||
allAllowed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!allAllowed) {
|
||||
if (userGroupStrings.length >= 1 && userGroupStrings[0] != null) {
|
||||
users = StringUtils.getTrimmedStringCollection(userGroupStrings[0]);
|
||||
}
|
||||
|
||||
if (userGroupStrings.length == 2) {
|
||||
if (userGroupStrings.length == 2 && userGroupStrings[1] != null) {
|
||||
groups = StringUtils.getTrimmedStringCollection(userGroupStrings[1]);
|
||||
groupsMapping.cacheGroupsAdd(new LinkedList<String>(groups));
|
||||
}
|
||||
@ -294,7 +307,7 @@ public void write(DataOutput out) throws IOException {
|
||||
@Override
|
||||
public void readFields(DataInput in) throws IOException {
|
||||
String aclString = Text.readString(in);
|
||||
buildACL(aclString);
|
||||
buildACL(aclString.split(" ", 2));
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -20,14 +20,13 @@
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.security.Groups;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.util.StringUtils;
|
||||
|
||||
@ -39,11 +38,14 @@ public class DefaultImpersonationProvider implements ImpersonationProvider {
|
||||
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\\.";
|
||||
// list of users, groups and hosts per proxyuser
|
||||
private Map<String, Collection<String>> proxyUsers =
|
||||
new HashMap<String, Collection<String>>();
|
||||
private Map<String, Collection<String>> proxyGroups =
|
||||
new HashMap<String, Collection<String>>();
|
||||
private static final String CONF_HADOOP_PROXYUSER_RE_USERS_GROUPS =
|
||||
CONF_HADOOP_PROXYUSER_RE+"[^.]*(" + Pattern.quote(CONF_USERS) +
|
||||
"|" + Pattern.quote(CONF_GROUPS) + ")";
|
||||
private static final String CONF_HADOOP_PROXYUSER_RE_HOSTS =
|
||||
CONF_HADOOP_PROXYUSER_RE+"[^.]*"+ Pattern.quote(CONF_HOSTS);
|
||||
// acl and list of hosts per proxyuser
|
||||
private Map<String, AccessControlList> proxyUserAcl =
|
||||
new HashMap<String, AccessControlList>();
|
||||
private Map<String, Collection<String>> proxyHosts =
|
||||
new HashMap<String, Collection<String>>();
|
||||
private Configuration conf;
|
||||
@ -52,28 +54,20 @@ public class DefaultImpersonationProvider implements ImpersonationProvider {
|
||||
public void setConf(Configuration conf) {
|
||||
this.conf = conf;
|
||||
|
||||
// get all the new keys for users
|
||||
String regex = CONF_HADOOP_PROXYUSER_RE+"[^.]*\\"+CONF_USERS;
|
||||
Map<String,String> allMatchKeys = conf.getValByRegex(regex);
|
||||
// get list of users and groups per proxyuser
|
||||
Map<String,String> allMatchKeys =
|
||||
conf.getValByRegex(CONF_HADOOP_PROXYUSER_RE_USERS_GROUPS);
|
||||
for(Entry<String, String> entry : allMatchKeys.entrySet()) {
|
||||
Collection<String> users = StringUtils.getTrimmedStringCollection(entry.getValue());
|
||||
proxyUsers.put(entry.getKey(), users);
|
||||
String aclKey = getAclKey(entry.getKey());
|
||||
if (!proxyUserAcl.containsKey(aclKey)) {
|
||||
proxyUserAcl.put(aclKey, new AccessControlList(
|
||||
allMatchKeys.get(aclKey + CONF_USERS) ,
|
||||
allMatchKeys.get(aclKey + CONF_GROUPS)));
|
||||
}
|
||||
}
|
||||
|
||||
// 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()) {
|
||||
Collection<String> groups = StringUtils.getTrimmedStringCollection(entry.getValue());
|
||||
proxyGroups.put(entry.getKey(), groups);
|
||||
//cache the groups. This is needed for NetGroups
|
||||
Groups.getUserToGroupsMappingService(conf).cacheGroupsAdd(
|
||||
new ArrayList<String>(groups));
|
||||
}
|
||||
|
||||
// now hosts
|
||||
regex = CONF_HADOOP_PROXYUSER_RE+"[^.]*\\"+CONF_HOSTS;
|
||||
allMatchKeys = conf.getValByRegex(regex);
|
||||
// get hosts per proxyuser
|
||||
allMatchKeys = conf.getValByRegex(CONF_HADOOP_PROXYUSER_RE_HOSTS);
|
||||
for(Entry<String, String> entry : allMatchKeys.entrySet()) {
|
||||
proxyHosts.put(entry.getKey(),
|
||||
StringUtils.getTrimmedStringCollection(entry.getValue()));
|
||||
@ -88,48 +82,22 @@ public Configuration getConf() {
|
||||
@Override
|
||||
public void authorize(UserGroupInformation user,
|
||||
String remoteAddress) throws AuthorizationException {
|
||||
|
||||
if (user.getRealUser() == null) {
|
||||
|
||||
UserGroupInformation realUser = user.getRealUser();
|
||||
if (realUser == null) {
|
||||
return;
|
||||
}
|
||||
boolean userAuthorized = false;
|
||||
|
||||
AccessControlList acl = proxyUserAcl.get(
|
||||
CONF_HADOOP_PROXYUSER+realUser.getShortUserName());
|
||||
if (acl == null || !acl.isUserAllowed(user)) {
|
||||
throw new AuthorizationException("User: " + realUser.getUserName()
|
||||
+ " is not allowed to impersonate " + user.getUserName());
|
||||
}
|
||||
|
||||
boolean ipAuthorized = false;
|
||||
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(
|
||||
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<String> ipList = proxyHosts.get(
|
||||
getProxySuperuserIpConfKey(superUser.getShortUserName()));
|
||||
getProxySuperuserIpConfKey(realUser.getShortUserName()));
|
||||
|
||||
if (isWildcardList(ipList)) {
|
||||
ipAuthorized = true;
|
||||
@ -149,9 +117,17 @@ public void authorize(UserGroupInformation user,
|
||||
}
|
||||
if(!ipAuthorized) {
|
||||
throw new AuthorizationException("Unauthorized connection for super-user: "
|
||||
+ superUser.getUserName() + " from IP " + remoteAddress);
|
||||
+ realUser.getUserName() + " from IP " + remoteAddress);
|
||||
}
|
||||
}
|
||||
|
||||
private String getAclKey(String key) {
|
||||
int endIndex = key.lastIndexOf(".");
|
||||
if (endIndex != -1) {
|
||||
return key.substring(0, endIndex);
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the configuration specifies the special configuration value
|
||||
@ -193,14 +169,13 @@ public static String getProxySuperuserIpConfKey(String userName) {
|
||||
return CONF_HADOOP_PROXYUSER+userName+CONF_HOSTS;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Map<String, Collection<String>> getProxyUsers() {
|
||||
return proxyUsers;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
public Map<String, Collection<String>> getProxyGroups() {
|
||||
return proxyGroups;
|
||||
Map<String,Collection<String>> proxyGroups = new HashMap<String,Collection<String>>();
|
||||
for(Entry<String, AccessControlList> entry : proxyUserAcl.entrySet()) {
|
||||
proxyGroups.put(entry.getKey() + CONF_GROUPS, entry.getValue().getGroups());
|
||||
}
|
||||
return proxyGroups;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
|
@ -140,7 +140,6 @@ public void testProxyUsers() throws Exception {
|
||||
PROXY_IP);
|
||||
ProxyUsers.refreshSuperUserGroupsConfiguration(conf);
|
||||
|
||||
|
||||
// First try proxying a group that's allowed
|
||||
UserGroupInformation realUserUgi = UserGroupInformation
|
||||
.createRemoteUser(REAL_USER_NAME);
|
||||
@ -362,6 +361,30 @@ public void testProxyUsersWithProviderOverride() throws Exception {
|
||||
// From bad IP
|
||||
assertNotAuthorized(proxyUserUgi, "1.2.3.5");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithProxyGroupsAndUsersWithSpaces() throws Exception {
|
||||
Configuration conf = new Configuration();
|
||||
conf.set(
|
||||
DefaultImpersonationProvider.getProxySuperuserUserConfKey(REAL_USER_NAME),
|
||||
StringUtils.join(",", Arrays.asList(PROXY_USER_NAME + " ",AUTHORIZED_PROXY_USER_NAME, "ONEMORE")));
|
||||
|
||||
conf.set(
|
||||
DefaultImpersonationProvider.getProxySuperuserGroupConfKey(REAL_USER_NAME),
|
||||
StringUtils.join(",", Arrays.asList(GROUP_NAMES)));
|
||||
|
||||
conf.set(
|
||||
DefaultImpersonationProvider.getProxySuperuserIpConfKey(REAL_USER_NAME),
|
||||
PROXY_IP);
|
||||
|
||||
ProxyUsers.refreshSuperUserGroupsConfiguration(conf);
|
||||
|
||||
Collection<String> groupsToBeProxied =
|
||||
ProxyUsers.getDefaultImpersonationProvider().getProxyGroups().get(
|
||||
DefaultImpersonationProvider.getProxySuperuserGroupConfKey(REAL_USER_NAME));
|
||||
|
||||
assertEquals (GROUP_NAMES.length, groupsToBeProxied.size());
|
||||
}
|
||||
|
||||
|
||||
private void assertNotAuthorized(UserGroupInformation proxyUgi, String host) {
|
||||
|
Loading…
Reference in New Issue
Block a user