HADOOP-6791. Refresh for proxy superuser config
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@951081 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
da60eca889
commit
9e2c3bf9ed
@ -3,6 +3,9 @@ Hadoop Change Log
|
||||
Trunk (unreleased changes)
|
||||
|
||||
NEW FEATURES
|
||||
HADOOP-6791. Refresh for proxy superuser config
|
||||
(common part for HDFS-1096) (boryas)
|
||||
|
||||
HADOOP-6581. Add authenticated TokenIdentifiers to UGI so that
|
||||
they can be used for authorization (Kan Zhang and Jitendra Pandey
|
||||
via jghoman)
|
||||
|
@ -1718,6 +1718,29 @@ public void write(DataOutput out) throws IOException {
|
||||
org.apache.hadoop.io.Text.writeString(out, (String) item.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* get keys matching the the regex
|
||||
* @param regex
|
||||
* @return Map<String,String> with matching keys
|
||||
*/
|
||||
public Map<String,String> getValByRegex(String regex) {
|
||||
Pattern p = Pattern.compile(regex);
|
||||
|
||||
Map<String,String> result = new HashMap<String,String>();
|
||||
Matcher m;
|
||||
|
||||
for(Map.Entry<Object,Object> item: getProps().entrySet()) {
|
||||
if (item.getKey() instanceof String &&
|
||||
item.getValue() instanceof String) {
|
||||
m = p.matcher((String)item.getKey());
|
||||
if(m.find()) { // match
|
||||
result.put((String) item.getKey(), (String) item.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//Load deprecated keys in common
|
||||
private static void addDeprecatedKeys() {
|
||||
|
@ -30,7 +30,7 @@
|
||||
*/
|
||||
@KerberosInfo(
|
||||
serverPrincipal=CommonConfigurationKeys.HADOOP_SECURITY_SERVICE_USER_NAME_KEY)
|
||||
public interface RefreshUserToGroupMappingsProtocol extends VersionedProtocol {
|
||||
public interface RefreshUserMappingsProtocol extends VersionedProtocol {
|
||||
|
||||
/**
|
||||
* Version 1: Initial version.
|
||||
@ -43,4 +43,12 @@ public interface RefreshUserToGroupMappingsProtocol extends VersionedProtocol {
|
||||
* @throws IOException
|
||||
*/
|
||||
public void refreshUserToGroupsMappings(Configuration conf) throws IOException;
|
||||
|
||||
/**
|
||||
* Refresh superuser proxy group list
|
||||
* @param conf
|
||||
* @throws IOException
|
||||
*/
|
||||
public void refreshSuperUserGroupsConfiguration(Configuration conf)
|
||||
throws IOException;
|
||||
}
|
@ -21,54 +21,101 @@
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.classification.InterfaceAudience;
|
||||
import org.apache.hadoop.util.StringUtils;
|
||||
|
||||
@InterfaceAudience.Private
|
||||
public class ProxyUsers {
|
||||
|
||||
/*
|
||||
private static final String CONF_HOSTS = ".hosts";
|
||||
public static final String CONF_GROUPS = ".groups";
|
||||
public static final String CONF_HADOOP_PROXYUSER = "hadoop.proxyuser.";
|
||||
public static final String CONF_HADOOP_PROXYUSER_RE = "hadoop\\.proxyuser\\.";
|
||||
private static Configuration conf=null;
|
||||
// list of groups and hosts per proxyuser
|
||||
private static Map<String, Collection<String>> proxyGroups =
|
||||
new HashMap<String, Collection<String>>();
|
||||
private static Map<String, Collection<String>> proxyHosts =
|
||||
new HashMap<String, Collection<String>>();
|
||||
|
||||
/**
|
||||
* reread the conf and get new values for "hadoop.proxyuser.*.groups/hosts"
|
||||
*/
|
||||
public static synchronized void refreshSuperUserGroupsConfiguration(Configuration cn) {
|
||||
conf = cn;
|
||||
|
||||
// remove alle existing stuff
|
||||
proxyGroups.clear();
|
||||
proxyHosts.clear();
|
||||
|
||||
// get all the new keys for groups
|
||||
String regex = CONF_HADOOP_PROXYUSER_RE+"[^.]*\\"+CONF_GROUPS;
|
||||
Map<String,String> allMatchKeys = conf.getValByRegex(regex);
|
||||
for(Entry<String, String> entry : allMatchKeys.entrySet()) {
|
||||
proxyGroups.put(entry.getKey(),
|
||||
StringUtils.getStringCollection(entry.getValue()));
|
||||
}
|
||||
|
||||
// now hosts
|
||||
regex = CONF_HADOOP_PROXYUSER_RE+"[^.]*\\"+CONF_HOSTS;
|
||||
allMatchKeys = conf.getValByRegex(regex);
|
||||
for(Entry<String, String> entry : allMatchKeys.entrySet()) {
|
||||
proxyHosts.put(entry.getKey(),
|
||||
StringUtils.getStringCollection(entry.getValue()));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns configuration key for effective user groups allowed for a superuser
|
||||
*
|
||||
* @param userName name of the superuser
|
||||
* @return configuration key for superuser groups
|
||||
*/
|
||||
public static String getProxySuperuserGroupConfKey(String userName) {
|
||||
return "hadoop.proxyuser."+userName+".groups";
|
||||
return ProxyUsers.CONF_HADOOP_PROXYUSER+userName+ProxyUsers.CONF_GROUPS;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Return configuration key for superuser ip addresses
|
||||
*
|
||||
* @param userName name of the superuser
|
||||
* @return configuration key for superuser ip-addresses
|
||||
*/
|
||||
public static String getProxySuperuserIpConfKey(String userName) {
|
||||
return "hadoop.proxyuser."+userName+".hosts";
|
||||
return ProxyUsers.CONF_HADOOP_PROXYUSER+userName+ProxyUsers.CONF_HOSTS;
|
||||
}
|
||||
|
||||
/*
|
||||
/**
|
||||
* Authorize the superuser which is doing doAs
|
||||
*
|
||||
* @param user ugi of the effective or proxy user which contains a real user
|
||||
* @param remoteAddress the ip address of client
|
||||
* @param conf configuration
|
||||
* @param newConf configuration
|
||||
* @throws AuthorizationException
|
||||
*/
|
||||
public static void authorize(UserGroupInformation user, String remoteAddress,
|
||||
Configuration conf) throws AuthorizationException {
|
||||
public static synchronized void authorize(UserGroupInformation user,
|
||||
String remoteAddress, Configuration newConf) throws AuthorizationException {
|
||||
|
||||
if(conf == null) {
|
||||
refreshSuperUserGroupsConfiguration(newConf);
|
||||
}
|
||||
|
||||
if (user.getRealUser() == null) {
|
||||
return;
|
||||
}
|
||||
boolean groupAuthorized = false;
|
||||
boolean ipAuthorized = false;
|
||||
UserGroupInformation superUser = user.getRealUser();
|
||||
|
||||
Collection<String> allowedUserGroups = conf
|
||||
.getStringCollection(getProxySuperuserGroupConfKey(superUser
|
||||
.getShortUserName()));
|
||||
Collection<String> allowedUserGroups = proxyGroups.get(
|
||||
getProxySuperuserGroupConfKey(superUser.getShortUserName()));
|
||||
|
||||
if (!allowedUserGroups.isEmpty()) {
|
||||
for (String group : user.getGroupNames()) {
|
||||
if (allowedUserGroups.contains(group)) {
|
||||
@ -83,9 +130,9 @@ public static void authorize(UserGroupInformation user, String remoteAddress,
|
||||
+ " is not allowed to impersonate " + user.getUserName());
|
||||
}
|
||||
|
||||
Collection<String> ipList = conf
|
||||
.getStringCollection(getProxySuperuserIpConfKey(superUser
|
||||
.getShortUserName()));
|
||||
Collection<String> ipList = proxyHosts.get(
|
||||
getProxySuperuserIpConfKey(superUser.getShortUserName()));
|
||||
|
||||
if (!ipList.isEmpty()) {
|
||||
for (String allowedHost : ipList) {
|
||||
InetAddress hostAddr;
|
||||
@ -96,11 +143,13 @@ public static void authorize(UserGroupInformation user, String remoteAddress,
|
||||
}
|
||||
if (hostAddr.getHostAddress().equals(remoteAddress)) {
|
||||
// Authorization is successful
|
||||
return;
|
||||
ipAuthorized = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new AuthorizationException("Unauthorized connection for super-user: "
|
||||
+ superUser.getUserName() + " from IP " + remoteAddress);
|
||||
if(!ipAuthorized) {
|
||||
throw new AuthorizationException("Unauthorized connection for super-user: "
|
||||
+ superUser.getUserName() + " from IP " + remoteAddress);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,14 +24,14 @@
|
||||
import java.io.StringWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
import org.apache.hadoop.fs.Path;
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
|
||||
import org.codehaus.jackson.map.ObjectMapper;
|
||||
|
||||
public class TestConfiguration extends TestCase {
|
||||
|
||||
@ -636,6 +636,25 @@ public void testDumpConfiguratioWithoutDefaults() throws IOException {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void testGetValByRegex() {
|
||||
Configuration conf = new Configuration();
|
||||
String key1 = "t.abc.key1";
|
||||
String key2 = "t.abc.key2";
|
||||
String key3 = "tt.abc.key3";
|
||||
String key4 = "t.abc.ey3";
|
||||
conf.set(key1, "value1");
|
||||
conf.set(key2, "value2");
|
||||
conf.set(key3, "value3");
|
||||
conf.set(key4, "value3");
|
||||
|
||||
Map<String,String> res = conf.getValByRegex("^t\\..*\\.key\\d");
|
||||
assertTrue("Conf didn't get key " + key1, res.containsKey(key1));
|
||||
assertTrue("Conf didn't get key " + key2, res.containsKey(key2));
|
||||
assertTrue("Picked out wrong key " + key3, !res.containsKey(key3));
|
||||
assertTrue("Picked out wrong key " + key4, !res.containsKey(key4));
|
||||
}
|
||||
|
||||
public static void main(String[] argv) throws Exception {
|
||||
junit.textui.TestRunner.main(new String[]{
|
||||
TestConfiguration.class.getName()
|
||||
|
@ -222,6 +222,8 @@ public void testRealUserIPAuthorizationFailure() throws IOException {
|
||||
Server server = RPC.getServer(TestProtocol.class, new TestImpl(), ADDRESS,
|
||||
0, 2, false, conf, null);
|
||||
|
||||
refreshConf(conf);
|
||||
|
||||
try {
|
||||
server.start();
|
||||
|
||||
@ -339,6 +341,7 @@ public void testRealUserGroupAuthorizationFailure() throws IOException {
|
||||
Server server = RPC.getServer(TestProtocol.class, new TestImpl(), ADDRESS,
|
||||
0, 2, false, conf, null);
|
||||
|
||||
|
||||
try {
|
||||
server.start();
|
||||
|
||||
@ -388,7 +391,8 @@ public void testProxyWithToken() throws Exception {
|
||||
server.start();
|
||||
|
||||
final UserGroupInformation current = UserGroupInformation
|
||||
.createRemoteUser(REAL_USER_NAME);
|
||||
.createRemoteUser(REAL_USER_NAME);
|
||||
|
||||
final InetSocketAddress addr = NetUtils.getConnectAddress(server);
|
||||
TestTokenIdentifier tokenId = new TestTokenIdentifier(new Text(current
|
||||
.getUserName()), new Text("SomeSuperUser"));
|
||||
@ -400,6 +404,9 @@ public void testProxyWithToken() throws Exception {
|
||||
UserGroupInformation proxyUserUgi = UserGroupInformation
|
||||
.createProxyUserForTesting(PROXY_USER_NAME, current, GROUP_NAMES);
|
||||
proxyUserUgi.addToken(token);
|
||||
|
||||
refreshConf(conf);
|
||||
|
||||
String retVal = proxyUserUgi.doAs(new PrivilegedExceptionAction<String>() {
|
||||
@Override
|
||||
public String run() throws Exception {
|
||||
@ -441,6 +448,9 @@ public void testTokenBySuperUser() throws Exception {
|
||||
|
||||
final UserGroupInformation current = UserGroupInformation
|
||||
.createUserForTesting(REAL_USER_NAME, GROUP_NAMES);
|
||||
|
||||
refreshConf(newConf);
|
||||
|
||||
final InetSocketAddress addr = NetUtils.getConnectAddress(server);
|
||||
TestTokenIdentifier tokenId = new TestTokenIdentifier(new Text(current
|
||||
.getUserName()), new Text("SomeSuperUser"));
|
||||
@ -469,6 +479,12 @@ public String run() throws Exception {
|
||||
}
|
||||
}
|
||||
});
|
||||
Assert.assertEquals(REAL_USER_NAME + " via SomeSuperUser", retVal);
|
||||
String expected = REAL_USER_NAME + " via SomeSuperUser";
|
||||
Assert.assertEquals(retVal + "!=" + expected, expected, retVal);
|
||||
}
|
||||
|
||||
//
|
||||
private void refreshConf(Configuration conf) throws IOException {
|
||||
ProxyUsers.refreshSuperUserGroupsConfiguration(conf);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user