3

I'm getting an http 403 forbidden error trying to delete an aws elasticsearch index via the java Jest(v6.3) elasticsearch client (which delegates the http calls to apache httpclient(v4.5.2) I know my permissions are setup correctly in AWS as I'm able to successfully use postman(with the help of AWS Signature authorization helper). however, with apache httpclient, when I issue the DELETE /{myIndexName} I receive the following error:

    The request signature we calculated does not match the signature you provided. 
    Check your AWS Secret Access Key and signing method. 
Consult the service documentation for details.

I'm signing the aws request by configuring the apache httpclient with an interceptor that signs the request.(The code below is for a Spring Framework @Configuration class that wires up the java Jest client and underlying apache httpclient) but I imagine if I used apache httpclient directly I'd experience the same issue.

@Configuration
public class ElasticSearchConfiguration {

    @Autowired
    private CredentialsProviderFactoryBean awsCredentialsProvider;

    @Bean
    public JestClient awsJestClient(@Value("${elasticsearch.url}") String connectionUrl) throws Exception {
        com.amazonaws.auth.AWSCredentialsProvider provider = awsCredentialsProvider.getObject();

        final com.google.common.base.Supplier<LocalDateTime> clock = () -> LocalDateTime.now(ZoneOffset.UTC);
        final vc.inreach.aws.request.AWSSigner awsSigner = new vc.inreach.aws.request.AWSSigner(provider, "us-east-1", "es", clock);
        final vc.inreach.aws.request.AWSSigningRequestInterceptor requestInterceptor = new vc.inreach.aws.request.AWSSigningRequestInterceptor(awsSigner);


        final JestClientFactory factory = new JestClientFactory() {
            @Override
            protected HttpClientBuilder configureHttpClient(HttpClientBuilder builder) {
                builder.addInterceptorLast(requestInterceptor);
                return builder;
            }
            @Override
            protected HttpAsyncClientBuilder configureHttpClient(HttpAsyncClientBuilder builder) {
                builder.addInterceptorLast(requestInterceptor);
                return builder;
            }
        };

        factory.setHttpClientConfig(new HttpClientConfig
                   .Builder(connectionUrl)
                   .connTimeout(60000)
                   .multiThreaded(true)
                   .build());

        return factory.getObject();
    }
}

Since it's working with postman it points to the a signing error but I'm at a loss to where the discrepancy is occurring. The configuration above works for all apache httpclient requests besides http DELETE requests.

Machavity
  • 30,841
  • 27
  • 92
  • 100
mjj1409
  • 3,075
  • 6
  • 28
  • 28

1 Answers1

1

After a bunch of research I found some clues that pointed to the possibility that the presence of the Content-Length (length=0) in request issued to aws were causing the signature mismatch. I'm guessing that the signature done via the client interceptor was not factoring in the Content-Length header but since we were sending the Content-Length header to the aws server it was factoring it in and thus causing the signature mismatch. I believe this to be the case because I added an additional interceptor(before the AWS signing interceptor) that explicitly removes the Content-Length header for DELETE requests and the request goes through successfully. (i.e. I'm able to delete the index). Updated code below:

@Configuration
public class ElasticSearchConfiguration {
    private static final Logger log = LoggerFactory.getLogger(ElasticSearchConfiguration.class);
    @Autowired
    private CredentialsProviderFactoryBean awsCredentialsProvider;


    @Bean
    public JestClient awsJestClient(@Value("${elasticsearch.url}") String connectionUrl) throws Exception {
        com.amazonaws.auth.AWSCredentialsProvider provider = awsCredentialsProvider.getObject();

        final com.google.common.base.Supplier<LocalDateTime> clock = () -> LocalDateTime.now(ZoneOffset.UTC);
        final vc.inreach.aws.request.AWSSigner awsSigner = new vc.inreach.aws.request.AWSSigner(provider, "us-east-1", "es", clock);
        final vc.inreach.aws.request.AWSSigningRequestInterceptor requestInterceptor = new vc.inreach.aws.request.AWSSigningRequestInterceptor(awsSigner);

        final HttpRequestInterceptor removeDeleteMethodContentLengthHeaderRequestInterceptor = (request, context) ->  {
            if(request.getRequestLine().getMethod().equals("DELETE")) {
                log.warn("intercepted aws es DELETE request, will remove 'Content-Length' header as it's presence invalidates the signature check on AWS' end");
                request.removeHeaders("Content-Length");
            }
        };

        final JestClientFactory factory = new JestClientFactory() {
            @Override
            protected HttpClientBuilder configureHttpClient(HttpClientBuilder builder) {
                builder.addInterceptorLast(removeDeleteMethodContentLengthHeaderRequestInterceptor);
                builder.addInterceptorLast(requestInterceptor);
                return builder;
            }
            @Override
            protected HttpAsyncClientBuilder configureHttpClient(HttpAsyncClientBuilder builder) {
                builder.addInterceptorLast(removeDeleteMethodContentLengthHeaderRequestInterceptor);
                builder.addInterceptorLast(requestInterceptor);
                return builder;
            }
        };

        factory.setHttpClientConfig(new HttpClientConfig
                .Builder(connectionUrl)
                .connTimeout(60000)
                .multiThreaded(true)
                .build());

        return factory.getObject();
    }
}
mjj1409
  • 3,075
  • 6
  • 28
  • 28