在 Python 中向 AWS Elasticsearch 发出签名的 HTTP 请求

Making a signed HTTP request to AWS Elasticsearch in Python

我正在尝试制作一个简单的 Python Lambda 来制作我们的 Elasticsearch 数据库的快照。这是通过 Elasticsearch's REST API 使用简单的 HTTP 请求完成的。

但是,对于 AWS,我必须签署这些请求。我有一种感觉,它可以通过 boto3 的低级客户端可能与 generate_presigned_url 一起实现,但我终究无法弄清楚如何正确调用此函数。例如,什么是有效的 ClientMethod?我已经尝试 ESHttpGet 但无济于事。

谁能指出我正确的方向?

请求库有几个 Python 扩展,可以为您执行 SigV4 签名。我用过this one,效果很好。

我最近发布了 requests-aws-sign,它为 Python 请求库提供 AWS V4 请求签名。

如果您查看 this code,您将了解如何使用 Botocore 生成 V4 请求签名。

编辑:显然此解决方法已 broken by Elastic.

为了做类似的事情,我挣扎了一段时间。目前 boto3 库不支持发出签名的 es 请求,但自从我 raised an issue 与他们一起它成为一个功能请求。

这是我同时使用上面提到的 DavidMuller's library 和 boto3 获取我的 STS 会话凭据所做的事情:

import boto3
from aws_requests_auth.aws_auth import AWSRequestsAuth
from elasticsearch import Elasticsearch, RequestsHttpConnection

session = boto3.session.Session()
credentials = session.get_credentials().get_frozen_credentials()

es_host = 'search-my-es-domain.eu-west-1.es.amazonaws.com'
awsauth = AWSRequestsAuth(
    aws_access_key=credentials.access_key,
    aws_secret_access_key=credentials.secret_key,
    aws_token=credentials.token,
    aws_host=es_host,
    aws_region=session.region_name,
    aws_service='es'
)

# use the requests connection_class and pass in our custom auth class
es = Elasticsearch(
    hosts=[{'host': es_host, 'port': 443}],
    http_auth=awsauth,
    use_ssl=True,
    verify_certs=True,
    connection_class=RequestsHttpConnection
)

print(es.info())

希望这可以节省一些时间。

虽然其他答案都很好,但我想消除对外部包的使用。显然,botocore 本身具有签署请求所需的所有功能,只需查看源代码即可。这就是我直接发送 AWS API 请求的最终结果(为了演示目的而硬编码):

      import boto3
      import botocore.credentials
      from botocore.awsrequest import AWSRequest
      from botocore.endpoint import URLLib3Session
      from botocore.auth import SigV4Auth

      params = '{"name": "hello"}'
      headers = {
        'Host': 'ram.ap-southeast-2.amazonaws.com',
      }
      request = AWSRequest(method="POST", url="https://ram.ap-southeast-2.amazonaws.com/createresourceshare", data=params, headers=headers)
      SigV4Auth(boto3.Session().get_credentials(), "ram", "ap-southeast-2").add_auth(request)    


      session = URLLib3Session()
      r = session.send(request.prepare())

为什么不直接使用请求?

import requests
headers = {'Content-Type': 'application/json',}
data = '{"director": "Burton, Tim", "genre": ["Comedy","Sci-Fi","R-rated"],"profit" : 98 , "year": 1996, "actor": ["Jack Nicholson","PierceBrosnan","Sarah Jessica Parker"], "title": "Mars Attacks!"}'
response = requests.post('https://search-your-awsendpoint.us-west-2.es.amazonaws.com/yourindex/_yourdoc/', headers=headers, data=data)

这对我有用