Assinatura de solicitações HTTP no Amazon OpenSearch Service - Amazon OpenSearch Service

Assinatura de solicitações HTTP no Amazon OpenSearch Service

Esta seção inclui exemplos de como enviar solicitações HTTP assinadas para o Amazon OpenSearch Service usando clientes do Elasticsearch e OpenSearch e outras bibliotecas comuns. Esses exemplos de código são úteis para interagir com as APIs do OpenSearch, como _index, _bulk e _snapshot. Se sua política de acesso ao domínio incluir usuários ou funções do IAM (ou se você usar um usuário primário do IAM com controle de acesso refinado), será necessário assinar solicitações para as APIs do OpenSearch APIs com suas credenciais do IAM.

Para obter exemplos de como interagir com a API de configuração, incluindo operações como criar, atualizar e excluir domínios do operação, consulte Uso de AWS SDKs para interagir com o Amazon OpenSearch Service.

Importante

As versões mais recentes dos clientes Elasticsearch podem incluir verificações de licença ou versão que interrompem artificialmente a compatibilidade. Para obter a versão correta do cliente a ser usada, consulte Compatibilidade com clientes Elasticsearch.

Java

A maneira mais fácil de enviar uma solicitação assinada é usando o Interceptador de assinatura de solicitação da Amazon Web Services. O repositório contém alguns exemplos para ajudar você a começar, ou você pode usar o exemplo de projeto para o OpenSearch Service no GitHub.

O exemplo a seguir usa o cliente Java REST de baixo nível do opensearch-java para realizar duas ações não relacionadas: registrar um repositório de snapshots e indexar um documento. Você deve fornecer valores para region e host.

import com.amazonaws.http.AwsRequestSigningApacheInterceptor; import org.apache.http.HttpEntity; import org.apache.http.HttpHost; import org.apache.http.HttpRequestInterceptor; import org.apache.http.entity.ContentType; import org.apache.http.nio.entity.NStringEntity; import org.opensearch.client.Request; import org.opensearch.client.Response; import org.opensearch.client.RestClient; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.auth.signer.Aws4Signer; import java.io.IOException; public class AmazonOpenSearchServiceSample { private static String serviceName = "es"; private static String region = ""; private static String host = ""; // e.g. https://search-mydomain.us-west-1.es.amazonaws.com private static String payload = "{ \"type\": \"s3\", \"settings\": { \"bucket\": \"your-bucket\", \"region\": \"us-west-1\", \"role_arn\": \"arn:aws:iam::123456789012:role/TheServiceRole\" } }"; private static String snapshotPath = "/_snapshot/my-snapshot-repo"; private static String sampleDocument = "{" + "\"title\":\"Walk the Line\"," + "\"director\":\"James Mangold\"," + "\"year\":\"2005\"}"; private static String indexingPath = "/my-index/_doc"; static final AwsCredentialsProvider credentialsProvider = DefaultCredentialsProvider.create(); public static void main(String[] args) throws IOException { RestClient searchClient = searchClient(serviceName, region); // Register a snapshot repository HttpEntity entity = new NStringEntity(payload, ContentType.APPLICATION_JSON); Request request = new Request("PUT", snapshotPath); request.setEntity(entity); // request.addParameter(name, value); // optional parameters Response response = searchClient.performRequest(request); System.out.println(response.toString()); // Index a document entity = new NStringEntity(sampleDocument, ContentType.APPLICATION_JSON); String id = "1"; request = new Request("PUT", indexingPath + "/" + id); request.setEntity(entity); // Using a String instead of an HttpEntity sets Content-Type to application/json automatically. // request.setJsonEntity(sampleDocument); response = searchClient.performRequest(request); System.out.println(response.toString()); } // Adds the interceptor to the OpenSearch REST client public static RestClient searchClient(String serviceName, String region) { Aws4Signer signer = Aws4Signer.create(); HttpRequestInterceptor interceptor = new AwsRequestSigningApacheInterceptor(serviceName, signer, credentialsProvider,region); return RestClient.builder(HttpHost.create(host)).setHttpClientConfigCallback(hacb -> hacb.addInterceptorLast(interceptor)).build(); } }

Se você preferir o cliente REST de alto nível, que oferece a maioria dos mesmos recursos e um código mais simples, tente usar o exemplo a seguir, que também usa o Interceptador de assinatura de solicitação da Amazon Web Services:

import com.amazonaws.http.AwsRequestSigningApacheInterceptor; import org.apache.http.HttpHost; import org.apache.http.HttpRequestInterceptor; import org.opensearch.action.index.IndexRequest; import org.opensearch.action.index.IndexResponse; import org.opensearch.client.RequestOptions; import org.opensearch.client.RestClient; import org.opensearch.client.RestHighLevelClient; import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider; import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.auth.signer.Aws4Signer; import java.io.IOException; import java.util.HashMap; import java.util.Map; public class AmazonOpenSearchServiceSample { private static String serviceName = "es"; private static String region = ""; // e.g. us-east-1 private static String host = ""; // e.g. https://search-mydomain.us-west-1.es.amazonaws.com private static String index = "my-index"; private static String type = "_doc"; private static String id = "1"; static final AwsCredentialsProvider credentialsProvider = DefaultCredentialsProvider.create(); public static void main(String[] args) throws IOException { RestHighLevelClient searchClient = searchClient(serviceName, region); // Create the document as a hash map Map<String, Object> document = new HashMap<>(); document.put("title", "Walk the Line"); document.put("director", "James Mangold"); document.put("year", "2005"); // Form the indexing request, send it, and print the response IndexRequest request = new IndexRequest(index, type, id).source(document); IndexResponse response = searchClient.index(request, RequestOptions.DEFAULT); System.out.println(response.toString()); } // Adds the interceptor to the OpenSearch REST client public static RestHighLevelClient searchClient(String serviceName, String region) { Aws4Signer signer = Aws4Signer.create(); HttpRequestInterceptor interceptor = new AwsRequestSigningApacheInterceptor(serviceName, signer, credentialsProvider,region); return new RestHighLevelClient(RestClient.builder(HttpHost.create(host)).setHttpClientConfigCallback(hacb -> hacb.addInterceptorLast(interceptor))); } }
dica

Ambos os exemplos assinados usam a cadeia de credenciais padrão. Execute aws configure usando a AWS CLI para definir suas credenciais.

Python

Este exemplo usa o cliente opensearch-py para Python, que você pode instalar usando pip. Você deve fornecer valores para region e host.

from opensearchpy import OpenSearch, RequestsHttpConnection, AWSV4SignerAuth import boto3 host = '' # cluster endpoint, for example: my-test-domain.us-east-1.es.amazonaws.com region = '' # e.g. us-west-1 credentials = boto3.Session().get_credentials() auth = AWSV4SignerAuth(credentials, region) index_name = 'movies' client = OpenSearch( hosts = [{'host': host, 'port': 443}], http_auth = auth, use_ssl = True, verify_certs = True, connection_class = RequestsHttpConnection ) q = 'miller' query = { 'size': 5, 'query': { 'multi_match': { 'query': q, 'fields': ['title^2', 'director'] } } } response = client.search( body = query, index = index_name ) print('\nSearch results:') print(response)

Em vez do cliente, você pode optar por solicitações. Os pacotes requests-aws4auth e SDK for Python (Boto3) simplificam o processo de autenticação, mas não são estritamente necessários. No terminal, execute os comandos a seguir:

pip install boto3 pip install opensearch-py pip install requests pip install requests-aws4auth

O código de exemplo a seguir estabelece uma conexão segura com o domínio do OpenSearch Service especificado e indexa um único documento. Você deve fornecer valores para region e host.

from opensearchpy import OpenSearch, RequestsHttpConnection from requests_aws4auth import AWS4Auth import boto3 host = '' # For example, my-test-domain.us-east-1.es.amazonaws.com region = '' # e.g. us-west-1 service = 'es' credentials = boto3.Session().get_credentials() awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service, session_token=credentials.token) search = OpenSearch( hosts = [{'host': host, 'port': 443}], http_auth = awsauth, use_ssl = True, verify_certs = True, connection_class = RequestsHttpConnection ) document = { "title": "Moneyball", "director": "Bennett Miller", "year": "2011" } search.index(index="movies", doc_type="_doc", id="5", body=document) print(search.get(index="movies", doc_type="_doc", id="5"))

Se você não deseja usar o elasticsearch-py, pode simplesmente fazer solicitações HTTP padrão. Este exemplo cria um novo índice com sete fragmentos e duas réplicas:

from requests_aws4auth import AWS4Auth import boto3 import requests host = '' # The domain with https:// and trailing slash. For example, https://my-test-domain.us-east-1.es.amazonaws.com/ path = 'my-index' # the OpenSearch API endpoint region = '' # For example, us-west-1 service = 'es' credentials = boto3.Session().get_credentials() awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service, session_token=credentials.token) url = host + path # The JSON body to accompany the request (if necessary) payload = { "settings" : { "number_of_shards" : 7, "number_of_replicas" : 2 } } r = requests.put(url, auth=awsauth, json=payload) # requests.get, post, and delete have similar syntax print(r.text)

Em vez de usar credenciais estáticas, você pode construir uma instância AWS4Auth usando credenciais com atualização automática. Isso é adequado para aplicativos de execução de longa duração usando AssumeRole. A instância de credenciais atualizáveis é usada para gerar credenciais estáticas válidas para cada solicitação, eliminando a necessidade de recriar a instância AWS4Auth quando as credenciais temporárias expirarem:

from requests_aws4auth import AWS4Auth from botocore.session import Session credentials = Session().get_credentials() auth = AWS4Auth(region=us-west-1', service='es', refreshable_credentials=credentials)

O próximo exemplo usa a biblioteca Beautiful Soup para ajudar a criar um arquivo em massa de um diretório local de arquivos HTML. Usando o mesmo cliente do primeiro exemplo, você pode enviar o arquivo para a API _bulk para indexação. Você pode usar esse código como base para adicionar a funcionalidade de pesquisa em um site:

from bs4 import BeautifulSoup from opensearchpy import OpenSearch, RequestsHttpConnection from requests_aws4auth import AWS4Auth import boto3 import glob import json bulk_file = '' id = 1 # This loop iterates through all HTML files in the current directory and # indexes two things: the contents of the first h1 tag and all other text. for html_file in glob.glob('*.htm'): with open(html_file) as f: soup = BeautifulSoup(f, 'html.parser') title = soup.h1.string body = soup.get_text(" ", strip=True) # If get_text() is too noisy, you can do further processing on the string. index = { 'title': title, 'body': body, 'link': html_file } # If running this script on a website, you probably need to prepend the URL and path to html_file. # The action_and_metadata portion of the bulk file bulk_file += '{ "index" : { "_index" : "site", "_type" : "_doc", "_id" : "' + str(id) + '" } }\n' # The optional_document portion of the bulk file bulk_file += json.dumps(index) + '\n' id += 1 host = '' # For example, my-test-domain.us-east-1.es.amazonaws.com region = '' # e.g. us-west-1 service = 'es' credentials = boto3.Session().get_credentials() awsauth = AWS4Auth(credentials.access_key, credentials.secret_key, region, service) search = OpenSearch( hosts = [{'host': host, 'port': 443}], http_auth = awsauth, use_ssl = True, verify_certs = True, connection_class = RequestsHttpConnection ) search.bulk(bulk_file) print(search.search(q='some test query'))

Ruby

O primeiro exemplo usa o cliente do Ruby do Elasticsearch e o middleware Faraday para realizar a assinatura da solicitação. Observe que as versões mais recentes do cliente podem incluir verificações de licença ou versão que interrompem artificialmente a compatibilidade. Para obter a versão correta do cliente a ser usada, consulte Compatibilidade com clientes Elasticsearch. Este exemplo usa a versão recomendada 7.13.3.

No terminal, execute os comandos a seguir:

gem install elasticsearch -v 7.13.3 gem install faraday_middleware-aws-sigv4

Este código de exemplo cria um novo cliente, configura o middleware Faraday para assinar solicitações e indexa um único documento. Você deve fornecer valores para full_url_and_port e region.

require 'elasticsearch' require 'faraday_middleware/aws_sigv4' full_url_and_port = '' # e.g. https://my-domain.region.es.amazonaws.com:443 index = 'ruby-index' type = '_doc' id = '1' document = { year: 2007, title: '5 Centimeters per Second', info: { plot: 'Told in three interconnected segments, we follow a young man named Takaki through his life.', rating: 7.7 } } region = '' # e.g. us-west-1 service = 'es' client = Elasticsearch::Client.new(url: full_url_and_port) do |f| f.request :aws_sigv4, service: service, region: region, access_key_id: ENV['AWS_ACCESS_KEY_ID'], secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'], session_token: ENV['AWS_SESSION_TOKEN'] # optional end puts client.index index: index, type: type, id: id, body: document

Se as credenciais não funcionarem, exporte-as no terminal usando os seguintes comandos:

export AWS_ACCESS_KEY_ID="your-access-key" export AWS_SECRET_ACCESS_KEY="your-secret-key" export AWS_SESSION_TOKEN="your-session-token"

O próximo exemplo usa o AWS SDK for Ruby e as bibliotecas do Ruby padrão para enviar uma solicitação HTTP assinada. Assim como o primeiro exemplo, ele indexa um único documento. Você deve fornecer valores para host e região.

require 'aws-sdk-opensearchservice' host = '' # e.g. https://my-domain.region.es.amazonaws.com index = 'ruby-index' type = '_doc' id = '2' document = { year: 2007, title: '5 Centimeters per Second', info: { plot: 'Told in three interconnected segments, we follow a young man named Takaki through his life.', rating: 7.7 } } service = 'es' region = '' # e.g. us-west-1 signer = Aws::Sigv4::Signer.new( service: service, region: region, access_key_id: ENV['AWS_ACCESS_KEY_ID'], secret_access_key: ENV['AWS_SECRET_ACCESS_KEY'], session_token: ENV['AWS_SESSION_TOKEN'] ) signature = signer.sign_request( http_method: 'PUT', url: host + '/' + index + '/' + type + '/' + id, body: document.to_json ) uri = URI(host + '/' + index + '/' + type + '/' + id) Net::HTTP.start(uri.host, uri.port, :use_ssl => true) do |http| request = Net::HTTP::Put.new uri request.body = document.to_json request['Host'] = signature.headers['host'] request['X-Amz-Date'] = signature.headers['x-amz-date'] request['X-Amz-Security-Token'] = signature.headers['x-amz-security-token'] request['X-Amz-Content-Sha256']= signature.headers['x-amz-content-sha256'] request['Authorization'] = signature.headers['authorization'] request['Content-Type'] = 'application/json' response = http.request request puts response.body end

Node

Este exemplo usa o cliente opensearch-js para JavaScript para criar um índice e adicionar um único documento. Para assinar a solicitação, ele primeiro localiza as credenciais usando o módulo credential-provider-node da versão 3 do SDK para JavaScript em Node.js. Em seguida, ele chama aws4 para assinar a solicitação usando o Signature versão 4. Você deve fornecer um valor para host:

const { Client, Connection } = require("@opensearch-project/opensearch"); const { defaultProvider } = require("@aws-sdk/credential-provider-node"); const aws4 = require("aws4"); var host = '' // e.g. https://my-domain.region.es.amazonaws.com const createAwsConnector = (credentials, region) => { class AmazonConnection extends Connection { buildRequestObject(params) { const request = super.buildRequestObject(params); request.service = 'es'; request.region = region; request.headers = request.headers || {}; request.headers['host'] = request.hostname; return aws4.sign(request, credentials); } } return { Connection: AmazonConnection }; }; const getClient = async () => { const credentials = await defaultProvider()(); return new Client({ ...createAwsConnector(credentials, 'us-east-1'), node: host, }); } async function search() { // Initialize the client. var client = await getClient(); // Create an index. var index_name = "test-index"; var response = await client.indices.create({ index: index_name, }); console.log("Creating index:"); console.log(response.body); // Add a document to the index. var document = { "title": "Moneyball", "director": "Bennett Miller", "year": "2011" }; var response = await client.index({ index: index_name, body: document }); console.log(response.body); } search().catch(console.log);

Este exemplo semelhante usa aws-opensearch-connector em vez de aws4. Você deve fornecer um valor para host:

const { Client } = require("@opensearch-project/opensearch"); const { defaultProvider } = require("@aws-sdk/credential-provider-node"); const createAwsOpensearchConnector = require("aws-opensearch-connector"); var host = '' // e.g. https://my-domain.region.es.amazonaws.com const getClient = async () => { const awsCredentials = await defaultProvider()(); const connector = createAwsOpensearchConnector({ credentials: awsCredentials, region: process.env.AWS_REGION ?? 'us-east-1', getCredentials: function(cb) { return cb(); } }); return new Client({ ...connector, node: host, }); } async function search() { // Initialize the client. var client = await getClient(); // Create an index. var index_name = "test-index"; var response = await client.indices.create({ index: index_name, }); console.log("Creating index:"); console.log(response.body); // Add a document to the index. var document = { "title": "Moneyball", "director": "Bennett Miller", "year": "2011" }; var response = await client.index({ index: index_name, body: document }); console.log(response.body); } search().catch(console.log);

Se você não quiser usar opensearch-js, basta fazer solicitações HTTP padrão. Esta seção inclui exemplos das versões 2 e 3 do SDK para JavaScript in Node.js. Embora a versão 2 seja publicada como um único pacote, a versão 3 tem uma arquitetura modular com um pacote separado para cada serviço.

Version 3

Este exemplo usa a versão 3 do SDK para JavaScript in Node.js. No terminal, execute os comandos a seguir:

npm i @aws-sdk/protocol-http npm i @aws-sdk/credential-provider-node npm i @aws-sdk/signature-v4 npm i @aws-sdk/node-http-handler npm i @aws-crypto/sha256-browser

Este código de exemplo indexa um único documento. Você deve fornecer valores para region e domain.

const { HttpRequest} = require("@aws-sdk/protocol-http"); const { defaultProvider } = require("@aws-sdk/credential-provider-node"); const { SignatureV4 } = require("@aws-sdk/signature-v4"); const { NodeHttpHandler } = require("@aws-sdk/node-http-handler"); const { Sha256 } = require("@aws-crypto/sha256-browser"); var region = ''; // e.g. us-west-1 var domain = ''; // e.g. search-domain.region.es.amazonaws.com var index = 'node-test'; var type = '_doc'; var id = '1'; var json = { "title": "Moneyball", "director": "Bennett Miller", "year": "2011" }; indexDocument(json).then(() => process.exit()) async function indexDocument(document) { // Create the HTTP request var request = new HttpRequest({ body: JSON.stringify(document), headers: { 'Content-Type': 'application/json', 'host': domain }, hostname: domain, method: 'PUT', path: index + '/' + type + '/' + id }); // Sign the request var signer = new SignatureV4({ credentials: defaultProvider(), region: region, service: 'es', sha256: Sha256 }); var signedRequest = await signer.sign(request); // Send the request var client = new NodeHttpHandler(); var { response } = await client.handle(signedRequest) console.log(response.statusCode + ' ' + response.body.statusMessage); var responseBody = ''; await new Promise(() => { response.body.on('data', (chunk) => { responseBody += chunk; }); response.body.on('end', () => { console.log('Response body: ' + responseBody); }); }).catch((error) => { console.log('Error: ' + error); }); };
Version 2

Este exemplo usa a versão 2 do SDK para JavaScript in Node.js. No terminal, execute o comando a seguir:

npm install aws-sdk

Este código de exemplo indexa um único documento. Você deve fornecer valores para region e domain.

var AWS = require('aws-sdk'); var region = ''; // e.g. us-west-1 var domain = ''; // e.g. search-domain.region.es.amazonaws.com var index = 'node-test'; var type = '_doc'; var id = '1'; var json = { "title": "Moneyball", "director": "Bennett Miller", "year": "2011" } indexDocument(json); function indexDocument(document) { var endpoint = new AWS.Endpoint(domain); var request = new AWS.HttpRequest(endpoint, region); request.method = 'PUT'; request.path += index + '/' + type + '/' + id; request.body = JSON.stringify(document); request.headers['host'] = domain; request.headers['Content-Type'] = 'application/json'; request.headers['Content-Length'] = Buffer.byteLength(request.body); var credentials = new AWS.EnvironmentCredentials('AWS'); var signer = new AWS.Signers.V4(request, 'es'); signer.addAuthorization(credentials, new Date()); var client = new AWS.HttpClient(); return new Promise((resolve, reject) => { client.handleRequest( request, null, (response) => { const {statusCode, statusMessage, headers} = response; let body = ''; response.on('data', (chunk) => { body += chunk; }); response.on('end', () => { const data = {statusCode, statusMessage, headers}; if (body) { data.body = body; } resolve(data); console.log("Response body:" + body); }); }, (error) => { reject(error); console.log("Error:" + error) } ); }) };

Se as credenciais não funcionarem, exporte-as no terminal usando os seguintes comandos:

export AWS_ACCESS_KEY_ID="your-access-key" export AWS_SECRET_ACCESS_KEY="your-secret-key" export AWS_SESSION_TOKEN="your-session-token"

Go

Este exemplo usa o AWS SDK for Go e indexa um único documento. Você deve fornecer valores para domain e region.

package main import ( "fmt" "net/http" "strings" "time" "github.com/aws/aws-sdk-go/aws/credentials" "github.com/aws/aws-sdk-go/aws/signer/v4" ) func main() { // Basic information for the Amazon OpenSearch Service domain domain := "" // e.g. https://my-domain.region.es.amazonaws.com index := "my-index" id := "1" endpoint := domain + "/" + index + "/" + "_doc" + "/" + id region := "" // e.g. us-east-1 service := "es" // Sample JSON document to be included as the request body json := `{ "title": "Thor: Ragnarok", "director": "Taika Waititi", "year": "2017" }` body := strings.NewReader(json) // Get credentials from environment variables and create the Signature Version 4 signer credentials := credentials.NewEnvCredentials() signer := v4.NewSigner(credentials) // An HTTP client for sending the request client := &http.Client{} // Form the HTTP request req, err := http.NewRequest(http.MethodPut, endpoint, body) if err != nil { fmt.Print(err) } // You can probably infer Content-Type programmatically, but here, we just say that it's JSON req.Header.Add("Content-Type", "application/json") // Sign the request, send it, and print the response signer.Sign(req, body, service, region, time.Now()) resp, err := client.Do(req) if err != nil { fmt.Print(err) } fmt.Print(resp.Status + "\n") }

Se as credenciais não funcionarem, exporte-as no terminal usando os seguintes comandos:

export AWS_ACCESS_KEY_ID="your-access-key" export AWS_SECRET_ACCESS_KEY="your-secret-key" export AWS_SESSION_TOKEN="your-session-token"