Commit 62cdbdc6 by nixonrodrigues

ATLAS-2824 updated authentication to support trusted proxy.

parent 854208c1
...@@ -38,6 +38,7 @@ import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHa ...@@ -38,6 +38,7 @@ import org.apache.hadoop.security.authentication.server.KerberosAuthenticationHa
import org.apache.hadoop.security.authentication.util.Signer; import org.apache.hadoop.security.authentication.util.Signer;
import org.apache.hadoop.security.authentication.util.SignerException; import org.apache.hadoop.security.authentication.util.SignerException;
import org.apache.hadoop.security.authentication.util.SignerSecretProvider; import org.apache.hadoop.security.authentication.util.SignerSecretProvider;
import org.apache.hadoop.security.authorize.ProxyUsers;
import org.apache.log4j.NDC; import org.apache.log4j.NDC;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
...@@ -50,7 +51,7 @@ import org.springframework.security.core.userdetails.User; ...@@ -50,7 +51,7 @@ import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.web.authentication.WebAuthenticationDetails; import org.springframework.security.web.authentication.WebAuthenticationDetails;
import org.springframework.stereotype.Component; import org.springframework.stereotype.Component;
import org.apache.hadoop.security.UserGroupInformation;
import javax.servlet.FilterChain; import javax.servlet.FilterChain;
import javax.servlet.FilterConfig; import javax.servlet.FilterConfig;
import javax.servlet.ServletContext; import javax.servlet.ServletContext;
...@@ -71,6 +72,7 @@ import java.text.SimpleDateFormat; ...@@ -71,6 +72,7 @@ import java.text.SimpleDateFormat;
import java.util.*; import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import org.apache.hadoop.security.authorize.AuthorizationException;
/** /**
...@@ -83,10 +85,11 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -83,10 +85,11 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationFilter.class); private static final Logger LOG = LoggerFactory.getLogger(AtlasAuthenticationFilter.class);
private static final String CONFIG_KERBEROS_TOKEN_VALIDITY = "atlas.authentication.method.kerberos.token.validity"; private static final String CONFIG_KERBEROS_TOKEN_VALIDITY = "atlas.authentication.method.kerberos.token.validity";
private static final String CONFIG_PROXY_USERS = "atlas.proxyusers"; private static final String CONFIG_PROXY_USERS = "atlas.proxyusers";
private static final String PREFIX = "atlas.authentication.method"; private static final String PREFIX = "atlas.authentication.method";
private static final String[] DEFAULT_PROXY_USERS = new String[] { "knox" }; private static final String[] DEFAULT_PROXY_USERS = new String[] { "knox" };
protected static final ServletContext nullContext = new NullServletContext(); private static final String CONF_PROXYUSER_PREFIX = "atlas.proxyuser";
protected static final ServletContext nullContext = new NullServletContext();
private Signer signer; private Signer signer;
private SignerSecretProvider secretProvider; private SignerSecretProvider secretProvider;
...@@ -97,18 +100,22 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -97,18 +100,22 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
private Configuration configuration; private Configuration configuration;
private Properties headerProperties; private Properties headerProperties;
private Set<String> atlasProxyUsers = new HashSet<>(); private Set<String> atlasProxyUsers = new HashSet<>();
private HttpServlet optionsServlet;
private boolean supportTrustedProxy = false;
public AtlasAuthenticationFilter() { public AtlasAuthenticationFilter() {
LOG.info("==> AtlasAuthenticationFilter()");
try { try {
LOG.info("AtlasAuthenticationFilter initialization started");
init(null); init(null);
} catch (ServletException e) { } catch (ServletException e) {
LOG.error("Error while initializing AtlasAuthenticationFilter : {}", e.getMessage()); LOG.error("Error while initializing AtlasAuthenticationFilter", e);
} }
}
private HttpServlet optionsServlet; LOG.info("<== AtlasAuthenticationFilter()");
}
/** /**
* Initialize the filter. * Initialize the filter.
...@@ -118,9 +125,10 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -118,9 +125,10 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
*/ */
@Override @Override
public void init(FilterConfig filterConfig) throws ServletException { public void init(FilterConfig filterConfig) throws ServletException {
LOG.info("AtlasAuthenticationFilter initialization started"); LOG.info("==> AtlasAuthenticationFilter.init");
final FilterConfig globalConf = filterConfig;
final Map<String, String> params = new HashMap<>(); final FilterConfig globalConf = filterConfig;
final Map<String, String> params = new HashMap<>();
try { try {
configuration = ApplicationProperties.get(); configuration = ApplicationProperties.get();
} catch (Exception e) { } catch (Exception e) {
...@@ -131,7 +139,11 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -131,7 +139,11 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
headerProperties = ConfigurationConverter.getProperties(configuration.subset("atlas.headers")); headerProperties = ConfigurationConverter.getProperties(configuration.subset("atlas.headers"));
} }
String tokenValidityStr = configuration.getString(CONFIG_KERBEROS_TOKEN_VALIDITY); String tokenValidityStr = null;
if(configuration != null) {
tokenValidityStr = configuration.getString(CONFIG_KERBEROS_TOKEN_VALIDITY);
}
if (StringUtils.isNotBlank(tokenValidityStr)) { if (StringUtils.isNotBlank(tokenValidityStr)) {
try { try {
...@@ -176,97 +188,121 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -176,97 +188,121 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
super.init(filterConfig1); super.init(filterConfig1);
ProxyUsers.refreshSuperUserGroupsConfiguration(getProxyuserConfiguration(), CONF_PROXYUSER_PREFIX);
optionsServlet = new HttpServlet() { optionsServlet = new HttpServlet() {
}; };
optionsServlet.init(); optionsServlet.init();
LOG.info("<== AtlasAuthenticationFilter.init(filterConfig={})", filterConfig);
} }
@Override @Override
public void initializeSecretProvider(FilterConfig filterConfig) public void initializeSecretProvider(FilterConfig filterConfig) throws ServletException {
throws ServletException { LOG.info("==> AtlasAuthenticationFilter.initializeSecretProvider");
LOG.debug("AtlasAuthenticationFilter :: initializeSecretProvider {}", filterConfig);
secretProvider = (SignerSecretProvider) filterConfig.getServletContext(). secretProvider = (SignerSecretProvider) filterConfig.getServletContext().getAttribute(AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE);
getAttribute(AuthenticationFilter.SIGNER_SECRET_PROVIDER_ATTRIBUTE);
if (secretProvider == null) { if (secretProvider == null) {
// As tomcat cannot specify the provider object in the configuration. // As tomcat cannot specify the provider object in the configuration.
// It'll go into this path // It'll go into this path
String configPrefix = filterConfig.getInitParameter(CONFIG_PREFIX); String configPrefix = filterConfig.getInitParameter(CONFIG_PREFIX);
configPrefix = (configPrefix != null) ? configPrefix + "." : ""; configPrefix = (configPrefix != null) ? configPrefix + "." : "";
try { try {
secretProvider = AuthenticationFilter.constructSecretProvider( secretProvider = AuthenticationFilter.constructSecretProvider(filterConfig.getServletContext(), super.getConfiguration(configPrefix, filterConfig), false);
filterConfig.getServletContext(),
super.getConfiguration(configPrefix, filterConfig), false);
this.isInitializedByTomcat = true; this.isInitializedByTomcat = true;
} catch (Exception ex) { } catch (Exception ex) {
throw new ServletException(ex); throw new ServletException(ex);
} }
} }
signer = new Signer(secretProvider); signer = new Signer(secretProvider);
LOG.info("<== AtlasAuthenticationFilter.initializeSecretProvider(filterConfig={})", filterConfig);
} }
@Override @Override
protected Properties getConfiguration(String configPrefix, FilterConfig filterConfig) throws ServletException { protected Properties getConfiguration(String configPrefix, FilterConfig filterConfig) throws ServletException {
LOG.info("==> AtlasAuthenticationFilter.getConfiguration()");
try { try {
configuration = ApplicationProperties.get(); configuration = ApplicationProperties.get();
} catch (Exception e) { } catch (Exception e) {
throw new ServletException(e); throw new ServletException(e);
} }
Properties config = new Properties(); Properties ret = new Properties();
String kerberosAuthEnabled = configuration != null ? configuration.getString("atlas.authentication.method.kerberos") : null; String kerberosAuthEnabled = configuration != null ? configuration.getString("atlas.authentication.method.kerberos") : null;
// getString may return null, and would like to log the nature of the default setting
String authMethod = ""; final String authMethod;
if (kerberosAuthEnabled == null || kerberosAuthEnabled.equalsIgnoreCase("false")) { if (kerberosAuthEnabled == null || kerberosAuthEnabled.equalsIgnoreCase("false")) {
LOG.info("No authentication method configured. Defaulting to simple authentication"); LOG.info("No authentication method configured. Defaulting to simple authentication");
authMethod = "simple"; authMethod = "simple";
} else if (kerberosAuthEnabled.equalsIgnoreCase("true")) { } else if (kerberosAuthEnabled.equalsIgnoreCase("true")) {
authMethod = "kerberos"; authMethod = "kerberos";
}
if (configuration.getString("atlas.authentication.method.kerberos.name.rules") != null) { if (configuration.getString("atlas.authentication.method.kerberos.name.rules") != null) {
config.put("kerberos.name.rules", configuration.getString("atlas.authentication.method.kerberos.name.rules")); ret.put("kerberos.name.rules", configuration.getString("atlas.authentication.method.kerberos.name.rules"));
} }
if (configuration.getString("atlas.authentication.method.kerberos.keytab") != null) {
config.put("kerberos.keytab", configuration.getString("atlas.authentication.method.kerberos.keytab")); if (configuration.getString("atlas.authentication.method.kerberos.keytab") != null) {
} ret.put("kerberos.keytab", configuration.getString("atlas.authentication.method.kerberos.keytab"));
if (configuration.getString("atlas.authentication.method.kerberos.principal") != null) { }
config.put("kerberos.principal", configuration.getString("atlas.authentication.method.kerberos.principal"));
if (configuration.getString("atlas.authentication.method.kerberos.principal") != null) {
ret.put("kerberos.principal", configuration.getString("atlas.authentication.method.kerberos.principal"));
}
} else {
authMethod = "";
} }
config.put(AuthenticationFilter.AUTH_TYPE, authMethod);
config.put(AuthenticationFilter.COOKIE_PATH, "/"); ret.put(AuthenticationFilter.AUTH_TYPE, authMethod);
ret.put(AuthenticationFilter.COOKIE_PATH, "/");
// add any config passed in as init parameters // add any config passed in as init parameters
Enumeration<String> enumeration = filterConfig.getInitParameterNames(); Enumeration<String> enumeration = filterConfig.getInitParameterNames();
while (enumeration.hasMoreElements()) { while (enumeration.hasMoreElements()) {
String name = enumeration.nextElement(); String name = enumeration.nextElement();
config.put(name, filterConfig.getInitParameter(name));
ret.put(name, filterConfig.getInitParameter(name));
} }
//Resolve _HOST into bind address //Resolve _HOST into bind address
String bindAddress = configuration.getString(SecurityProperties.BIND_ADDRESS); String bindAddress = configuration.getString(SecurityProperties.BIND_ADDRESS);
if (bindAddress == null) { if (bindAddress == null) {
LOG.info("No host name configured. Defaulting to local host name."); LOG.info("No host name configured. Defaulting to local host name.");
try { try {
bindAddress = InetAddress.getLocalHost().getHostName(); bindAddress = InetAddress.getLocalHost().getHostName();
} catch (UnknownHostException e) { } catch (UnknownHostException e) {
throw new ServletException("Unable to obtain host name", e); throw new ServletException("Unable to obtain host name", e);
} }
} }
String principal = config.getProperty(KerberosAuthenticationHandler.PRINCIPAL);
String principal = ret.getProperty(KerberosAuthenticationHandler.PRINCIPAL);
if (principal != null) { if (principal != null) {
try { try {
principal = SecurityUtil.getServerPrincipal(principal, bindAddress); principal = SecurityUtil.getServerPrincipal(principal, bindAddress);
} catch (IOException ex) { } catch (IOException ex) {
throw new RuntimeException("Could not resolve Kerberos principal name: " + ex.toString(), ex); throw new RuntimeException("Could not resolve Kerberos principal name: " + ex.toString(), ex);
} }
config.put(KerberosAuthenticationHandler.PRINCIPAL, principal);
ret.put(KerberosAuthenticationHandler.PRINCIPAL, principal);
} }
LOG.debug(" AuthenticationFilterConfig: {}", config);
LOG.debug(" AuthenticationFilterConfig: {}", ret);
supportKeyTabBrowserLogin = configuration.getBoolean("atlas.authentication.method.kerberos.support.keytab.browser.login", false); supportKeyTabBrowserLogin = configuration.getBoolean("atlas.authentication.method.kerberos.support.keytab.browser.login", false);
supportTrustedProxy = configuration.getBoolean("atlas.authentication.method.trustedproxy", true);
String agents = configuration.getString(AtlasCSRFPreventionFilter.BROWSER_USER_AGENT_PARAM, AtlasCSRFPreventionFilter.BROWSER_USER_AGENTS_DEFAULT); String agents = configuration.getString(AtlasCSRFPreventionFilter.BROWSER_USER_AGENT_PARAM, AtlasCSRFPreventionFilter.BROWSER_USER_AGENTS_DEFAULT);
if (agents == null) { if (agents == null) {
...@@ -283,67 +319,20 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -283,67 +319,20 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
parseBrowserUserAgents(agents); parseBrowserUserAgents(agents);
return config; LOG.info("<== AtlasAuthenticationFilter.getConfiguration(configPrefix={}, filterConfig={}): {}", configPrefix, filterConfig, ret);
return ret;
} }
@Override @Override
public void doFilter(final ServletRequest request, final ServletResponse response, public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain filterChain) throws IOException, ServletException {
final FilterChain filterChain) throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) request; final HttpServletRequest httpRequest = (HttpServletRequest) request;
FilterChain filterChainWrapper = new FilterChain() {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse)
throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
final HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
if (isKerberos) {
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
String userName = readUserFromCookie(httpResponse);
if (StringUtils.isEmpty(userName) && !StringUtils.isEmpty(httpRequest.getRemoteUser())) {
userName = httpRequest.getRemoteUser();
}
if ((existingAuth == null || !existingAuth.isAuthenticated()) && (!StringUtils.isEmpty(userName))) {
List<GrantedAuthority> grantedAuths = AtlasAuthenticationProvider.getAuthoritiesFromUGI(userName);
final UserDetails principal = new User(userName, "", grantedAuths);
final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken(principal, "", grantedAuths);
WebAuthenticationDetails webDetails = new WebAuthenticationDetails(httpRequest);
((AbstractAuthenticationToken) finalAuthentication).setDetails(webDetails);
SecurityContextHolder.getContext().setAuthentication(finalAuthentication);
request.setAttribute("atlas.http.authentication.type", true);
LOG.info("Logged into Atlas as = {}", userName);
}
}
// OPTIONS method is sent from quick start jersey atlas client
if (httpRequest.getMethod().equals("OPTIONS")) {
optionsServlet.service(request, response);
} else {
try {
String requestUser = httpRequest.getRemoteUser();
NDC.push(requestUser + ":" + httpRequest.getMethod() + httpRequest.getRequestURI());
LOG.info("Request from authenticated user: {}, URL={}", requestUser,
Servlets.getRequestURI(httpRequest));
filterChain.doFilter(servletRequest, servletResponse);
} finally {
NDC.pop();
}
}
}
};
try { try {
Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication(); Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
HttpServletResponse httpResponse = (HttpServletResponse) response; HttpServletResponse httpResponse = (HttpServletResponse) response;
AtlasResponseRequestWrapper responseWrapper = new AtlasResponseRequestWrapper(httpResponse); AtlasResponseRequestWrapper responseWrapper = new AtlasResponseRequestWrapper(httpResponse);
responseWrapper.setHeader("X-Frame-Options", "DENY"); responseWrapper.setHeader("X-Frame-Options", "DENY");
responseWrapper.setHeader("X-Content-Type-Options", "nosniff"); responseWrapper.setHeader("X-Content-Type-Options", "nosniff");
responseWrapper.setHeader("X-XSS-Protection", "1; mode=block"); responseWrapper.setHeader("X-XSS-Protection", "1; mode=block");
...@@ -351,17 +340,17 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -351,17 +340,17 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
if (headerProperties != null) { if (headerProperties != null) {
for (String headerKey : headerProperties.stringPropertyNames()) { for (String headerKey : headerProperties.stringPropertyNames()) {
String headerValue = headerProperties.getProperty(headerKey); responseWrapper.setHeader(headerKey, headerProperties.getProperty(headerKey));
responseWrapper.setHeader(headerKey, headerValue);
} }
} }
if (existingAuth == null) { if (existingAuth == null) {
String authHeader = httpRequest.getHeader("Authorization"); String authHeader = httpRequest.getHeader("Authorization");
if (authHeader != null && authHeader.startsWith("Basic")) { if (authHeader != null && authHeader.startsWith("Basic")) {
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
} else if (isKerberos) { } else if (isKerberos) {
doKerberosAuth(request, response, filterChainWrapper, filterChain); doKerberosAuth(request, response, filterChain);
} else { } else {
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
} }
...@@ -391,46 +380,54 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -391,46 +380,54 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
* @throws IOException thrown if an IO error occurred. * @throws IOException thrown if an IO error occurred.
* @throws ServletException thrown if a processing error occurred. * @throws ServletException thrown if a processing error occurred.
*/ */
public void doKerberosAuth(ServletRequest request, ServletResponse response, FilterChain filterChainWrapper, FilterChain filterChain) private void doKerberosAuth(ServletRequest request, ServletResponse response, FilterChain filterChain) throws IOException, ServletException {
throws IOException, ServletException { KerberosFilterChainWrapper filterChainWrapper = new KerberosFilterChainWrapper(request, response, filterChain);
boolean unauthorizedResponse = true; boolean unauthorizedResponse = true;
int errCode = HttpServletResponse.SC_UNAUTHORIZED; int errCode = HttpServletResponse.SC_UNAUTHORIZED;
AuthenticationException authenticationEx = null; AuthenticationException authenticationEx = null;
HttpServletRequest httpRequest = (HttpServletRequest) request; HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response; HttpServletResponse httpResponse = (HttpServletResponse) response;
boolean isHttps = "https".equals(httpRequest.getScheme()); boolean isHttps = "https".equals(httpRequest.getScheme());
AuthenticationHandler authHandler = getAuthenticationHandler(); AuthenticationHandler authHandler = getAuthenticationHandler();
try { try {
boolean newToken = false; boolean newToken = false;
AuthenticationToken token; AuthenticationToken token;
try { try {
token = getToken(httpRequest); token = getToken(httpRequest);
} catch (AuthenticationException ex) { } catch (AuthenticationException ex) {
LOG.warn("AuthenticationToken ignored: {}", ex.getMessage()); LOG.warn("AuthenticationToken ignored: {}", ex);
// will be sent back in a 401 unless filter authenticates // will be sent back in a 401 unless filter authenticates
authenticationEx = ex; authenticationEx = ex;
token = null; token = null;
} }
if (authHandler.managementOperation(token, httpRequest, httpResponse)) { if (authHandler.managementOperation(token, httpRequest, httpResponse)) {
if (token == null) { if (token == null) {
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("Request [{}] triggering authentication", getRequestURL(httpRequest)); LOG.debug("Request [{}] triggering authentication", getRequestURL(httpRequest));
} }
token = authHandler.authenticate(httpRequest, httpResponse); token = authHandler.authenticate(httpRequest, httpResponse);
if (token != null && token.getExpires() != 0 &&
token != AuthenticationToken.ANONYMOUS) { if (token != null && token.getExpires() != 0 && token != AuthenticationToken.ANONYMOUS) {
token.setExpires(System.currentTimeMillis() + getValidity() * 1000); token.setExpires(System.currentTimeMillis() + getValidity() * 1000);
} }
newToken = true; newToken = true;
} }
if (token != null) { if (token != null) {
unauthorizedResponse = false;
if (LOG.isDebugEnabled()) { if (LOG.isDebugEnabled()) {
LOG.debug("Request [{}] user [{}] authenticated", getRequestURL(httpRequest), token.getUserName()); LOG.debug("Request [{}] user [{}] authenticated", getRequestURL(httpRequest), token.getUserName());
} }
unauthorizedResponse = false;
final AuthenticationToken authToken = token; final AuthenticationToken authToken = token;
httpRequest = new HttpServletRequestWrapper(httpRequest) {
httpRequest = new HttpServletRequestWrapper(httpRequest) {
@Override @Override
public String getAuthType() { public String getAuthType() {
return authToken.getType(); return authToken.getType();
...@@ -447,7 +444,31 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -447,7 +444,31 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
} }
}; };
if(StringUtils.isNotBlank(httpRequest.getRemoteUser()) && atlasProxyUsers.contains(httpRequest.getRemoteUser())){ // Create the proxy user if doAsUser exists
String doAsUser = supportTrustedProxy ? Servlets.getDoAsUser(httpRequest) : null;
if (supportTrustedProxy && doAsUser != null) {
LOG.debug("doAsUser is {}", doAsUser);
UserGroupInformation requestUgi = (token != null) ? UserGroupInformation.createRemoteUser(token.getUserName()) : null;
if (requestUgi != null) {
requestUgi = UserGroupInformation.createProxyUser(doAsUser, requestUgi);
try {
ProxyUsers.authorize(requestUgi, request.getRemoteAddr());
request.setAttribute("proxyUser", doAsUser);
} catch (AuthorizationException ex) {
LOG.warn("Proxy user AuthorizationException", ex);
httpResponse.setStatus(HttpServletResponse.SC_FORBIDDEN);
filterChain.doFilter(request, response);
return;
}
}
} else if(StringUtils.isNotBlank(httpRequest.getRemoteUser()) && atlasProxyUsers.contains(httpRequest.getRemoteUser())){
LOG.info("Ignoring kerberos login from proxy user "+ httpRequest.getRemoteUser()); LOG.info("Ignoring kerberos login from proxy user "+ httpRequest.getRemoteUser());
httpResponse.setHeader(KerberosAuthenticator.WWW_AUTHENTICATE, ""); httpResponse.setHeader(KerberosAuthenticator.WWW_AUTHENTICATE, "");
...@@ -457,11 +478,10 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -457,11 +478,10 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
return; return;
} }
if (newToken && !token.isExpired() && token != AuthenticationToken.ANONYMOUS) { if (newToken && !token.isExpired() && token != AuthenticationToken.ANONYMOUS) {
String signedToken = signer.sign(token.toString()); String signedToken = signer.sign(token.toString());
createAuthCookie(httpResponse, signedToken, getCookieDomain(),
getCookiePath(), token.getExpires(), isHttps); createAtlasAuthCookie(httpResponse, signedToken, getCookieDomain(), getCookiePath(), token.getExpires(), isHttps);
} }
filterChainWrapper.doFilter(httpRequest, httpResponse); filterChainWrapper.doFilter(httpRequest, httpResponse);
...@@ -470,36 +490,41 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -470,36 +490,41 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
unauthorizedResponse = false; unauthorizedResponse = false;
} }
} catch (AuthenticationException ex) { } catch (AuthenticationException ex) {
LOG.warn("Authentication exception: {}", ex.getMessage(), ex);
// exception from the filter itself is fatal // exception from the filter itself is fatal
errCode = HttpServletResponse.SC_FORBIDDEN; errCode = HttpServletResponse.SC_FORBIDDEN;
authenticationEx = ex; authenticationEx = ex;
LOG.warn("Authentication exception: {}", ex.getMessage(), ex);
} }
if (unauthorizedResponse) { if (unauthorizedResponse) {
if (!httpResponse.isCommitted()) { if (!httpResponse.isCommitted()) {
createAuthCookie(httpResponse, "", getCookieDomain(), createAtlasAuthCookie(httpResponse, "", getCookieDomain(), getCookiePath(), 0, isHttps);
getCookiePath(), 0, isHttps);
// If response code is 401. Then WWW-Authenticate Header should be // If response code is 401. Then WWW-Authenticate Header should be
// present.. reset to 403 if not found.. // present.. reset to 403 if not found..
if ((errCode == HttpServletResponse.SC_UNAUTHORIZED) if (errCode == HttpServletResponse.SC_UNAUTHORIZED && !httpResponse.containsHeader(KerberosAuthenticator.WWW_AUTHENTICATE)) {
&& (!httpResponse.containsHeader(
KerberosAuthenticator.WWW_AUTHENTICATE))) {
errCode = HttpServletResponse.SC_FORBIDDEN; errCode = HttpServletResponse.SC_FORBIDDEN;
} }
if (authenticationEx == null) { // added this code for atlas error handling and fallback if (authenticationEx == null) { // added this code for atlas error handling and fallback
if (!supportKeyTabBrowserLogin && isBrowser(httpRequest.getHeader("User-Agent"))) { if (!supportKeyTabBrowserLogin && isBrowser(httpRequest.getHeader("User-Agent"))) {
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
} else { } else {
boolean chk = true; boolean chk = true;
Collection<String> headerNames = httpResponse.getHeaderNames(); Collection<String> headerNames = httpResponse.getHeaderNames();
for (String headerName : headerNames) { for (String headerName : headerNames) {
String value = httpResponse.getHeader(headerName); String value = httpResponse.getHeader(headerName);
if (headerName.equalsIgnoreCase("Set-Cookie") && value.startsWith("ATLASSESSIONID")) { if (headerName.equalsIgnoreCase("Set-Cookie") && value.startsWith("ATLASSESSIONID")) {
chk = false; chk = false;
break; break;
} }
} }
String authHeader = httpRequest.getHeader("Authorization"); String authHeader = httpRequest.getHeader("Authorization");
if (authHeader == null && chk) { if (authHeader == null && chk) {
filterChain.doFilter(request, response); filterChain.doFilter(request, response);
} else if (authHeader != null && authHeader.startsWith("Basic")) { } else if (authHeader != null && authHeader.startsWith("Basic")) {
...@@ -527,21 +552,26 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -527,21 +552,26 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
private static String readUserFromCookie(HttpServletResponse response1) { private static String readUserFromCookie(HttpServletResponse response1) {
String userName = null; String userName = null;
boolean isCookieSet = response1.containsHeader("Set-Cookie"); boolean isCookieSet = response1.containsHeader("Set-Cookie");
if (isCookieSet) { if (isCookieSet) {
Collection<String> authUserName = response1.getHeaders("Set-Cookie"); Collection<String> authUserName = response1.getHeaders("Set-Cookie");
if (authUserName != null) { if (authUserName != null) {
for (String cookie : authUserName) { for (String cookie : authUserName) {
if (!StringUtils.isEmpty(cookie)) { if (!StringUtils.isEmpty(cookie)) {
if (cookie.toLowerCase().startsWith(AuthenticatedURL.AUTH_COOKIE.toLowerCase()) && cookie.contains("u=")) { if (cookie.toLowerCase().startsWith(AuthenticatedURL.AUTH_COOKIE.toLowerCase()) && cookie.contains("u=")) {
String[] split = cookie.split(";"); String[] split = cookie.split(";");
if (split != null) { if (split != null) {
for (String s : split) { for (String s : split) {
if (!StringUtils.isEmpty(s) && s.toLowerCase().startsWith(AuthenticatedURL.AUTH_COOKIE.toLowerCase())) { if (!StringUtils.isEmpty(s) && s.toLowerCase().startsWith(AuthenticatedURL.AUTH_COOKIE.toLowerCase())) {
int ustr = s.indexOf("u="); int ustr = s.indexOf("u=");
if (ustr != -1) { if (ustr != -1) {
int andStr = s.indexOf("&", ustr); int andStr = s.indexOf("&", ustr);
if (andStr != -1) { if (andStr != -1) {
try { try {
userName = s.substring(ustr + 2, andStr); userName = s.substring(ustr + 2, andStr);
...@@ -559,16 +589,19 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -559,16 +589,19 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
} }
} }
} }
return userName; return userName;
} }
public static void createAuthCookie(HttpServletResponse resp, String token, String domain, String path, long expires, boolean isSecure) { private void createAtlasAuthCookie(HttpServletResponse resp, String token, String domain, String path, long expires, boolean isSecure) {
StringBuilder sb = (new StringBuilder(AuthenticatedURL.AUTH_COOKIE)).append("="); StringBuilder sb = (new StringBuilder(AuthenticatedURL.AUTH_COOKIE)).append("=");
if (token != null && token.length() > 0) { if (token != null && token.length() > 0) {
sb.append("\"").append(token).append("\""); sb.append("\"").append(token).append("\"");
} }
sb.append("; Version=1"); sb.append("; Version=1");
if (path != null) { if (path != null) {
sb.append("; Path=").append(path); sb.append("; Path=").append(path);
} }
...@@ -578,10 +611,9 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -578,10 +611,9 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
} }
if (expires >= 0L) { if (expires >= 0L) {
Date date = new Date(expires);
SimpleDateFormat df = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss zzz"); SimpleDateFormat df = new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss zzz");
df.setTimeZone(TimeZone.getTimeZone("GMT")); df.setTimeZone(TimeZone.getTimeZone("GMT"));
sb.append("; Expires=").append(df.format(date)); sb.append("; Expires=").append(df.format(new Date(expires)));
} }
if (isSecure) { if (isSecure) {
...@@ -635,18 +667,94 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter { ...@@ -635,18 +667,94 @@ public class AtlasAuthenticationFilter extends AuthenticationFilter {
} }
boolean isBrowser(String userAgent) { boolean isBrowser(String userAgent) {
if (userAgent == null) { if (userAgent != null) {
return false;
}
if (browserUserAgents != null) {
for (Pattern pattern : browserUserAgents) { for (Pattern pattern : browserUserAgents) {
Matcher matcher = pattern.matcher(userAgent); Matcher matcher = pattern.matcher(userAgent);
if (matcher.matches()) { if (matcher.matches()) {
return true; return true;
} }
} }
} }
return false; return false;
} }
private class KerberosFilterChainWrapper implements FilterChain {
private final ServletRequest request;
private final ServletResponse response;
private final FilterChain filterChain;
KerberosFilterChainWrapper(ServletRequest request, ServletResponse response, FilterChain filterChain) {
this.request = request;
this.response = response;
this.filterChain = filterChain;
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException, ServletException {
final HttpServletRequest httpRequest = (HttpServletRequest) servletRequest;
final HttpServletResponse httpResponse = (HttpServletResponse) servletResponse;
final Authentication existingAuth = SecurityContextHolder.getContext().getAuthentication();
String loggedInUser = readUserFromCookie(httpResponse);
String userName = loggedInUser;
if (!StringUtils.isEmpty((String) httpRequest.getAttribute("proxyUser"))) {
userName = (String) httpRequest.getAttribute("proxyUser");
} else if (StringUtils.isEmpty(userName) && !StringUtils.isEmpty(httpRequest.getRemoteUser())) {
userName = httpRequest.getRemoteUser();
}
if ((existingAuth == null || !existingAuth.isAuthenticated()) && !StringUtils.isEmpty(userName)) {
final List<GrantedAuthority> grantedAuths = AtlasAuthenticationProvider.getAuthoritiesFromUGI(userName);
final UserDetails principal = new User(userName, "", grantedAuths);
final Authentication finalAuthentication = new UsernamePasswordAuthenticationToken(principal, "", grantedAuths);
final WebAuthenticationDetails webDetails = new WebAuthenticationDetails(httpRequest);
((AbstractAuthenticationToken) finalAuthentication).setDetails(webDetails);
SecurityContextHolder.getContext().setAuthentication(finalAuthentication);
request.setAttribute("atlas.http.authentication.type", true);
if (!StringUtils.equals(loggedInUser, userName)) {
LOG.info("Logged into Atlas as = {}, by proxyUser = {}", userName, loggedInUser);
} else {
LOG.info("Logged into Atlas as = {}", userName);
}
}
// OPTIONS method is sent from quick start jersey atlas client
if (httpRequest.getMethod().equals("OPTIONS")) {
optionsServlet.service(request, response);
} else {
try {
String requestUser = httpRequest.getRemoteUser();
NDC.push(requestUser + ":" + httpRequest.getMethod() + httpRequest.getRequestURI());
LOG.info("Request from authenticated user: {}, URL={}", requestUser, Servlets.getRequestURI(httpRequest));
filterChain.doFilter(servletRequest, servletResponse);
} finally {
NDC.pop();
}
}
}
}
private org.apache.hadoop.conf.Configuration getProxyuserConfiguration() {
org.apache.hadoop.conf.Configuration ret = new org.apache.hadoop.conf.Configuration(false);
if(configuration!=null) {
Properties props = ConfigurationConverter.getProperties(configuration.subset(CONF_PROXYUSER_PREFIX));
for (String key : props.stringPropertyNames()) {
ret.set(CONF_PROXYUSER_PREFIX + "." + key, props.getProperty(key));
}
}
return ret;
}
} }
...@@ -91,6 +91,7 @@ public class NegativeSSLAndKerberosTest extends BaseSSLAndKerberosTest { ...@@ -91,6 +91,7 @@ public class NegativeSSLAndKerberosTest extends BaseSSLAndKerberosTest {
+ "/users-credentials"); + "/users-credentials");
configuration.setProperty("atlas.auth.policy.file",persistDir configuration.setProperty("atlas.auth.policy.file",persistDir
+ "/policy-store.txt" ); + "/policy-store.txt" );
configuration.setProperty("atlas.authentication.method.trustedproxy", "false");
TestUtils.writeConfiguration(configuration, persistDir + File.separator + TestUtils.writeConfiguration(configuration, persistDir + File.separator +
ApplicationProperties.APPLICATION_PROPERTIES); ApplicationProperties.APPLICATION_PROPERTIES);
......
...@@ -91,6 +91,7 @@ public class SSLAndKerberosTest extends BaseSSLAndKerberosTest { ...@@ -91,6 +91,7 @@ public class SSLAndKerberosTest extends BaseSSLAndKerberosTest {
configuration.setProperty("atlas.authentication.principal","dgi/localhost@"+kdc.getRealm()); configuration.setProperty("atlas.authentication.principal","dgi/localhost@"+kdc.getRealm());
configuration.setProperty("atlas.authentication.method.file", "false"); configuration.setProperty("atlas.authentication.method.file", "false");
configuration.setProperty("atlas.authentication.method.trustedproxy", "false");
configuration.setProperty("atlas.authentication.method.kerberos", "true"); configuration.setProperty("atlas.authentication.method.kerberos", "true");
configuration.setProperty("atlas.authentication.method.kerberos.principal", "HTTP/localhost@" + kdc.getRealm()); configuration.setProperty("atlas.authentication.method.kerberos.principal", "HTTP/localhost@" + kdc.getRealm());
configuration.setProperty("atlas.authentication.method.kerberos.keytab", httpKeytabFile.getAbsolutePath()); configuration.setProperty("atlas.authentication.method.kerberos.keytab", httpKeytabFile.getAbsolutePath());
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment