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 com o Java é usar AwsSdk2Transport, introduzido no opensearch-java versão 2.1.0. O exemplo a seguir cria um índice, grava um documento e exclui o índice. Você deve fornecer valores para region e host.

package com.amazonaws.samples; import java.io.IOException; import java.util.HashMap; import java.util.Map; import org.opensearch.client.opensearch.OpenSearchClient; import org.opensearch.client.opensearch.core.IndexRequest; import org.opensearch.client.opensearch.indices.CreateIndexRequest; import org.opensearch.client.opensearch.indices.DeleteIndexRequest; import org.opensearch.client.transport.aws.AwsSdk2Transport; import org.opensearch.client.transport.aws.AwsSdk2TransportOptions; import software.amazon.awssdk.http.SdkHttpClient; import software.amazon.awssdk.http.apache.ApacheHttpClient; import software.amazon.awssdk.regions.Region; public class IndexDocument { private static final String host = "search-....us-west-2.es.amazonaws.com"; private static Region region = Region.US_WEST_2; public static void main(String[] args) throws IOException, InterruptedException { SdkHttpClient httpClient = ApacheHttpClient.builder().build(); try { OpenSearchClient client = new OpenSearchClient( new AwsSdk2Transport( httpClient, host, region, AwsSdk2TransportOptions.builder().build())); // create the index String index = "sample-index"; CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder().index(index).build(); client.indices().create(createIndexRequest); // index data Map<String, Object> document = new HashMap<>(); document.put("firstName", "Michael"); document.put("lastName", "Douglas"); IndexRequest documentIndexRequest = new IndexRequest.Builder() .index(index) .id("2") .document(document) .build(); client.index(documentIndexRequest); // delete the index DeleteIndexRequest deleteRequest = new DeleteIndexRequest.Builder().index(index).build(); client.indices().delete(deleteRequest); } finally { httpClient.close(); } } }

Outras alternativas incluem o uso de um AWS Request Signing Interceptor e/ou o cliente REST de alto nível. Veja este exemplo

dica

Este exemplo usa 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"