HDFS-3433. GetImageServlet should allow administrative requestors when security is enabled. Contributed by Aaron T. Myers.
git-svn-id: https://svn.apache.org/repos/asf/hadoop/common/trunk@1339540 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
026d0b4d53
commit
1377709b4c
|
@ -96,7 +96,7 @@ public class HttpServer implements FilterContainer {
|
||||||
// The ServletContext attribute where the daemon Configuration
|
// The ServletContext attribute where the daemon Configuration
|
||||||
// gets stored.
|
// gets stored.
|
||||||
public static final String CONF_CONTEXT_ATTRIBUTE = "hadoop.conf";
|
public static final String CONF_CONTEXT_ATTRIBUTE = "hadoop.conf";
|
||||||
static final String ADMINS_ACL = "admins.acl";
|
public static final String ADMINS_ACL = "admins.acl";
|
||||||
public static final String SPNEGO_FILTER = "SpnegoFilter";
|
public static final String SPNEGO_FILTER = "SpnegoFilter";
|
||||||
|
|
||||||
public static final String BIND_ADDRESS = "bind.address";
|
public static final String BIND_ADDRESS = "bind.address";
|
||||||
|
@ -792,7 +792,7 @@ public class HttpServer implements FilterContainer {
|
||||||
*
|
*
|
||||||
* @param servletContext
|
* @param servletContext
|
||||||
* @param request
|
* @param request
|
||||||
* @param response
|
* @param response used to send the error response if user does not have admin access.
|
||||||
* @return true if admin-authorized, false otherwise
|
* @return true if admin-authorized, false otherwise
|
||||||
* @throws IOException
|
* @throws IOException
|
||||||
*/
|
*/
|
||||||
|
@ -814,18 +814,33 @@ public class HttpServer implements FilterContainer {
|
||||||
"authorized to access this page.");
|
"authorized to access this page.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (servletContext.getAttribute(ADMINS_ACL) != null &&
|
||||||
|
!userHasAdministratorAccess(servletContext, remoteUser)) {
|
||||||
|
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "User "
|
||||||
|
+ remoteUser + " is unauthorized to access this page.");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the admin ACLs from the given ServletContext and check if the given
|
||||||
|
* user is in the ACL.
|
||||||
|
*
|
||||||
|
* @param servletContext the context containing the admin ACL.
|
||||||
|
* @param remoteUser the remote user to check for.
|
||||||
|
* @return true if the user is present in the ACL, false if no ACL is set or
|
||||||
|
* the user is not present
|
||||||
|
*/
|
||||||
|
public static boolean userHasAdministratorAccess(ServletContext servletContext,
|
||||||
|
String remoteUser) {
|
||||||
AccessControlList adminsAcl = (AccessControlList) servletContext
|
AccessControlList adminsAcl = (AccessControlList) servletContext
|
||||||
.getAttribute(ADMINS_ACL);
|
.getAttribute(ADMINS_ACL);
|
||||||
UserGroupInformation remoteUserUGI =
|
UserGroupInformation remoteUserUGI =
|
||||||
UserGroupInformation.createRemoteUser(remoteUser);
|
UserGroupInformation.createRemoteUser(remoteUser);
|
||||||
if (adminsAcl != null) {
|
return adminsAcl != null && adminsAcl.isUserAllowed(remoteUserUGI);
|
||||||
if (!adminsAcl.isUserAllowed(remoteUserUGI)) {
|
|
||||||
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "User "
|
|
||||||
+ remoteUser + " is unauthorized to access this page.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -208,6 +208,9 @@ Release 2.0.1-alpha - UNRELEASED
|
||||||
|
|
||||||
HDFS-3422. TestStandbyIsHot timeouts too aggressive (todd)
|
HDFS-3422. TestStandbyIsHot timeouts too aggressive (todd)
|
||||||
|
|
||||||
|
HDFS-3433. GetImageServlet should allow administrative requestors when
|
||||||
|
security is enabled. (atm)
|
||||||
|
|
||||||
Release 2.0.0-alpha - UNRELEASED
|
Release 2.0.0-alpha - UNRELEASED
|
||||||
|
|
||||||
INCOMPATIBLE CHANGES
|
INCOMPATIBLE CHANGES
|
||||||
|
|
|
@ -44,6 +44,7 @@ import org.apache.hadoop.hdfs.server.common.StorageInfo;
|
||||||
import org.apache.hadoop.hdfs.server.protocol.RemoteEditLog;
|
import org.apache.hadoop.hdfs.server.protocol.RemoteEditLog;
|
||||||
import org.apache.hadoop.hdfs.util.DataTransferThrottler;
|
import org.apache.hadoop.hdfs.util.DataTransferThrottler;
|
||||||
import org.apache.hadoop.hdfs.util.MD5FileUtils;
|
import org.apache.hadoop.hdfs.util.MD5FileUtils;
|
||||||
|
import org.apache.hadoop.http.HttpServer;
|
||||||
import org.apache.hadoop.io.MD5Hash;
|
import org.apache.hadoop.io.MD5Hash;
|
||||||
import org.apache.hadoop.security.UserGroupInformation;
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
import org.apache.hadoop.util.StringUtils;
|
import org.apache.hadoop.util.StringUtils;
|
||||||
|
@ -85,11 +86,12 @@ public class GetImageServlet extends HttpServlet {
|
||||||
final Configuration conf =
|
final Configuration conf =
|
||||||
(Configuration)getServletContext().getAttribute(JspHelper.CURRENT_CONF);
|
(Configuration)getServletContext().getAttribute(JspHelper.CURRENT_CONF);
|
||||||
|
|
||||||
if(UserGroupInformation.isSecurityEnabled() &&
|
if (UserGroupInformation.isSecurityEnabled() &&
|
||||||
!isValidRequestor(request.getUserPrincipal().getName(), conf)) {
|
!isValidRequestor(context, request.getUserPrincipal().getName(), conf)) {
|
||||||
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
response.sendError(HttpServletResponse.SC_FORBIDDEN,
|
||||||
"Only Namenode and Secondary Namenode may access this servlet");
|
"Only Namenode, Secondary Namenode, and administrators may access " +
|
||||||
LOG.warn("Received non-NN/SNN request for image or edits from "
|
"this servlet");
|
||||||
|
LOG.warn("Received non-NN/SNN/administrator request for image or edits from "
|
||||||
+ request.getUserPrincipal().getName() + " at " + request.getRemoteHost());
|
+ request.getUserPrincipal().getName() + " at " + request.getRemoteHost());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -209,8 +211,8 @@ public class GetImageServlet extends HttpServlet {
|
||||||
}
|
}
|
||||||
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static boolean isValidRequestor(String remoteUser, Configuration conf)
|
static boolean isValidRequestor(ServletContext context, String remoteUser,
|
||||||
throws IOException {
|
Configuration conf) throws IOException {
|
||||||
if(remoteUser == null) { // This really shouldn't happen...
|
if(remoteUser == null) { // This really shouldn't happen...
|
||||||
LOG.warn("Received null remoteUser while authorizing access to getImage servlet");
|
LOG.warn("Received null remoteUser while authorizing access to getImage servlet");
|
||||||
return false;
|
return false;
|
||||||
|
@ -237,11 +239,17 @@ public class GetImageServlet extends HttpServlet {
|
||||||
|
|
||||||
for(String v : validRequestors) {
|
for(String v : validRequestors) {
|
||||||
if(v != null && v.equals(remoteUser)) {
|
if(v != null && v.equals(remoteUser)) {
|
||||||
if(LOG.isInfoEnabled()) LOG.info("GetImageServlet allowing: " + remoteUser);
|
LOG.info("GetImageServlet allowing checkpointer: " + remoteUser);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(LOG.isInfoEnabled()) LOG.info("GetImageServlet rejecting: " + remoteUser);
|
|
||||||
|
if (HttpServer.userHasAdministratorAccess(context, remoteUser)) {
|
||||||
|
LOG.info("GetImageServlet allowing administrator: " + remoteUser);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG.info("GetImageServlet rejecting: " + remoteUser);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,17 +21,26 @@ import static org.junit.Assert.*;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import javax.servlet.ServletContext;
|
||||||
|
|
||||||
import org.apache.hadoop.conf.Configuration;
|
import org.apache.hadoop.conf.Configuration;
|
||||||
import org.apache.hadoop.hdfs.DFSConfigKeys;
|
import org.apache.hadoop.hdfs.DFSConfigKeys;
|
||||||
import org.apache.hadoop.hdfs.DFSUtil;
|
import org.apache.hadoop.hdfs.DFSUtil;
|
||||||
import org.apache.hadoop.hdfs.HdfsConfiguration;
|
import org.apache.hadoop.hdfs.HdfsConfiguration;
|
||||||
|
import org.apache.hadoop.http.HttpServer;
|
||||||
|
import org.apache.hadoop.security.UserGroupInformation;
|
||||||
|
import org.apache.hadoop.security.authentication.util.KerberosName;
|
||||||
|
import org.apache.hadoop.security.authorize.AccessControlList;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.ArgumentMatcher;
|
||||||
|
import org.mockito.Mockito;
|
||||||
|
|
||||||
public class TestGetImageServlet {
|
public class TestGetImageServlet {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testIsValidRequestorWithHa() throws IOException {
|
public void testIsValidRequestor() throws IOException {
|
||||||
Configuration conf = new HdfsConfiguration();
|
Configuration conf = new HdfsConfiguration();
|
||||||
|
KerberosName.setRules("RULE:[1:$1]\nRULE:[2:$1]");
|
||||||
|
|
||||||
// Set up generic HA configs.
|
// Set up generic HA configs.
|
||||||
conf.set(DFSConfigKeys.DFS_FEDERATION_NAMESERVICES, "ns1");
|
conf.set(DFSConfigKeys.DFS_FEDERATION_NAMESERVICES, "ns1");
|
||||||
|
@ -53,8 +62,33 @@ public class TestGetImageServlet {
|
||||||
// Initialize this conf object as though we're running on NN1.
|
// Initialize this conf object as though we're running on NN1.
|
||||||
NameNode.initializeGenericKeys(conf, "ns1", "nn1");
|
NameNode.initializeGenericKeys(conf, "ns1", "nn1");
|
||||||
|
|
||||||
|
AccessControlList acls = Mockito.mock(AccessControlList.class);
|
||||||
|
Mockito.when(acls.isUserAllowed(Mockito.<UserGroupInformation>any())).thenReturn(false);
|
||||||
|
ServletContext context = Mockito.mock(ServletContext.class);
|
||||||
|
Mockito.when(context.getAttribute(HttpServer.ADMINS_ACL)).thenReturn(acls);
|
||||||
|
|
||||||
// Make sure that NN2 is considered a valid fsimage/edits requestor.
|
// Make sure that NN2 is considered a valid fsimage/edits requestor.
|
||||||
assertTrue(GetImageServlet.isValidRequestor("hdfs/host2@TEST-REALM.COM",
|
assertTrue(GetImageServlet.isValidRequestor(context,
|
||||||
conf));
|
"hdfs/host2@TEST-REALM.COM", conf));
|
||||||
|
|
||||||
|
// Mark atm as an admin.
|
||||||
|
Mockito.when(acls.isUserAllowed(Mockito.argThat(new ArgumentMatcher<UserGroupInformation>() {
|
||||||
|
@Override
|
||||||
|
public boolean matches(Object argument) {
|
||||||
|
return ((UserGroupInformation) argument).getShortUserName().equals("atm");
|
||||||
|
}
|
||||||
|
}))).thenReturn(true);
|
||||||
|
|
||||||
|
// Make sure that NN2 is still considered a valid requestor.
|
||||||
|
assertTrue(GetImageServlet.isValidRequestor(context,
|
||||||
|
"hdfs/host2@TEST-REALM.COM", conf));
|
||||||
|
|
||||||
|
// Make sure an admin is considered a valid requestor.
|
||||||
|
assertTrue(GetImageServlet.isValidRequestor(context,
|
||||||
|
"atm@TEST-REALM.COM", conf));
|
||||||
|
|
||||||
|
// Make sure other users are *not* considered valid requestors.
|
||||||
|
assertFalse(GetImageServlet.isValidRequestor(context,
|
||||||
|
"todd@TEST-REALM.COM", conf));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue