First commit, Dilli?
Wahoo - Congrats!

On Wed, Aug 21, 2013 at 2:19 PM, <[email protected]> wrote:

> Updated Branches:
>   refs/heads/master 0f763c6bb -> e36fc4a31
>
>
> patch to fix KNOX-89, Knox doing SPNego with Hadoop for every client
> request is not scalable
>
>
> Project: http://git-wip-us.apache.org/repos/asf/incubator-knox/repo
> Commit:
> http://git-wip-us.apache.org/repos/asf/incubator-knox/commit/e36fc4a3
> Tree: http://git-wip-us.apache.org/repos/asf/incubator-knox/tree/e36fc4a3
> Diff: http://git-wip-us.apache.org/repos/asf/incubator-knox/diff/e36fc4a3
>
> Branch: refs/heads/master
> Commit: e36fc4a3151d208e4327132f2bffb9e383d2c060
> Parents: 0f763c6
> Author: Dilli Dorai Arumugam <[email protected]>
> Authored: Tue Aug 20 22:37:49 2013 -0700
> Committer: Dilli Dorai Arumugam <[email protected]>
> Committed: Wed Aug 21 11:12:19 2013 -0700
>
> ----------------------------------------------------------------------
>  .../home/templates/krb5JAASLogin.conf           |  31 +--
>  .../apache/hadoop/gateway/GatewayMessages.java  |   6 +
>  .../gateway/dispatch/AppCookieManager.java      | 190 +++++++++++++++++++
>  .../gateway/dispatch/HttpClientDispatch.java    | 109 ++++++-----
>  .../gateway/dispatch/AppCookieManagerTest.java  |  53 ++++++
>  5 files changed, 312 insertions(+), 77 deletions(-)
> ----------------------------------------------------------------------
>
>
>
> http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e36fc4a3/gateway-release/home/templates/krb5JAASLogin.conf
> ----------------------------------------------------------------------
> diff --git a/gateway-release/home/templates/krb5JAASLogin.conf
> b/gateway-release/home/templates/krb5JAASLogin.conf
> index cfb4d19..d9f8d7b 100644
> --- a/gateway-release/home/templates/krb5JAASLogin.conf
> +++ b/gateway-release/home/templates/krb5JAASLogin.conf
> @@ -17,20 +17,6 @@
>  *
>  * IMPORTANT: REPLACE EXAMPLE.COM and keyTab file location with your site
> specific values
>  */
> -com.sun.security.jgss.login {
> -    com.sun.security.auth.module.Krb5LoginModule required
> -    renewTGT=true
> -    doNotPrompt=true
> -    useKeyTab=true
> -    keyTab="/etc/knox/conf/knox.service.keytab"
> -    principal="[email protected]"
> -    isInitiator=true
> -    storeKey=true
> -    useTicketCache=true
> -    client=true
> -    debug=true;
> -};
> -
>  com.sun.security.jgss.initiate {
>      com.sun.security.auth.module.Krb5LoginModule required
>      renewTGT=true
> @@ -41,20 +27,5 @@ com.sun.security.jgss.initiate {
>      isInitiator=true
>      storeKey=true
>      useTicketCache=true
> -    client=true
> -    debug=true;
> -};
> -
> -com.sun.security.jgss.accept {
> -    com.sun.security.auth.module.Krb5LoginModule required
> -    renewTGT=true
> -    doNotPrompt=true
> -    useKeyTab=true
> -    keyTab="/etc/knox/conf/knox.service.keytab"
> -    principal="[email protected]"
> -    isInitiator=true
> -    storeKey=true
> -    useTicketCache=true
> -    client=true
> -    debug=true;
> +    client=true;
>  };
>
>
> http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e36fc4a3/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java
> ----------------------------------------------------------------------
> diff --git
> a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java
> b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java
> index 3337b19..34ef775 100644
> ---
> a/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java
> +++
> b/gateway-server/src/main/java/org/apache/hadoop/gateway/GatewayMessages.java
> @@ -260,4 +260,10 @@ public interface GatewayMessages {
>
>    @Message( level = MessageLevel.ERROR, text = "Failed to get map from
> Json string {0}: {1}" )
>    void failedToGetMapFromJsonString( String json, @StackTrace( level =
> MessageLevel.DEBUG ) Exception e );
> +
> +  @Message( level = MessageLevel.INFO, text = "Successful Knox->Hadoop
> SPNegotiation authentication for URL: {0}" )
> +  void successfulSPNegoAuthn(String uri);
> +
> +  @Message( level = MessageLevel.ERROR, text = "Failed Knox->Hadoop
> SPNegotiation authentication for URL: {0}" )
> +  void failedSPNegoAuthn(String uri);
>  }
>
>
> http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e36fc4a3/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/AppCookieManager.java
> ----------------------------------------------------------------------
> diff --git
> a/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/AppCookieManager.java
> b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/AppCookieManager.java
> new file mode 100644
> index 0000000..d0f0ecc
> --- /dev/null
> +++
> b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/AppCookieManager.java
> @@ -0,0 +1,190 @@
> +/**
> + * 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.gateway.dispatch;
> +
> +import java.io.IOException;
> +import java.net.URI;
> +import java.security.Principal;
> +
> +import org.apache.hadoop.gateway.GatewayMessages;
> +import org.apache.hadoop.gateway.i18n.messages.MessagesFactory;
> +import org.apache.http.Header;
> +import org.apache.http.HeaderElement;
> +import org.apache.http.HttpEntity;
> +import org.apache.http.HttpHost;
> +import org.apache.http.HttpRequest;
> +import org.apache.http.HttpResponse;
> +import org.apache.http.auth.AuthScope;
> +import org.apache.http.auth.Credentials;
> +import org.apache.http.client.methods.HttpGet;
> +import org.apache.http.client.methods.HttpOptions;
> +import org.apache.http.client.methods.HttpUriRequest;
> +import org.apache.http.client.params.AuthPolicy;
> +import org.apache.http.impl.auth.SPNegoSchemeFactory;
> +import org.apache.http.impl.client.DefaultHttpClient;
> +
> +/**
> + * Handles SPNego authentication as a client of hadoop service, caches
> + * hadoop.auth cookie returned by hadoop service on successful SPNego
> + * authentication. Refreshes hadoop.auth cookie on demand if the cookie
> has
> + * expired.
> + *
> + */
> +public class AppCookieManager {
> +
> +  static final String HADOOP_AUTH = "hadoop.auth";
> +  private static final String HADOOP_AUTH_EQ = "hadoop.auth=";
> +  private static final String SET_COOKIE = "Set-Cookie";
> +
> +  private static GatewayMessages LOG =
> MessagesFactory.get(GatewayMessages.class);
> +
> +  private static final EmptyJaasCredentials EMPTY_JAAS_CREDENTIALS = new
> EmptyJaasCredentials();
> +
> +  String appCookie;
> +
> +  /**
> +   * Utility method to excerise AppCookieManager directly
> +   * @param args element 0 of args should be a URL to hadoop service
> protected by SPengo
> +   * @throws IOException in case of errors
> +   */
> +  public static void main(String[] args) throws IOException {
> +    HttpUriRequest outboundRequest = new HttpGet(args[0]);
> +    new AppCookieManager().getAppCookie(outboundRequest, false);
> +  }
> +
> +  public AppCookieManager() {
> +  }
> +
> +  /**
> +   * Fetches hadoop.auth cookie from hadoop service authenticating using
> SpNego
> +   *
> +   * @param outboundRequest
> +   *          out going request
> +   * @param refresh
> +   *          flag indicating whether to refresh the cached cookie
> +   * @return hadoop.auth cookie from hadoop service authenticating using
> SpNego
> +   * @throws IOException
> +   *           in case of errors
> +   */
> +  public String getAppCookie(HttpUriRequest outboundRequest, boolean
> refresh)
> +      throws IOException {
> +
> +    URI uri = outboundRequest.getURI();
> +    String scheme = uri.getScheme();
> +    String host = uri.getHost();
> +    int port = uri.getPort();
> +    String path = uri.getPath();
> +    if (!refresh) {
> +      if (appCookie != null) {
> +        return appCookie;
> +      }
> +    }
> +
> +    DefaultHttpClient client = new DefaultHttpClient();
> +    SPNegoSchemeFactory spNegoSF = new SPNegoSchemeFactory(
> +    /* stripPort */true);
> +    // spNegoSF.setSpengoGenerator(new BouncySpnegoTokenGenerator());
> +    client.getAuthSchemes().register(AuthPolicy.SPNEGO, spNegoSF);
> +    client.getCredentialsProvider().setCredentials(
> +        new AuthScope(/* host */null, /* port */-1, /* realm */null),
> +        EMPTY_JAAS_CREDENTIALS);
> +
> +    clearAppCookie();
> +    String hadoopAuthCookie = null;
> +    HttpResponse httpResponse = null;
> +    try {
> +      HttpHost httpHost = new HttpHost(host, port, scheme);
> +      HttpRequest httpRequest = new HttpOptions(path);
> +      httpResponse = client.execute(httpHost, httpRequest);
> +      Header[] headers = httpResponse.getHeaders(SET_COOKIE);
> +      hadoopAuthCookie = getHadoopAuthCookieValue(headers);
> +      if (hadoopAuthCookie == null) {
> +        LOG.failedSPNegoAuthn(uri.toString());
> +        throw new IOException(
> +            "SPNego authn failed, can not get hadoop.auth cookie");
> +      }
> +    } finally {
> +      if (httpResponse != null) {
> +        HttpEntity entity = httpResponse.getEntity();
> +        if (entity != null) {
> +          entity.getContent().close();
> +        }
> +      }
> +
> +    }
> +    LOG.successfulSPNegoAuthn(uri.toString());
> +    hadoopAuthCookie = HADOOP_AUTH_EQ + quote(hadoopAuthCookie);
> +    setAppCookie(hadoopAuthCookie);
> +    return appCookie;
> +  }
> +
> +  /**
> +   * Returns the cached app cookie
> +   *
> +   * @return the cached app cookie, can be null
> +   */
> +  public String getCachedKnoxAppCookie() {
> +    return appCookie;
> +  }
> +
> +  private void setAppCookie(String appCookie) {
> +    this.appCookie = appCookie;
> +  }
> +
> +  private void clearAppCookie() {
> +    appCookie = null;
> +  }
> +
> +  static String quote(String s) {
> +    return s == null ? s : "\"" + s + "\"";
> +  }
> +
> +  static String getHadoopAuthCookieValue(Header[] headers) {
> +    if (headers == null) {
> +      return null;
> +    }
> +    for (Header header : headers) {
> +      HeaderElement[] elements = header.getElements();
> +      for (HeaderElement element : elements) {
> +        String cookieName = element.getName();
> +        if (cookieName.equals(HADOOP_AUTH)) {
> +          if (element.getValue() != null) {
> +            String trimmedVal = element.getValue().trim();
> +            if (!trimmedVal.isEmpty()) {
> +              return trimmedVal;
> +            }
> +          }
> +        }
> +      }
> +    }
> +    return null;
> +  }
> +
> +  private static class EmptyJaasCredentials implements Credentials {
> +
> +    public String getPassword() {
> +      return null;
> +    }
> +
> +    public Principal getUserPrincipal() {
> +      return null;
> +    }
> +
> +  }
> +
> +}
>
>
> http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e36fc4a3/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HttpClientDispatch.java
> ----------------------------------------------------------------------
> diff --git
> a/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HttpClientDispatch.java
> b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HttpClientDispatch.java
> index 2d38aea..ac8d85c 100644
> ---
> a/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HttpClientDispatch.java
> +++
> b/gateway-server/src/main/java/org/apache/hadoop/gateway/dispatch/HttpClientDispatch.java
> @@ -17,7 +17,15 @@
>   */
>  package org.apache.hadoop.gateway.dispatch;
>
> -import org.apache.commons.io.IOUtils;
> +import java.io.IOException;
> +import java.io.InputStream;
> +import java.net.URI;
> +import java.net.URISyntaxException;
> +import java.security.Principal;
> +
> +import javax.servlet.http.HttpServletRequest;
> +import javax.servlet.http.HttpServletResponse;
> +
>  import org.apache.hadoop.gateway.GatewayMessages;
>  import org.apache.hadoop.gateway.GatewayResources;
>  import org.apache.hadoop.gateway.config.GatewayConfig;
> @@ -26,7 +34,7 @@ import
> org.apache.hadoop.gateway.i18n.resources.ResourcesFactory;
>  import org.apache.http.Header;
>  import org.apache.http.HttpEntity;
>  import org.apache.http.HttpResponse;
> -import org.apache.http.auth.AuthScope;
> +import org.apache.http.HttpStatus;
>  import org.apache.http.auth.Credentials;
>  import org.apache.http.client.methods.HttpDelete;
>  import org.apache.http.client.methods.HttpGet;
> @@ -34,39 +42,32 @@ import org.apache.http.client.methods.HttpOptions;
>  import org.apache.http.client.methods.HttpPost;
>  import org.apache.http.client.methods.HttpPut;
>  import org.apache.http.client.methods.HttpUriRequest;
> -import org.apache.http.client.params.AuthPolicy;
>  import org.apache.http.entity.BufferedHttpEntity;
> -import org.apache.http.entity.ByteArrayEntity;
>  import org.apache.http.entity.ContentType;
>  import org.apache.http.entity.InputStreamEntity;
> -import org.apache.http.impl.auth.SPNegoSchemeFactory;
>  import org.apache.http.impl.client.DefaultHttpClient;
> -import org.apache.http.protocol.BasicHttpContext;
> -import org.apache.http.protocol.HttpContext;
> -
> -import javax.activation.MimeType;
> -import javax.servlet.http.HttpServletRequest;
> -import javax.servlet.http.HttpServletResponse;
> -import java.io.IOException;
> -import java.io.InputStream;
> -import java.net.URI;
> -import java.net.URISyntaxException;
> -import java.nio.charset.Charset;
> -import java.security.Principal;
> +import org.apache.http.message.BasicHeader;
>
>  /**
>   *
>   */
>  public class HttpClientDispatch extends AbstractGatewayDispatch {
> -
> -  private static final String CT_APP_WWW_FORM_URL_ENCODED =
> "application/x-www-form-urlencoded";
> -  private static final String CT_APP_XML = "application/xml";
> +
> +  // private static final String CT_APP_WWW_FORM_URL_ENCODED =
> "application/x-www-form-urlencoded";
> +  // private static final String CT_APP_XML = "application/xml";
> +  private static final String Q_DELEGATION_EQ = "?delegation=";
> +  private static final String AMP_DELEGATION_EQ = "&delegation=";
> +  private static final String COOKIE = "Cookie";
> +  private static final String SET_COOKIE = "Set-Cookie";
> +  private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
> +  private static final String NEGOTIATE = "Negotiate";
>
>    private static GatewayMessages LOG = MessagesFactory.get(
> GatewayMessages.class );
>    private static GatewayResources RES = ResourcesFactory.get(
> GatewayResources.class );
> -  private static final EmptyJaasCredentials EMPTY_JAAS_CREDENTIALS = new
> EmptyJaasCredentials();
>    private static final int REPLAY_BUFFER_MAX_SIZE = 1024 * 1024; // limit
> to 1MB
>
> +  private AppCookieManager appCookieManager = new AppCookieManager();;
> +
>    protected void executeRequest(
>        HttpUriRequest outboundRequest,
>        HttpServletRequest inboundRequest,
> @@ -74,22 +75,46 @@ public class HttpClientDispatch extends
> AbstractGatewayDispatch {
>            throws IOException {
>      LOG.dispatchRequest( outboundRequest.getMethod(),
> outboundRequest.getURI() );
>      DefaultHttpClient client = new DefaultHttpClient();
> -
> -    if
> ("true".equals(System.getProperty(GatewayConfig.HADOOP_KERBEROS_SECURED))) {
> -      SPNegoSchemeFactory nsf = new SPNegoSchemeFactory(/* stripPort */
> true);
> -      // nsf.setSpengoGenerator(new BouncySpnegoTokenGenerator());
> -      client.getAuthSchemes().register(AuthPolicy.SPNEGO, nsf);
>
> -      client.getCredentialsProvider().setCredentials(
> -          new AuthScope(/* host */ null, /* port */ -1, /* realm */ null),
> -          EMPTY_JAAS_CREDENTIALS);
> -    }
> -
> -    HttpContext localContext = new BasicHttpContext();
> -
>      HttpResponse inboundResponse;
>      try {
> -      inboundResponse = client.execute(outboundRequest, localContext);
> +      String query = outboundRequest.getURI().getQuery();
> +      if
> (!"true".equals(System.getProperty(GatewayConfig.HADOOP_KERBEROS_SECURED)))
> {
> +        // Hadoop cluster not Kerberos enabled
> +        inboundResponse = client.execute(outboundRequest);
> +      } else if (query.contains(Q_DELEGATION_EQ) ||
> +        // query string carries delegation token
> +        query.contains(AMP_DELEGATION_EQ)) {
> +        inboundResponse = client.execute(outboundRequest);
> +      } else {
> +        // Kerberos secured, no delegation token in query string
> +        outboundRequest.removeHeaders(COOKIE);
> +        String appCookie = appCookieManager.getCachedKnoxAppCookie();
> +        if (appCookie != null) {
> +          outboundRequest.addHeader(new BasicHeader(COOKIE, appCookie));
> +        }
> +        inboundResponse = client.execute(outboundRequest);
> +        // if inBoundResponse has status 401 and header WWW-Authenticate:
> Negoitate
> +        // refresh hadoop.auth.cookie and attempt one more time
> +        int statusCode = inboundResponse.getStatusLine().getStatusCode();
> +        if (statusCode == HttpStatus.SC_UNAUTHORIZED ) {
> +          Header[] wwwAuthHeaders =
> inboundResponse.getHeaders(WWW_AUTHENTICATE) ;
> +          if (wwwAuthHeaders != null && wwwAuthHeaders.length != 0 &&
> +              wwwAuthHeaders[0].getValue().trim().startsWith(NEGOTIATE)) {
> +            appCookie = appCookieManager.getAppCookie(outboundRequest,
> true);
> +            outboundRequest.removeHeaders(COOKIE);
> +            outboundRequest.addHeader(new BasicHeader(COOKIE, appCookie));
> +            client = new DefaultHttpClient();
> +            inboundResponse = client.execute(outboundRequest);
> +          } else {
> +            // no supported authentication type found
> +            // we would let the original response propogate
> +          }
> +        } else {
> +          // not a 401 Unauthorized status code
> +          // we would let the original response propogate
> +        }
> +      }
>      } catch (IOException e) {
>        // we do not want to expose back end host. port end points to
> clients, see JIRA KNOX-58
>        LOG.dispatchServiceConnectionException( outboundRequest.getURI(), e
> );
> @@ -101,6 +126,9 @@ public class HttpClientDispatch extends
> AbstractGatewayDispatch {
>      Header[] headers = inboundResponse.getAllHeaders();
>      for( Header header : headers ) {
>        String name = header.getName();
> +      if (name.equals(SET_COOKIE) || name.equals(WWW_AUTHENTICATE)) {
> +        continue;
> +      }
>        String value = header.getValue();
>        outboundResponse.addHeader( name, value );
>      }
> @@ -239,18 +267,5 @@ public class HttpClientDispatch extends
> AbstractGatewayDispatch {
>  //    }
>  //
>  //  }
> -
> -  private static class EmptyJaasCredentials implements Credentials {
> -
> -    public String getPassword() {
> -      return null;
> -    }
> -
> -    public Principal getUserPrincipal() {
> -      return null;
> -    }
> -
> -  }
> -
>
>  }
>
>
> http://git-wip-us.apache.org/repos/asf/incubator-knox/blob/e36fc4a3/gateway-server/src/test/java/org/apache/hadoop/gateway/dispatch/AppCookieManagerTest.java
> ----------------------------------------------------------------------
> diff --git
> a/gateway-server/src/test/java/org/apache/hadoop/gateway/dispatch/AppCookieManagerTest.java
> b/gateway-server/src/test/java/org/apache/hadoop/gateway/dispatch/AppCookieManagerTest.java
> new file mode 100644
> index 0000000..704c41c
> --- /dev/null
> +++
> b/gateway-server/src/test/java/org/apache/hadoop/gateway/dispatch/AppCookieManagerTest.java
> @@ -0,0 +1,53 @@
> +/**
> + * 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.gateway.dispatch;
> +
> +
> +import static org.junit.Assert.assertNotNull;
> +import static org.junit.Assert.assertNull;
> +
> +import org.apache.http.Header;
> +import org.apache.http.message.BasicHeader;
> +import org.junit.Test;
> +
> +public class AppCookieManagerTest {
> +
> +  @Test
> +  public void getCachedKnoxAppCookie() {
> +    assertNull(new AppCookieManager().getCachedKnoxAppCookie());
> +  }
> +
> +  @Test
> +  public void getHadoopAuthCookieValueWithNullHeaders() {
> +    assertNull(AppCookieManager.getHadoopAuthCookieValue(null));
> +  }
> +
> +  @Test
> +  public void getHadoopAuthCookieValueWitEmptylHeaders() {
> +    assertNull(AppCookieManager.getHadoopAuthCookieValue(new Header[0]));
> +  }
> +
> +  @Test
> +  public void getHadoopAuthCookieValueWithValidlHeaders() {
> +    Header[] headers = new Header[1];
> +    headers[0] = new BasicHeader("Set-Cookie",
> AppCookieManager.HADOOP_AUTH + "=dummyvalue");
> +    assertNotNull(AppCookieManager.getHadoopAuthCookieValue(headers));
> +  }
> +
> +}
> +
>
>

Reply via email to