diff --git a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/AuthenticationFilterInitializer.java b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/AuthenticationFilterInitializer.java index 65d2211af53..ca221f5b3dc 100644 --- a/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/AuthenticationFilterInitializer.java +++ b/hadoop-common-project/hadoop-common/src/main/java/org/apache/hadoop/security/AuthenticationFilterInitializer.java @@ -29,9 +29,8 @@ import java.util.Map; /** - * Initializes {@link AuthenticationWithProxyUserFilter} - * which provides support for Kerberos HTTP SPNEGO authentication - * and proxy user authentication. + * Initializes hadoop-auth AuthenticationFilter which provides support for + * Kerberos HTTP SPNEGO authentication. *
* It enables anonymous access, simple/speudo and Kerberos HTTP SPNEGO * authentication for Hadoop JobTracker, NameNode, DataNodes and @@ -59,10 +58,8 @@ public class AuthenticationFilterInitializer extends FilterInitializer { public void initFilter(FilterContainer container, Configuration conf) { Map- * http://www.apache.org/licenses/LICENSE-2.0 - *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.hadoop.security;
-
-import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
-import org.apache.hadoop.security.authorize.AuthorizationException;
-import org.apache.hadoop.security.authorize.ProxyUsers;
-import org.apache.hadoop.util.HttpExceptionUtils;
-import org.apache.http.NameValuePair;
-import org.apache.http.client.utils.URLEncodedUtils;
-
-import javax.servlet.FilterChain;
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletRequestWrapper;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.List;
-
-/**
- * Extend the function of {@link AuthenticationFilter} to
- * support authorizing proxy user. If the query string
- * contains doAs parameter, then check the proxy user,
- * otherwise do the next filter.
- */
-public class AuthenticationWithProxyUserFilter extends AuthenticationFilter {
-
- /**
- * Constant used in URL's query string to perform a proxy user request, the
- * value of the
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-package org.apache.hadoop.http;
-
-import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.fs.CommonConfigurationKeys;
-import org.apache.hadoop.minikdc.MiniKdc;
-import org.apache.hadoop.net.NetUtils;
-import org.apache.hadoop.security.AuthenticationFilterInitializer;
-import org.apache.hadoop.security.SecurityUtil;
-import org.apache.hadoop.security.UserGroupInformation;
-import org.apache.hadoop.security.UserGroupInformation.AuthenticationMethod;
-import org.apache.hadoop.security.authentication.KerberosTestUtils;
-import org.apache.hadoop.security.authentication.client.AuthenticatedURL;
-import org.apache.hadoop.security.authentication.client.AuthenticationException;
-import org.apache.hadoop.security.authentication.server.AuthenticationFilter;
-import org.apache.hadoop.security.authentication.server.AuthenticationToken;
-import org.apache.hadoop.security.authentication.util.Signer;
-import org.apache.hadoop.security.authentication.util.SignerSecretProvider;
-import org.apache.hadoop.security.authentication.util.StringSignerSecretProviderCreator;
-import org.apache.hadoop.security.authorize.AccessControlList;
-import org.apache.hadoop.security.authorize.ProxyUsers;
-import org.ietf.jgss.GSSException;
-import org.junit.AfterClass;
-import org.junit.BeforeClass;
-import org.junit.Test;
-import org.junit.Assert;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import java.io.File;
-import java.io.FileWriter;
-import java.io.Writer;
-import java.net.HttpURLConnection;
-import java.net.URI;
-import java.net.URL;
-import java.security.AccessController;
-import java.security.PrivilegedExceptionAction;
-import java.util.HashSet;
-import java.util.Properties;
-import java.util.Set;
-import javax.security.auth.Subject;
-import javax.servlet.ServletContext;
-
-import static org.junit.Assert.assertTrue;
-
-/**
- * This class is tested for http server with SPENGO authentication.
- */
-public class TestHttpServerWithSpengo {
-
- static final Logger LOG =
- LoggerFactory.getLogger(TestHttpServerWithSpengo.class);
-
- private static final String SECRET_STR = "secret";
- private static final String HTTP_USER = "HTTP";
- private static final String PREFIX = "hadoop.http.authentication.";
- private static final long TIMEOUT = 20000;
-
- private static File httpSpnegoKeytabFile = new File(
- KerberosTestUtils.getKeytabFile());
- private static String httpSpnegoPrincipal =
- KerberosTestUtils.getServerPrincipal();
- private static String realm = KerberosTestUtils.getRealm();
-
- private static File testRootDir = new File("target",
- TestHttpServerWithSpengo.class.getName() + "-root");
- private static MiniKdc testMiniKDC;
- private static File secretFile = new File(testRootDir, SECRET_STR);
-
- private static UserGroupInformation authUgi;
-
- @BeforeClass
- public static void setUp() throws Exception {
- try {
- testMiniKDC = new MiniKdc(MiniKdc.createConf(), testRootDir);
- testMiniKDC.start();
- testMiniKDC.createPrincipal(
- httpSpnegoKeytabFile, HTTP_USER + "/localhost", "keytab-user");
- } catch (Exception e) {
- assertTrue("Couldn't setup MiniKDC", false);
- }
-
- System.setProperty("sun.security.krb5.debug", "true");
- Configuration conf = new Configuration();
- SecurityUtil.setAuthenticationMethod(AuthenticationMethod.KERBEROS, conf);
- UserGroupInformation.setConfiguration(conf);
- authUgi = UserGroupInformation.loginUserFromKeytabAndReturnUGI(
- "keytab-user", httpSpnegoKeytabFile.toString());
- Writer w = new FileWriter(secretFile);
- w.write("secret");
- w.close();
- }
-
- @AfterClass
- public static void tearDown() {
- if (testMiniKDC != null) {
- testMiniKDC.stop();
- }
- }
-
- /**
- * groupA
- * - userA
- * groupB
- * - userA, userB
- * groupC
- * - userC
- * SPNEGO filter has been enabled.
- * userA has the privilege to impersonate users in groupB.
- * userA has admin access to all default servlets, but userB
- * and userC don't have. So "/logs" can only be accessed by userA.
- * @throws Exception
- */
- @Test
- public void testAuthenticationWithProxyUser() throws Exception {
-
- Configuration spengoConf = getSpengoConf(new Configuration());
-
- //setup logs dir
- System.setProperty("hadoop.log.dir", testRootDir.getAbsolutePath());
-
- // Setup user group
- UserGroupInformation.createUserForTesting("userA",
- new String[]{"groupA", "groupB"});
- UserGroupInformation.createUserForTesting("userB",
- new String[]{"groupB"});
- UserGroupInformation.createUserForTesting("userC",
- new String[]{"groupC"});
-
- // Make userA impersonate users in groupB
- spengoConf.set("hadoop.proxyuser.userA.hosts", "*");
- spengoConf.set("hadoop.proxyuser.userA.groups", "groupB");
- ProxyUsers.refreshSuperUserGroupsConfiguration(spengoConf);
-
- HttpServer2 httpServer = null;
- try {
- // Create http server to test.
- httpServer = getCommonBuilder()
- .setConf(spengoConf)
- .setACL(new AccessControlList("userA groupA"))
- .build();
- httpServer.start();
-
- // Get signer to encrypt token
- Signer signer = getSignerToEncrypt();
-
- // setup auth token for userA
- AuthenticatedURL.Token token = getEncryptedAuthToken(signer, "userA");
-
- String serverURL = "http://" +
- NetUtils.getHostPortString(httpServer.getConnectorAddress(0)) + "/";
-
- // The default authenticator is kerberos.
- AuthenticatedURL authUrl = new AuthenticatedURL();
-
- // userA impersonates userB, it's allowed.
- for (String servlet :
- new String[]{"stacks", "jmx", "conf"}) {
- HttpURLConnection conn = authUrl
- .openConnection(new URL(serverURL + servlet + "?doAs=userB"),
- token);
- Assert.assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
- }
-
- // userA cannot impersonate userC, it fails.
- for (String servlet :
- new String[]{"stacks", "jmx", "conf"}){
- HttpURLConnection conn = authUrl
- .openConnection(new URL(serverURL + servlet + "?doAs=userC"),
- token);
- Assert.assertEquals(HttpURLConnection.HTTP_FORBIDDEN,
- conn.getResponseCode());
- }
-
- // "/logs" and "/logLevel" require admin authorization,
- // only userA has the access.
- for (String servlet :
- new String[]{"logLevel", "logs"}) {
- HttpURLConnection conn = authUrl
- .openConnection(new URL(serverURL + servlet), token);
- Assert.assertEquals(HttpURLConnection.HTTP_OK, conn.getResponseCode());
- }
-
- // Setup token for userB
- token = getEncryptedAuthToken(signer, "userB");
-
- // userB cannot access these servlets.
- for (String servlet :
- new String[]{"logLevel", "logs"}) {
- HttpURLConnection conn = authUrl
- .openConnection(new URL(serverURL + servlet), token);
- Assert.assertEquals(HttpURLConnection.HTTP_FORBIDDEN,
- conn.getResponseCode());
- }
-
- } finally {
- if (httpServer != null) {
- httpServer.stop();
- }
- }
- }
-
- @Test
- public void testSessionCookie() throws Exception {
- Configuration conf = new Configuration();
- conf.set(HttpServer2.FILTER_INITIALIZER_PROPERTY,
- AuthenticationFilterInitializer.class.getName());
- conf.set(PREFIX + "type", "kerberos");
- conf.setBoolean(PREFIX + "simple.anonymous.allowed", false);
- conf.set(PREFIX + "signer.secret.provider",
- TestSignerSecretProvider.class.getName());
-
- conf.set(PREFIX + "kerberos.keytab",
- httpSpnegoKeytabFile.getAbsolutePath());
- conf.set(PREFIX + "kerberos.principal", httpSpnegoPrincipal);
- conf.set(PREFIX + "cookie.domain", realm);
- conf.setBoolean(CommonConfigurationKeys.HADOOP_SECURITY_AUTHORIZATION,
- true);
-
- //setup logs dir
- System.setProperty("hadoop.log.dir", testRootDir.getAbsolutePath());
-
- HttpServer2 httpServer = null;
- // Create http server to test.
- httpServer = getCommonBuilder()
- .setConf(conf)
- .build();
- httpServer.start();
-
- // Get signer to encrypt token
- final Signer signer = new Signer(new TestSignerSecretProvider());
- final AuthenticatedURL authUrl = new AuthenticatedURL();
-
- final URL url = new URL("http://" + NetUtils.getHostPortString(
- httpServer.getConnectorAddress(0)) + "/conf");
-
- // this illustrates an inconsistency with AuthenticatedURL. the
- // authenticator is only called when the token is not set. if the
- // authenticator fails then it must throw an AuthenticationException to
- // the caller, yet the caller may see 401 for subsequent requests
- // that require re-authentication like token expiration.
- final UserGroupInformation simpleUgi =
- UserGroupInformation.createRemoteUser("simple-user");
-
- authUgi.doAs(new PrivilegedExceptionActionDO_AS
parameter is the user the request will be
- * done on behalf of.
- */
- private static final String DO_AS = "doAs";
-
- private static final Charset UTF8_CHARSET = Charset.forName("UTF-8");
-
-
- /**
- * This method provide the ability to do pre/post tasks
- * in filter chain. Override this method to authorize
- * proxy user between AuthenticationFilter and next filter.
- * @param filterChain the filter chain object.
- * @param request the request object.
- * @param response the response object.
- *
- * @throws IOException
- * @throws ServletException
- */
- @Override
- protected void doFilter(FilterChain filterChain, HttpServletRequest request,
- HttpServletResponse response) throws IOException, ServletException {
-
- // authorize proxy user before calling next filter.
- String proxyUser = getDoAs(request);
- if (proxyUser != null) {
- UserGroupInformation realUser =
- UserGroupInformation.createRemoteUser(request.getRemoteUser());
- UserGroupInformation proxyUserInfo =
- UserGroupInformation.createProxyUser(proxyUser, realUser);
-
- try {
- ProxyUsers.authorize(proxyUserInfo, request.getRemoteAddr());
- } catch (AuthorizationException ex) {
- HttpExceptionUtils.createServletExceptionResponse(response,
- HttpServletResponse.SC_FORBIDDEN, ex);
- // stop filter chain if there is an Authorization Exception.
- return;
- }
-
- final UserGroupInformation finalProxyUser = proxyUserInfo;
- // Change the remote user after proxy user is authorized.
- request = new HttpServletRequestWrapper(request) {
- @Override
- public String getRemoteUser() {
- return finalProxyUser.getUserName();
- }
- };
-
- }
- filterChain.doFilter(request, response);
- }
-
- /**
- * Get proxy user from query string.
- * @param request the request object
- * @return proxy user
- */
- public static String getDoAs(HttpServletRequest request) {
- String queryString = request.getQueryString();
- if (queryString == null) {
- return null;
- }
- List