Dockerコンテナ内からAmplify clientを使ったAppSyncの接続に失敗したので調査した
最近はAmplifyをガッツリ使っています。GraphQLのコードを自動生成できるので、Amplify clientを利用すれば好きなバックエンドサーバーからAppSyncを呼び出しデータの読み書きを安全に実行可能です。
今回、ECSで動作させる前提だったDockerコンテナとそこで実行するNode.js(Typescript)のコードからAmplify clientを利用したところ、認証関連のエラーが出たので調査しました。
発生したエラー
UnauthorizedException: Unknown error {
recoverySuggestion: `If you're calling an Amplify-generated API, make sure to set the "authMode" in generateClient({ authMode: '...' }) to the backend authorization rule's auth provider ('apiKey', 'userPool', 'iam', 'oidc', 'lambda')`
}
AmplifyのgenerateClientで生成したクライアントインスタンスから.graphQLメソッドを呼び出したところで上記エラーが発生しました。なんか認証関連でミスっている気配が漂っています。
原因と解決策
結局、docker buildした際のCPUアーキテクチャと実行している環境(M1 mac mini)のCPUアーキテクチャの違いでAmplify Client内で利用しているSigV4署名が正しく動作しなかったことが原因でした。
ECSではamd64なので
docker build --platform linux/amd64
とオプションを指定していましたが、ローカルでデバッグする時はarm64なのでCPUアーキテクチャに差異があり、署名処理が正しく動作していなかったようです。ローカルデバッグ時にもarm64でビルドしたイメージを使うことで解決しました。
調査の流れ
ローカルではAWSにSSOログインをしています。そこでコンテナを実行するときは-v ~/.aws:/home/nextjs/.aws:roオプションを付与して認証情報をそのまま渡していました。
同じコンテナからSQSにアクセスをしていて、そちらは問題なく動作していたので認証情報関連ファイルの不足ではないだろう・・・という予想は立っていました。
となるとAmplify clientの初期化に関する部分です。ちなみにクライアントの初期化は以下のコード。
Amplify.configure(
{
API: {
GraphQL: {
endpoint: env.GRAPHQL_ENDPOINT,
region: env.AWS_REGION,
defaultAuthMode: 'iam',
},
},
},
{
Auth: {
credentialsProvider: {
getCredentialsAndIdentityId: async () => {
const credentials = await defaultProvider()()
return {
credentials,
}
},
clearCredentialsAndIdentityId: () => {
/* noop */
},
},
},
ssr: true
}
)
const amplifyClient = generateClient<any>({
authMode: 'iam',
})
コンテナではなくホストマシンから実行すると問題なく動作するので初期化に問題もなさそうです。
で、コンテナ起動時の警告を見てみるとちゃんと以下のように出ていました。
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
アーキテクチャが違うよ、と。あ、はい・・・。
結論とまとめ
ホストマシンの環境とコンテナの環境に差異があったためSigV4署名が正しく動作しなかったようでした。QEMUによるCPUエミュレーションが実行されると思いますが、そこで差異を吸収できなかったようですね。
Discussion