在 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)
这对我有用
我正在尝试制作一个简单的 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)
这对我有用