Skip to content

Commit

Permalink
[AMBARI-24985] Handle requests from a configured trusted proxy to ide…
Browse files Browse the repository at this point in the history
…ntify a proxied user using Kerberos

* [AMBARI-24985] Handle requests from a configured trusted proxy to identify a proxied user using Kerberos

* [AMBARI-24985] Handle requests from a configured trusted proxy to identify a proxied user using Kerberos
  • Loading branch information
rlevas committed Dec 3, 2018
1 parent 0ce508d commit b6a3341
Show file tree
Hide file tree
Showing 29 changed files with 1,524 additions and 124 deletions.
Expand Up @@ -53,6 +53,7 @@ public class QueryLexer {
public static final String QUERY_FROM = "from";
public static final String QUERY_MINIMAL = "minimal_response";
public static final String QUERY_SORT = "sortBy";
public static final String QUERY_DOAS = "doAs";

/**
* All valid deliminators.
Expand Down Expand Up @@ -216,6 +217,7 @@ private Pattern generatePattern() {
SET_IGNORE.add(QUERY_FROM);
SET_IGNORE.add(QUERY_MINIMAL);
SET_IGNORE.add(QUERY_SORT);
SET_IGNORE.add(QUERY_DOAS);
SET_IGNORE.add("_");
}

Expand Down
Expand Up @@ -22,7 +22,9 @@
import org.apache.ambari.server.security.authentication.AmbariLocalAuthenticationProvider;
import org.apache.ambari.server.security.authentication.jwt.AmbariJwtAuthenticationProvider;
import org.apache.ambari.server.security.authentication.kerberos.AmbariAuthToLocalUserDetailsService;
import org.apache.ambari.server.security.authentication.kerberos.AmbariKerberosAuthenticationProvider;
import org.apache.ambari.server.security.authentication.kerberos.AmbariKerberosTicketValidator;
import org.apache.ambari.server.security.authentication.kerberos.AmbariProxiedUserDetailsService;
import org.apache.ambari.server.security.authentication.pam.AmbariPamAuthenticationProvider;
import org.apache.ambari.server.security.authorization.AmbariAuthorizationFilter;
import org.apache.ambari.server.security.authorization.AmbariLdapAuthenticationProvider;
Expand All @@ -37,7 +39,6 @@
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.kerberos.authentication.KerberosServiceAuthenticationProvider;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.www.BasicAuthenticationFilter;

Expand Down Expand Up @@ -67,15 +68,14 @@ public void configureAuthenticationManager(AuthenticationManagerBuilder auth,
AmbariLocalAuthenticationProvider ambariLocalAuthenticationProvider,
AmbariLdapAuthenticationProvider ambariLdapAuthenticationProvider,
AmbariInternalAuthenticationProvider ambariInternalAuthenticationProvider,
KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider
AmbariKerberosAuthenticationProvider ambariKerberosAuthenticationProvider
) {
auth.authenticationProvider(ambariJwtAuthenticationProvider)
.authenticationProvider(ambariPamAuthenticationProvider)
.authenticationProvider(ambariLocalAuthenticationProvider)
.authenticationProvider(ambariLdapAuthenticationProvider)
.authenticationProvider(ambariInternalAuthenticationProvider)
.authenticationProvider(kerberosServiceAuthenticationProvider);

.authenticationProvider(ambariKerberosAuthenticationProvider);
}

@Override
Expand All @@ -98,17 +98,13 @@ protected void configure(HttpSecurity http) throws Exception {
}

@Bean
public KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider(
public AmbariKerberosAuthenticationProvider ambariKerberosAuthenticationProvider(
AmbariKerberosTicketValidator ambariKerberosTicketValidator,
AmbariAuthToLocalUserDetailsService userDetailsService) {

KerberosServiceAuthenticationProvider kerberosServiceAuthenticationProvider =
new KerberosServiceAuthenticationProvider();
AmbariAuthToLocalUserDetailsService authToLocalUserDetailsService,
AmbariProxiedUserDetailsService proxiedUserDetailsService) {

kerberosServiceAuthenticationProvider.setTicketValidator(ambariKerberosTicketValidator);
kerberosServiceAuthenticationProvider.setUserDetailsService(userDetailsService);

return kerberosServiceAuthenticationProvider;
return new AmbariKerberosAuthenticationProvider(authToLocalUserDetailsService,
proxiedUserDetailsService,
ambariKerberosTicketValidator);
}

}
Expand Up @@ -25,6 +25,8 @@
import javax.servlet.http.HttpServletResponse;

import org.apache.ambari.server.security.AmbariEntryPoint;
import org.apache.ambari.server.utils.RequestUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
Expand Down Expand Up @@ -63,7 +65,7 @@ public AmbariBasicAuthenticationFilter(AuthenticationManager authenticationManag
AmbariAuthenticationEventHandler eventHandler) {
super(authenticationManager, ambariEntryPoint);

if(eventHandler == null) {
if (eventHandler == null) {
throw new IllegalArgumentException("The AmbariAuthenticationEventHandler must not be null");
}

Expand All @@ -90,8 +92,28 @@ public AmbariBasicAuthenticationFilter(AuthenticationManager authenticationManag
*/
@Override
public boolean shouldApply(HttpServletRequest httpServletRequest) {

if (LOG.isDebugEnabled()) {
RequestUtils.logRequestHeadersAndQueryParams(httpServletRequest, LOG);
}

String header = httpServletRequest.getHeader("Authorization");
return (header != null) && header.startsWith("Basic ");
if ((header != null) && header.startsWith("Basic ")) {
// If doAs is sent as a query parameter, ignore the basic auth header.
// This logic is here to help deal with a potential issue when Knox is the trusted proxy and it
// forwards the original request's Authorization header (for Basic Auth) when Kerberos authentication
// is required.
String doAsQueryParameterValue = RequestUtils.getQueryStringParameterValue(httpServletRequest, "doAs");
if (StringUtils.isEmpty(doAsQueryParameterValue)) {
return true;
} else {
LOG.warn("The 'doAs' query parameter was provided; however, the BasicAuth header is found. " +
"Ignoring the BasicAuth header hoping to negotiate Kerberos authentication.");
return false;
}
} else {
return false;
}
}

@Override
Expand All @@ -104,7 +126,7 @@ public boolean shouldIncrementFailureCount() {
*
* @param httpServletRequest the request
* @param httpServletResponse the response
* @param filterChain the Spring filter chain
* @param filterChain the Spring filter chain
* @throws IOException
* @throws ServletException
*/
Expand Down
Expand Up @@ -93,7 +93,7 @@ public Authentication authenticate(Authentication authentication) throws Authent
}
}

AmbariUserDetails userDetails = new AmbariUserDetails(users.getUser(userEntity), password, users.getUserAuthorities(userEntity));
AmbariUserDetails userDetails = new AmbariUserDetailsImpl(users.getUser(userEntity), password, users.getUserAuthorities(userEntity));
return new AmbariUserAuthentication(password, userDetails, true);
}
}
Expand Down
@@ -0,0 +1,96 @@
/*
* 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.ambari.server.security.authentication;

import java.util.Collection;

import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

/**
* AmbariProxiedUserDetailsImpl is an {@link AmbariUserDetails} implementation that contains details
* about the proxied user as well as the proxy user.
* <p>
* This data can be helpful with logging information about the user performing an action.
*/
public class AmbariProxiedUserDetailsImpl implements AmbariUserDetails {

/**
* Details about the acting user.
* <p>
* This user was specified as the doAs or proxied user during a trusted proxy authentication attempt
*/
private final UserDetails proxiedUserDetails;

/**
* Details about the proxy user that was authenticated.
* <p>
* This user that authenticated wth Ambari but specified a doAs or proxied user to be used as the acting user for operations.
*/
private final AmbariProxyUserDetails proxyUserDetails;

public AmbariProxiedUserDetailsImpl(UserDetails proxiedUserDetails, AmbariProxyUserDetails proxyUserDetails) {
this.proxiedUserDetails = proxiedUserDetails;
this.proxyUserDetails = proxyUserDetails;
}

@Override
public Integer getUserId() {
return (proxiedUserDetails instanceof AmbariUserDetails) ? ((AmbariUserDetails) proxiedUserDetails).getUserId() : null;
}

@Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return (proxiedUserDetails == null) ? null : proxiedUserDetails.getAuthorities();
}

@Override
public String getPassword() {
return (proxiedUserDetails == null) ? null : proxiedUserDetails.getPassword();
}

@Override
public String getUsername() {
return (proxiedUserDetails == null) ? null : proxiedUserDetails.getUsername();
}

@Override
public boolean isAccountNonExpired() {
return (proxiedUserDetails != null) && proxiedUserDetails.isAccountNonExpired();
}

@Override
public boolean isAccountNonLocked() {
return (proxiedUserDetails != null) && proxiedUserDetails.isAccountNonLocked();
}

@Override
public boolean isCredentialsNonExpired() {
return (proxiedUserDetails != null) && proxiedUserDetails.isCredentialsNonExpired();
}

@Override
public boolean isEnabled() {
return (proxiedUserDetails != null) && proxiedUserDetails.isEnabled();
}

public AmbariProxyUserDetails getProxyUserDetails() {
return proxyUserDetails;
}
}
@@ -0,0 +1,41 @@
/*
* 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.ambari.server.security.authentication;

import org.apache.ambari.server.security.authorization.UserAuthenticationType;

/**
* AmbariProxyUserDetails contains information about the proxy user authenticated during a trusted
* proxy authentication attempt.
*/
public interface AmbariProxyUserDetails {
/**
* Returns the local username of the authenticated proxy user.
*
* @return the local username of the authenticated proxy user
*/
String getUsername();

/**
* Returns the authentication type used to peform authentication.
*
* @return a {@link UserAuthenticationType}
*/
UserAuthenticationType getAuthenticationType();
}
@@ -0,0 +1,50 @@
/*
* 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.ambari.server.security.authentication;

import org.apache.ambari.server.security.authorization.UserAuthenticationType;

/**
* AmbariProxyUserDetailsImpl is a general implementation of a {@link AmbariProxyUserDetails}.
*/
public class AmbariProxyUserDetailsImpl implements AmbariProxyUserDetails {
private final String username;
private final UserAuthenticationType authenticationType;

/**
* Constructor
*
* @param username the local username
* @param authenticationType the {@link UserAuthenticationType}
*/
public AmbariProxyUserDetailsImpl(String username, UserAuthenticationType authenticationType) {
this.username = username;
this.authenticationType = authenticationType;
}

@Override
public String getUsername() {
return username;
}

@Override
public UserAuthenticationType getAuthenticationType() {
return authenticationType;
}
}

0 comments on commit b6a3341

Please sign in to comment.