[小ネタ] botocoreのAWS APIリクエストの署名プロセスのみを利用する
- 2017/02/02 修正 : PreserveAuthSession が BotocoreHTTPSession に変更されていました
はじめに
藤本です。
先日、Amazon Elasticsearch ServiceのIAM Roleによるアクセス制御というエントリを投稿し、IAMによるアクセス制御が設定されたAmazon Elasticsearch Serviceに対してAWSリクエストの署名をしたREST APIに発行するために、botoを利用しました。
どうせならAWS Lambda for Pythonで標準ライブラリとして持っているbotocoreの署名プロセスを利用したくなりました。botocoreのソースコードを追いかけ、署名のみを利用する事ができましたので今回はそちらのコードをご紹介します。
概要
AWSへのリクエストには署名が必要です。AWS公式ドキュメントに記載がある署名プロセスを使えば、AWSへのリクエストが可能です。ただ、実装するのは面倒です。
botocoreはawscliやboto3の土台となるLowレベルインタフェースです。こんなに信用できるライブラリはありません。活用して、楽しましょう。
今回はAWS Lambda for Pythonを使って、IAMによるアクセス制御が設定されたAmazon Elasticsearch Serviceにbotocoreライブラリを利用して任意のAPIに署名し、リクエストを発行します。
botocoreの署名プロセス
やることは4つです。
- Credentialsインスタンス生成
アクセスキー、シークレットキー、セッショントークン(必須ではない)を引数にCredentialsクラスのインスタンスを作成します。追加の小ネタですがAWS Lambdaではクレデンシャル情報は環境変数に設定されています。 - AWSRequestインスタンス生成
API発行したいURL、HTTPメソッド、リクエストボディ(必須ではない)を引数にAWSリクエストクラスのインスタンスを作成します。 - AWSリクエスト署名(署名バージョン4)
生成したCredentials、AWSRequestから署名バージョン4の署名ヘッダを作成します。現在は署名バージョン4が推奨されていますが、今後、新しい署名が出来た時にはそちらに移行することを推奨します。詳しくはauthモジュールを見てみてください - API発行
ソースコードは以下のようになります。
import os from botocore.awsrequest import AWSRequest from botocore.auth import SigV4Auth from botocore.endpoint import BotocoreHTTPSession from botocore.credentials import Credentials ### 1. Credential生成 credentials = Credentials(os.environ["AWS_ACCESS_KEY_ID"], os.environ["AWS_SECRET_ACCESS_KEY"], os.environ["AWS_SESSION_TOKEN"]) ### 2. AWSRequest生成 request = AWSRequest(method=<METHOD>, url=<URL>, data=<DATA>) ### 3. AWSリクエスト署名 SigV4Auth(credentials, <SERVICE_NAME>, os.environ["AWS_REGION"]).add_auth(request) ### 4. API発行 BotocoreHTTPSession().send(request.prepare())
やってみた
それでは上記の実装を利用して、IAMによるアクセス制御を行ったAmazon ESに対してAPIを発行します。
まずは署名なしで実行して拒否されることを確認します。
import urllib2 def lambda_handler(event, context): url = "http://search-**************************.ap-northeast-1.es.amazonaws.com/" try: urllib2.urlopen(url) except urllib2.URLError, e: return e.read()
はい、拒否されました。
次は、署名を行ったリクエストを発行します。
import os from botocore.awsrequest import AWSRequest from botocore.auth import SigV4Auth from botocore.endpoint import BotocoreHTTPSession from botocore.credentials import Credentials def lambda_handler(event, context): url = "http://search-**************************.ap-northeast-1.es.amazonaws.com/" credentials = Credentials(os.environ["AWS_ACCESS_KEY_ID"], os.environ["AWS_SECRET_ACCESS_KEY"], os.environ["AWS_SESSION_TOKEN"]) request = AWSRequest(method="GET", url=url) SigV4Auth(credentials, "es", os.environ["AWS_REGION"]).add_auth(request) response = BotocoreHTTPSession().send(request.prepare()) return response.json()
はい、成功しました。
次は、リクエストボディを指定したPOSTも行ってみます。
import os from botocore.awsrequest import AWSRequest from botocore.auth import SigV4Auth from botocore.endpoint import BotocoreHTTPSession from botocore.credentials import Credentials def lambda_handler(event, context): url = "http://search-**************************.ap-northeast-1.es.amazonaws.com/hoge/hoge/hoge" body = '{"name": "fujimoto"}' credentials = Credentials(os.environ["AWS_ACCESS_KEY_ID"], os.environ["AWS_SECRET_ACCESS_KEY"], os.environ["AWS_SESSION_TOKEN"]) request = AWSRequest(method="POST", url=url, data=body) SigV4Auth(credentials, "es", os.environ["AWS_REGION"]).add_auth(request) response = BotocoreHTTPSession().send(request.prepare()) return response.json()
うん。
まとめ
楽できるところは楽して、やるべきことに時間を使っていきましょう。