Подписанный запрос на удаление aws elasticsearch возвращает 403 запрещено (с использованием apache httpclient)

Я получаю http 403 forbidden ошибку при попытке удалить aws elasticsearch индекс через java Jest (v6.3) клиент elasticsearch (который делегирует http-вызовы https://hc.apache.org/httpcomponents-client-ga/ Я знаю, что мои разрешения настроены правильно в AWS, так как я могу успешно использовать почтальон (с помощью помощника авторизации AWS Signature). Однако с apache httpclient, когда я выдаю DELETE /{myIndexName}, я получаю следующую ошибку:

    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.

Я подписываю запрос aws, настроив apache httpclient перехватчиком, который подписывает запрос. (Код ниже предназначен для класса Spring Framework @Configuration, который подключает клиент java Jest и базовый apache httpclient), но я полагаю, если бы я использовал apache httpclient напрямую, я Испытал бы ту же проблему.

@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();
    }
}

Поскольку он работает с почтальоном, он указывает на ошибку подписи, но я не понимаю, где возникает несоответствие. Приведенная выше конфигурация работает для всех apache httpclient запросов, кроме HTTP-запросов DELETE.




Ответы (1)


После кучи исследований я нашел некоторые подсказки, указывающие на возможность того, что наличие Content-Length (length = 0) в запросе, отправленном на aws, вызывало несоответствие подписи. Я предполагаю, что подпись, выполненная через клиентский перехватчик, не учитывалась в заголовке Content-Length, но поскольку мы отправляли заголовок Content-Length на сервер aws, он учитывал его и, таким образом, приводил к несоответствию подписи. Я считаю, что это так, потому что я добавил дополнительный перехватчик (перед перехватчиком подписи AWS), который явно удаляет заголовок Content-Length для запросов DELETE, и запрос проходит успешно. (т.е. я могу удалить индекс). Обновленный код ниже:

@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();
    }
}
person mjj1409    schedule 30.10.2018