HADOOP-16287. Implement ProxyUserAuthenticationFilter for web protocol impersonation.
Contributed by Prabhu Joseph
This commit is contained in:
parent
a771e2a638
commit
ea0b1d8fba
@ -160,6 +160,16 @@
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.glassfish.grizzly</groupId>
|
||||
<artifactId>grizzly-http-servlet</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
|
@ -0,0 +1,115 @@
|
||||
/**
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* 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. See accompanying LICENSE file.
|
||||
*/
|
||||
package org.apache.hadoop.security.authentication.server;
|
||||
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.security.authorize.AuthorizationException;
|
||||
import org.apache.hadoop.security.authorize.ProxyUsers;
|
||||
import org.apache.hadoop.security.UserGroupInformation;
|
||||
import org.apache.hadoop.util.HttpExceptionUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.Principal;
|
||||
import java.util.Enumeration;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletRequestWrapper;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* AuthenticationFilter which adds support to perform operations
|
||||
* using end user instead of proxy user. Fetches the end user from
|
||||
* doAs Query Parameter.
|
||||
*/
|
||||
public class ProxyUserAuthenticationFilter extends AuthenticationFilter {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(
|
||||
ProxyUserAuthenticationFilter.class);
|
||||
|
||||
private static final String DO_AS = "doAs";
|
||||
public static final String PROXYUSER_PREFIX = "proxyuser";
|
||||
|
||||
@Override
|
||||
public void init(FilterConfig filterConfig) throws ServletException {
|
||||
Configuration conf = getProxyuserConfiguration(filterConfig);
|
||||
ProxyUsers.refreshSuperUserGroupsConfiguration(conf, PROXYUSER_PREFIX);
|
||||
super.init(filterConfig);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doFilter(FilterChain filterChain, HttpServletRequest request,
|
||||
HttpServletResponse response) throws IOException, ServletException {
|
||||
|
||||
String doAsUser = request.getParameter(DO_AS);
|
||||
if (doAsUser != null && !doAsUser.equals(request.getRemoteUser())) {
|
||||
LOG.debug("doAsUser = {}, RemoteUser = {} , RemoteAddress = {} ",
|
||||
doAsUser, request.getRemoteUser(), request.getRemoteAddr());
|
||||
UserGroupInformation requestUgi = (request.getUserPrincipal() != null) ?
|
||||
UserGroupInformation.createRemoteUser(request.getRemoteUser())
|
||||
: null;
|
||||
if (requestUgi != null) {
|
||||
requestUgi = UserGroupInformation.createProxyUser(doAsUser,
|
||||
requestUgi);
|
||||
try {
|
||||
ProxyUsers.authorize(requestUgi, request.getRemoteAddr());
|
||||
|
||||
final UserGroupInformation ugiF = requestUgi;
|
||||
request = new HttpServletRequestWrapper(request) {
|
||||
@Override
|
||||
public String getRemoteUser() {
|
||||
return ugiF.getShortUserName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Principal getUserPrincipal() {
|
||||
return new Principal() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return ugiF.getUserName();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
LOG.debug("Proxy user Authentication successful");
|
||||
} catch (AuthorizationException ex) {
|
||||
HttpExceptionUtils.createServletExceptionResponse(response,
|
||||
HttpServletResponse.SC_FORBIDDEN, ex);
|
||||
LOG.warn("Proxy user Authentication exception", ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
super.doFilter(filterChain, request, response);
|
||||
}
|
||||
|
||||
protected Configuration getProxyuserConfiguration(FilterConfig filterConfig)
|
||||
throws ServletException {
|
||||
Configuration conf = new Configuration(false);
|
||||
Enumeration<?> names = filterConfig.getInitParameterNames();
|
||||
while (names.hasMoreElements()) {
|
||||
String name = (String) names.nextElement();
|
||||
if (name.startsWith(PROXYUSER_PREFIX + ".")) {
|
||||
String value = filterConfig.getInitParameter(name);
|
||||
conf.set(name, value);
|
||||
}
|
||||
}
|
||||
return conf;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,60 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* 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.authentication.server;
|
||||
|
||||
import java.util.Map;
|
||||
import org.apache.hadoop.conf.Configuration;
|
||||
import org.apache.hadoop.http.FilterContainer;
|
||||
import org.apache.hadoop.http.FilterInitializer;
|
||||
import org.apache.hadoop.security.AuthenticationFilterInitializer;
|
||||
import org.apache.hadoop.security.authorize.ProxyUsers;
|
||||
|
||||
/**
|
||||
* Filter initializer to initialize
|
||||
* {@link ProxyUserAuthenticationFilter} which adds support
|
||||
* to perform operations using end user instead of proxy user.
|
||||
*/
|
||||
public class ProxyUserAuthenticationFilterInitializer
|
||||
extends FilterInitializer {
|
||||
|
||||
private String configPrefix;
|
||||
|
||||
public ProxyUserAuthenticationFilterInitializer() {
|
||||
this.configPrefix = "hadoop.http.authentication.";
|
||||
}
|
||||
|
||||
protected Map<String, String> createFilterConfig(Configuration conf) {
|
||||
Map<String, String> filterConfig = AuthenticationFilterInitializer
|
||||
.getFilterConfigMap(conf, configPrefix);
|
||||
//Add proxy user configs
|
||||
for (Map.Entry<String, String> entry : conf.getPropsWithPrefix(
|
||||
ProxyUsers.CONF_HADOOP_PROXYUSER).entrySet()) {
|
||||
filterConfig.put("proxyuser" + entry.getKey(), entry.getValue());
|
||||
}
|
||||
return filterConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initFilter(FilterContainer container, Configuration conf) {
|
||||
Map<String, String> filterConfig = createFilterConfig(conf);
|
||||
container.addFilter("ProxyUserAuthenticationFilter",
|
||||
ProxyUserAuthenticationFilter.class.getName(), filterConfig);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Provides the server-side framework for authentication.
|
||||
*/
|
||||
package org.apache.hadoop.security.authentication.server;
|
@ -64,3 +64,11 @@ Add org.apache.hadoop.security.HttpCrossOriginFilterInitializer to hadoop.http.f
|
||||
| hadoop.http.cross-origin.allowed-methods | `GET,POST,HEAD` | Comma separated list of methods that are allowed |
|
||||
| hadoop.http.cross-origin.allowed-headers | `X-Requested-With,Content-Type,Accept,Origin` | Comma separated list of headers that are allowed |
|
||||
| hadoop.http.cross-origin.max-age | `1800` | Number of seconds a pre-flighted request can be cached |
|
||||
|
||||
|
||||
Trusted Proxy
|
||||
-------------
|
||||
Trusted Proxy adds support to perform operations using end user instead of proxy user. It fetches the end user from
|
||||
doAs query parameter. To enable Trusted Proxy, please set the following configuration parameter:
|
||||
|
||||
Add org.apache.hadoop.security.authentication.server.ProxyUserAuthenticationFilterInitializer to hadoop.http.filter.initializers at the end in core-site.xml.
|
||||
|
@ -0,0 +1,125 @@
|
||||
/**
|
||||
* Licensed to the Apache Software Foundation (ASF) under one
|
||||
* or more contributor license agreements. See the NOTICE file
|
||||
* distributed with this work for additional information
|
||||
* regarding copyright ownership. The ASF licenses this file
|
||||
* to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance
|
||||
* with the License. You may obtain a copy of the License at
|
||||
*
|
||||
* 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.authentication.server;
|
||||
|
||||
import java.security.Principal;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.servlet.FilterConfig;
|
||||
import javax.servlet.FilterChain;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.ServletResponse;
|
||||
import javax.servlet.ServletRequest;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import org.glassfish.grizzly.servlet.HttpServletResponseImpl;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
|
||||
/**
|
||||
* Test ProxyUserAuthenticationFilter with doAs Request Parameter.
|
||||
*/
|
||||
public class TestProxyUserAuthenticationFilter {
|
||||
|
||||
private String actualUser;
|
||||
|
||||
private static class DummyFilterConfig implements FilterConfig {
|
||||
private final Map<String, String> map;
|
||||
|
||||
DummyFilterConfig(Map<String, String> map) {
|
||||
this.map = map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFilterName() {
|
||||
return "dummy";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInitParameter(String param) {
|
||||
return map.get(param);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Enumeration<String> getInitParameterNames() {
|
||||
return Collections.enumeration(map.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServletContext getServletContext() {
|
||||
ServletContext context = Mockito.mock(ServletContext.class);
|
||||
Mockito.when(context.getAttribute(
|
||||
AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE))
|
||||
.thenReturn(null);
|
||||
return context;
|
||||
}
|
||||
}
|
||||
|
||||
private class HttpServletResponseForTest extends HttpServletResponseImpl {
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Test(timeout = 10000)
|
||||
public void testFilter() throws Exception {
|
||||
Map<String, String> params = new HashMap<String, String>();
|
||||
params.put("proxyuser.knox.users", "testuser");
|
||||
params.put("proxyuser.knox.hosts", "127.0.0.1");
|
||||
params.put("type", "simple");
|
||||
|
||||
FilterConfig config = new DummyFilterConfig(params);
|
||||
|
||||
FilterChain chain = new FilterChain() {
|
||||
@Override
|
||||
public void doFilter(ServletRequest servletRequest,
|
||||
ServletResponse servletResponse) {
|
||||
HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||
actualUser = request.getRemoteUser();
|
||||
}
|
||||
};
|
||||
|
||||
ProxyUserAuthenticationFilter testFilter =
|
||||
new ProxyUserAuthenticationFilter();
|
||||
testFilter.init(config);
|
||||
|
||||
HttpServletRequest request = Mockito.mock(HttpServletRequest.class);
|
||||
Mockito.when(request.getRemoteUser()).thenReturn("knox");
|
||||
Mockito.when(request.getParameter("doAs")).thenReturn("testuser");
|
||||
Mockito.when(request.getRemoteAddr()).thenReturn("127.0.0.1");
|
||||
Mockito.when(request.getUserPrincipal()).thenReturn(new Principal() {
|
||||
@Override
|
||||
public String getName() {
|
||||
return "knox@EXAMPLE.COM";
|
||||
}
|
||||
});
|
||||
|
||||
HttpServletResponseForTest response = new HttpServletResponseForTest();
|
||||
|
||||
testFilter.doFilter(chain, request, response);
|
||||
|
||||
assertThat(actualUser).isEqualTo("testuser");
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user