🐕

PythonでIAM認証を利用してAppSyncを非同期で実行しようとしたら意外と苦戦した話

2021/03/09に公開3

はじめに

タイトルの通りですがPythonでIAM認証を利用してAppSyncを非同期で実行したいという要件に挑んでみたら、意外と苦戦した(数日溶かした)のでその話をつらつら書きます。

解決策

フルスクラッチで処理を実装しました。
該当のコードは以下のリンクにおいてあります。

https://github.com/choimake/async-appsync

ざっくり説明します。

AWSでのIAM認証

AppSyncでのIAM認証を突破するためには、リクエストのヘッダーに以下の情報を追記してやる必要があります。

  • x-amz-date
  • x-amz-content-sha256
  • Authorization

Authorizationの値の生成するには、IAMのACCESS_KEY、SECRET_KEY、エンドポイント、リージョンなどの値を使って、あれこれ計算して求めてあげないといけません。

その処理を以下のコードで行っています。
https://github.com/choimake/async-appsync/blob/main/aws_iam_auth.py

計算ロジックは以下ドキュメントに載っていますので、詳しく知りたい方はこちらを参照。
https://docs.aws.amazon.com/ja_jp/general/latest/gr/sigv4_signing.html

非同期httpリクエスト

Pythonで非同期のhttpリクエストを送る際にはaiohttpを使いました。
以下のようなコードでリクエストが送信できます。

async with aiohttp.ClientSession() as session:
    async with session.post(
        self.endpoint,
        json=request_body,
        headers={**self.headers, **headers},
    ) as response:
        return await response.json()

ここのヘッダーの部分にIAM認証用のヘッダー情報を渡せば、AppSyncが叩くことができます。

実装は以下のコードで行っています。
https://github.com/choimake/async-appsync/blob/main/appsync_client.py

使い方

今回は、以下のような形でAppSyncを実行できるよう実装しました。

ACCESS_KEY_ID = "<aws_access_key_id>"
APPSYNC_ENDPOINT = "<app_sync_endpoint>"
SECRET_ACCESS_KEY = "<aws_secret_access_key>"

REGION = "ap-northeast-1"
SERVICE = "appsync"


auth = AWSIamAuth(
    access_key=ACCESS_KEY_ID,
    secret_key=SECRET_ACCESS_KEY,
    endpoint=APPSYNC_ENDPOINT,
    region=REGION,
    service=SERVICE,
)

client = AppSyncClient(endpoint=APPSYNC_ENDPOINT, auth=auth)


query = """
<query>
"""

loop = asyncio.get_event_loop()
response = loop.run_until_complete(client.execute_async(query=query))

print(response)

なぜ苦戦したのか

  • Pythonの非同期処理についてさっぱりわかってなかった
  • 既存のライブラリで要件を満たすものないか、ひたすら探して時間が溶けた
  • 英語わからん

Pythonの非同期処理についてさっぱりわかってなかった

Pythonの非同期処理ではasyncioという機能を使うのですが、このasyncioの(最低限の)使い方を把握するまでに時間がかかりました。

async?await?coroutine?
何それ、美味しいの? みたいな状態から、
以下の記事を読んではコード書き、エラーにぶち当たりながらなんとか非同期処理の書き方をふんわり理解しました。

既存のライブラリで要件を満たすものないか、ひたすら探して時間が溶けた

非同期処理の書き方がわかったので、次はライブラリの選定を試みました。
何かしらの便利なライブラリはあるだろうと思って試してみたところ、一番近いところまでいったやつで非同期でクエリ叩けるけどIAM認証が通らないという問題に当たりました。

僕が調べた限り、非同期でGraphQL実行するライブラリだと、httpリクエストを送る際にはaiohttpというライブラリが使われていたのですが、このaiohttpと連携してIAM認証が突破できているライブラリが中々見つかりません。できれば自分で実装したくなかったので、何かないかな〜とひたすら時間を溶かしながら調べていました。

で、結局見つけられませんでした。

英語わからん

Python、非同期、IAM認証、ライブラリ、これらのキーワードで今回の問題に該当する日本語記事は見つからず、ひたすら英語の記事をさまよっていました。が、元々技術的な意味で理解が浅い段階でよくわからん英語の記事みてもあんまり成果は出ません。

わからないことだらけの中で調べ物をして疲弊を重ね、次第に自分が何やってるかよくわからなくなってました。

泥沼からの脱出

結局自力だとどうにもならず、AWSサポートに問い合わせしました。
「PythonでIAM認証を利用してAppSyncを非同期で実行するのに便利なライブラリありませんか?」という旨での質問です。

フルスクラッチはどうしても嫌だったので、なんか便利な方法教えてくれないかな〜と淡い期待を持って返答を待ちました。

そしていただいた回答は

ヘッダに認証情報を追加する処理、自分で実装した方が良きでは?
【参考資料】
- https://docs.aws.amazon.com/ja_jp/general/latest/gr/sigv4_signing.html

というものでした。

...やっぱり?

そしてフルスクラッチへ

これまでの調査の過程で

  • 非同期処理がどう実装されているのか
  • IAMの認証がどう実装されているのか
  • 上の2つを組み合わせてどうやれば目的の処理を実装すれば良いか

知らず知らずのうちに上記の知見を得ていたので、実装自体は比較的スンナリ完了しました。

さいごに

おかげ様で、asyncio、IAM認証などについて多少なりとも知見を得ることができました。
が、正直しんどかったのと、Python慣れしてる方ならきっとスンナリできるんだろうなぁ...みたいな無力感みたいなものを今覚えています。

後の誰かが僕のような苦悩をせず、サクッとIAM認証を利用してAppSyncを非同期で実行してくれれば幸いです。

Discussion

mtmt

認証まわりはこれが使えたかもですねー
https://github.com/tedder/requests-aws4auth

choimakechoimake

コメントありがとうございます。

requests-aws4authも調べてはみたんですが、こいつは内部的にrequests.authのAuthBaseが使われていました。requestsライブラリでの認証には使えます。ただaiohttpにそのまま使えるライブラリではなかったので今回は使用を断念しました。

つらひ...

mtmt

なるほどです!
今回のコードを一部切り出して、requests-aws4auth のaiohttp 版をライブラリ化したら需要ありそうですね!