Hi !

Thank you for your rich answers and sorry for the delay.

@Larry. The informations sytems architects would still like to have Knox
authenticating the user before the end service, for security concerns (they
don't want the http stream to even reach the end service, which is in
another network zone, if the user can't authenticate to the LDAP). So the
authentication should be enforced both by Knox and the end service, I guess
it's a mix of your first and second point :) It allows to keep it simple,
no mutual authentication between the services, but i'm sure this could also
be useful in some usecases.

@Sumit. Yes i suppose what I am trying to achieve and the issue with Ambari
are quite similar. I had not seen your response (I naively thought that I
would get the answers to my email box ;)) but I did go with the custom
dispatch solution, using the custom dispatch class in my service.xml file.
However, as I wrote above, I am still using Knox authentication filter.

Once one understands that a custom dispatch would do the job, it is pretty
easy to implement.

I'm pasting a quick&dirty code below, what do you guys think ? It seems to
works fine.

Thank you both for you help and I should get your answers now that I
subscribed to the list ! ;)
Jérémiemie

/* SimpleDispatch is made to get incoming request, copy everything to
outgoing request, and that's it, no kerberos stuff
or excluded headers. It's well suited to relay a Basic HTTP Authentication
to a backend service like Elasticsearch for instance */

public class SimpleDispatch extends DefaultDispatch {

    protected static SpiGatewayMessages LOG =
MessagesFactory.get(SpiGatewayMessages.class);
    protected static SpiGatewayResources RES =
ResourcesFactory.get(SpiGatewayResources.class);
    protected void executeRequest(
            HttpUriRequest outboundRequest,
            HttpServletRequest inboundRequest,
            HttpServletResponse outboundResponse)
            throws IOException {


        // Copy all headers from the inbound request to the outbound request
        // Looks like the content-length header is already added to the
outboundrequest by Knox if there's a body, so don't add it again
        List<String> excludeHeaders = Lists.newArrayList("Content-Length");
//
        Enumeration<String> headersName = inboundRequest.getHeaderNames();
        while (headersName.hasMoreElements()) {
            String headerName = headersName.nextElement();
            if (outboundRequest.getHeaders(headerName).length == 0 &&
!excludeHeaders.contains(headerName)) {
                outboundRequest.addHeader(headerName,
inboundRequest.getHeader(headerName));
            }
        }

        HttpResponse inboundResponse =
executeOutboundRequest(outboundRequest);
        writeOutboundResponse(outboundRequest, inboundRequest,
outboundResponse, inboundResponse);

    }

    protected HttpResponse executeOutboundRequest(HttpUriRequest
outboundRequest) throws IOException {

        // This code is basically a copy paste of the parent class without
all the Kerberos stuff

        LOG.dispatchRequest(outboundRequest.getMethod(),
outboundRequest.getURI());
        HttpResponse inboundResponse = null;

        try {
            inboundResponse = client.execute(outboundRequest);
        } 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);
            auditor.audit(Action.DISPATCH,
outboundRequest.getURI().toString(), ResourceType.URI,
ActionOutcome.FAILURE);
            throw new IOException(RES.dispatchConnectionError());
        } finally {
            if (inboundResponse != null) {
                int statusCode =
inboundResponse.getStatusLine().getStatusCode();
                if (statusCode != 201) {
                    LOG.dispatchResponseStatusCode(statusCode);
                } else {
                    Header location =
inboundResponse.getFirstHeader("Location");
                    if (location == null) {
                        LOG.dispatchResponseStatusCode(statusCode);
                    } else {
                        LOG.dispatchResponseCreatedStatusCode(statusCode,
location.getValue());
                    }
                }
                auditor.audit(Action.DISPATCH,
outboundRequest.getURI().toString(), ResourceType.URI,
ActionOutcome.SUCCESS, RES.responseStatus(statusCode));
            } else {
                auditor.audit(Action.DISPATCH,
outboundRequest.getURI().toString(), ResourceType.URI,
ActionOutcome.UNAVAILABLE);
            }

        }
        return inboundResponse;
    }
}

Here is my service.xml (you can see that what i'm doing is actually using
Knox with a searchguard-secured Elasticsearch):

<service role="ELASTICSEARCH" name="elasticsearch" version="1.6.2">
  <routes>
     <route path="/elasticsearch/**">
        <rewrite apply="ELASTICSEARCH/inbound" to="request.url"/>
     </route>
  </routes>
  <dispatch classname="xxx.xxx.xxx.xxx.SimpleDispatch"/>
</service>

Reply via email to