Почему я получаю запрещенную ошибку 403 при публикации json в конечной точке elasticsearch на AWS?

Я отправляю json в AWS elasticsearch, используя лямбда-функцию Java.

public Object handleRequest(DynamodbEvent dynamodbEvent, Context context) {

            //code to general the json document     
            AmazonDynamoDBClient amazonDynamoDBClient = new AmazonDynamoDBClient();

    List<DynamodbEvent.DynamodbStreamRecord> dynamodbStreamRecordlist = dynamodbEvent.getRecords();

    if (!dynamodbStreamRecordlist.isEmpty()) {
        DynamodbEvent.DynamodbStreamRecord record = dynamodbStreamRecordlist.get(0);
        if(record.getEventSource().equalsIgnoreCase("aws:dynamodb"))
            tableName = getTableNameFromARN(record.getEventSourceARN());
    }
    LaneAnnotation laneAnnotation = new LaneAnnotation();

    ScanRequest scanRequest = new ScanRequest().withTableName(tableName);
    ScanResult result = amazonDynamoDBClient.scan(scanRequest);

    List<Lines> linesFinalList = new ArrayList<Lines>();

    if(result != null) {
        for (Map<String, AttributeValue> item : result.getItems()) {      

         //code for looping through the table items and generating a json     object for the elastic search model
        }    

            //Code to post the json below - 
            RestTemplate restTemplate = new RestTemplate();
            SimpleClientHttpRequestFactory clientHttpRequestFactory = (SimpleClientHttpRequestFactory)restTemplate.getRequestFactory();
            clientHttpRequestFactory.setConnectTimeout(10000);
            clientHttpRequestFactory.setReadTimeout(10000);

            HttpEntity<String> entity = new HttpEntity<String>(<json goes here>, headers);

            try{
                restTemplate.exchange(endpoint, HttpMethod.POST, entity, String.class);
            }catch(Exception e){
                e.printStackTrace();
            }
}

Однако при тестировании лямбда-функции AWS я вижу следующую ошибку:

org.springframework.web.client.HttpClientErrorException: 403 Forbidden
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:700)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:653)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:613)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:531)
    at com.here.aws.LambdaApplication.handleRequest(LambdaApplication.java:166)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at lambdainternal.EventHandlerLoader$PojoMethodRequestHandler.handleRequest(EventHandlerLoader.java:456)
    at lambdainternal.EventHandlerLoader$PojoHandlerAsStreamHandler.handleRequest(EventHandlerLoader.java:375)
    at lambdainternal.EventHandlerLoader$2.call(EventHandlerLoader.java:1139)
    at lambdainternal.AWSLambda.startRuntime(AWSLambda.java:285)
    at lambdainternal.AWSLambda.<clinit>(AWSLambda.java:57)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:348)
    at lambdainternal.LambdaRTEntry.main(LambdaRTEntry.java:94)

Я даже изменил политику доступа и добавил свой IP-адрес. Сталкивались ли с этим и другие? Как вы решили эту проблему?> Любая помощь будет оценена по достоинству.

EDIT1: сейчас я пытаюсь включить подписание запроса, как указано здесь - https://aws.amazon.com/blogs/security/how-to-control-access-к-вашему-домену-службы-эластичного-поиска-амазонки/
Сообщит, если все пойдет хорошо.

EDIT2:

Вот второй способ отправки запроса, который я пробовал, ссылаясь на ссылку выше:

@Override
    public Object handleRequest(DynamodbEvent dynamodbEvent, Context context) {

        AmazonDynamoDBClient amazonDynamoDBClient = new AmazonDynamoDBClient();

        List<DynamodbEvent.DynamodbStreamRecord> dynamodbStreamRecordlist = dynamodbEvent.getRecords();

        if (!dynamodbStreamRecordlist.isEmpty()) {
            DynamodbEvent.DynamodbStreamRecord record = dynamodbStreamRecordlist.get(0);
            if(record.getEventSource().equalsIgnoreCase("aws:dynamodb"))
                tableName = getTableNameFromARN(record.getEventSourceARN());
        }
        LaneAnnotation laneAnnotation = new LaneAnnotation();

        ScanRequest scanRequest = new ScanRequest().withTableName(tableName);
        ScanResult result = amazonDynamoDBClient.scan(scanRequest);

        List<Lines> linesFinalList = new ArrayList<Lines>();

        if(result != null) {
            for (Map<String, AttributeValue> item : result.getItems()) {
           //Generate the json object that needs to be sent in the request

        }

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON_UTF8);

        Request<?> request = new DefaultRequest<Void>(SERVICE_NAME);
        request.setContent(new ByteArrayInputStream(elasticSearchModel.toString().getBytes()));
        request.setEndpoint(URI.create(endpoint));
        request.setHttpMethod(HttpMethodName.POST);

        AWS4Signer signer = new AWS4Signer();
        signer.setServiceName(SERVICE_NAME);
        signer.setRegionName(Regions.US_EAST_1.getName());

        AWSCredentialsProvider credsProvider =
                new DefaultAWSCredentialsProviderChain();

        AWSCredentials creds = credsProvider.getCredentials();

        // Sign request with supplied creds
        signer.sign(request, creds);
        log.info("Request signed");

        ExecutionContext executionContext = new ExecutionContext(true);

        ClientConfiguration clientConfiguration = new ClientConfiguration();
        AmazonHttpClient client = new AmazonHttpClient(clientConfiguration);

        MyHttpResponseHandler<Void> responseHandler = new MyHttpResponseHandler<Void>();
        MyErrorHandler errorHandler = new MyErrorHandler();

        Response<Void> response =
                client.execute(request, responseHandler, errorHandler, executionContext);

        return dynamodbEvent;
    }

Однако я получаю следующую ошибку -

    Check the signature you provided. Check your AWS Secret Access Key and signing method. Consult the service documentation for details.

    The Canonical String for this request should have been
    'GET
    /

    host:somehostname-XXXXXXXXXXXXXXXX.us-east-1.es.amazonaws.com
    x-amz-date:20170130T105736Z
    x-amz-security-token:FQoDYXdzEG4aDJJ4ryjXXXXXXXXXXXXXXXX/auMHooYENY6YXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX

    host;x-amz-date;x-amz-security-token
    e3b0c4429XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'

    The String-to-Sign should have been
    'AWS4-HMAC-SHA256
    20170130T105736Z
    20170130/us-east-1/es/aws4_request
    9a5b4c92ec121c333f8cdXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
    "}"

10:57:36.818 [main] DEBUG org.apache.http.headers - http-outgoing-1 << HTTP/1.1 403 Forbidden

person Community    schedule 30.01.2017    source источник
comment
Может быть ряд вещей, которые могут вызвать это. Либо индекс es установлен только для чтения (то есть когда сам ES возвращает 403), либо если вы используете прокси перед ним, либо у вас нет доступа для записи в этот индекс.   -  person chaos    schedule 30.01.2017
comment
Это потому, что мне нужно добавить аутентификацию в запрос? Я просто загружаю свою лямбда-банку на s3 и использую ее URL-адрес для проверки моей лямбда-функции. Я добавил еще немного кода выше, чтобы показать, как я извлекаю элементы из своей таблицы.   -  person    schedule 30.01.2017
comment
Только что нашел это - aws.amazon.com/blogs/security/ Позвольте мне попробовать, и я расскажу здесь о том, как все прошло.   -  person    schedule 30.01.2017
comment
Отредактировал мой вопрос   -  person    schedule 30.01.2017


Ответы (1)


AWS Elastic Search имеет шлюз безопасности, обеспечивающий аутентификацию. Параметры аутентификации настраиваются в консоли AWS Elastic Search.

Вы получаете сообщение об ошибке аутентификации 403, поскольку ваша политика доступа AWS Elastic Search не разрешает IP-адрес для шлюза NAT, который использует ваша Lambda.

http://docs.aws.amazon.com/elasticsearch-service/latest/developerguide/es-createupdatedomains.html#es-createddomain-configure-access-policies

Вот шаблон политики доступа, который вы можете использовать для аутентификации на основе IP.

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "*"
            },
            "Action": "es:*",
            "Resource": "arn:aws:es:us-east-1:YOUR-AWS-ACCOUNT-ID:domain/YOUR-ELASTICSEARCH-DOMAIN-NAME/*",
            "Condition": {
                "IpAddress": {
                    "aws:SourceIp": [
                        "YOUR-NAT-GATEWAY-PUBLIC-IP/32"
                    ]
                }
            }
        }
    ]
}
person Travis Collins    schedule 17.12.2017