SCSKの畑です。
弊社 AWS 環境で開発したアプリケーションをお客さんの AWS 環境に移行したのですが、移行当初は正常動作しなかったため、原因切り分けのためにタイトルの内容を試しました、という内容となります。
背景
お客さんの AWS 環境(AWSマネジメントコンソール)上から実行できるオペレーションが IAM 権限により限定されていた、という話は初回のエントリでも説明した通りなのですが、構築や実装に使用する変更関連の権限だけでなく、参照関連の権限についても付与対象が絞られていました。その1つとして「IAM ロール/ポリシーに割り当てられている IAM 権限が確認できない」という制限がありました。
元々このお客さんの AWS 環境は過去フェーズの案件も含めて数年前から使用していたのですが、当初は StepFunctions や Lambda を使用して Redshift にデータを投入するための ETL/ELT を実装するような内容がメインでした。元々バックエンド側の開発はフロントエンド側と比較して手慣れていたことや、エラー発生時にまず確認すべき CloudWatchLogs には参照権限があったため、この制限があってもそこまで問題を感じていませんでした。(もちろんこれが原因でエラーの切り分けに苦労したことも過去ありましたが・・)また、切り分けの結果 IAM 権限の問題が疑われる場合に設定内容の開示や追加設定をお願いすることは問題ありませんでした。
ただ、本案件事例にて実装したアプリケーションはこれまでと異なりフロントエンド/バックエンド両方の開発要素を含んでおり、かつ使用するサービスも多いため、これまでと比較してエラー発生時の原因切り分けや究明に手間取ってしまうことがありました。今振り返ると、今回取り上げる内容もその延長線上にあったのだなと思いますが・・
アプリケーション移行後の動作確認中にエラー発生
冒頭で記載した通り、弊社 AWS 環境で開発したアプリケーションをお客さんの AWS 環境に移行した後の動作確認中に、Cognito ユーザ認証後にクライアント側から AppSync API を叩いたタイミングで以下のようなエラーが発生しました。
{ message: "Unauthorized", 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')` }
先にエラー発生の原因についてネタばらししてしまうと、単純に Cognito ID プールの「認証されたロール」経由で付与される IAM ポリシーにおける appsync:graphql 権限の resource 句が正しく設定されていなかったことにより、結果として同 IAM 権限が不足していたことによるものでした。(本案件事例における AppSync の 認可方式については、このエントリの内容をご参照ください)
ところが、上記アプリケーション側のログからだけでは当初原因が掴めませんでした。
- message の内容は「Unauthorized」という単語のみなので原因究明には不十分であり、かつ Cognito ユーザ認証後に AppSync API を叩いている以上 Unauthorized と判定されている理由からしてそもそも良く分からない。
- recoverySuggestion の内容は Amplify のスキーマ定義における @auth 句の設定に関する内容と思われ、そもそも弊社環境においては同じ設定で問題なく動作している以上、設定変更しても本質的な解決に繋がらないことが推測されたのでこちらも情報としては参考にならないと判断。
では AppSync のログに何かしら出ているのでは?と考えて CloudWatchLogs を見てみたのですが・・
{ "logType": "RequestSummary", "requestId": "eafb8d66-f76c-4100-a836-e860dbca1808", "graphQLAPIId": "<graphQLAPIId>", "statusCode": 401, "latency": 63460261 }
bd4f8c67-6eb0-4c98-8c6a-432e6f9f57dd Response Headers: {x-amzn-ErrorType=UnauthorizedException}
以上のように、ほぼアプリケーション側のログと同じ程度の情報量しかなかったため、同じく参考にならず。
- Cognito の「認証されたロール」で指定している IAM ロール/ポリシーに appsync:graphql 権限が付与されていない
- Cognito でのユーザ認証後、「認証されたロール」で指定している IAM ロールが何らかの原因で正常に AssumeRole されていない
のどちらかではないかと考えました。理由は以下の通りです。
- 先述した通り Cognito ユーザ認証については成功しており、ユーザ未認証の状態で実行されたとは考えがたい。
- そもそもユーザ未認証ではアプリケーション上から AppSync API を実行する画面に遷移しない。
- つまり考えられる可能性は、正常に「認証されたロール」で指定している IAM ロールが AssumeRole されなかったか、AssumeRole されたものの IAM 権限が不足しているか、のどちらかと推測。
- バックエンド用の Lambda からは AppSync API が正常に実行できていることから、AppSync 側の設定については一旦考慮外。
- 厳密には個々のスキーマ/リゾルバ/関数定義などが誤っている可能性はあるが、そもそも AppSync API の実行すらできないことを鑑みるとそれ以前の問題と判断。
この内、1.については先述した「IAM ロール/ポリシーに割り当てられている IAM 権限が確認できない」制限があることから直接 IAM ロール/ポリシーの設定確認ができなかったため、弊社環境で再現試験を実施したところ同じようなエラーメッセージが発生することを確認できました。2.については以下の理由より可能性は低いと考えられたため、1.の可能性が濃厚という結論になりました。
- 「認証されたロール」に指定した IAM ロールが設定されていることがお客さんの AWS 環境上で確認できている。
- 上記を踏まえると、正常に AssumeRole できない原因は IAM ロールに設定するCognito ID プールとの信頼関係設定程度しか思い付かなかったが、弊社環境で意図的に設定を間違えた場合は以下のように別のエラーが発生することが分かったためこの可能性はなし。とすると他の可能性が思い浮かばない。。
InvalidIdentityPoolConfigurationException: Invalid identity pool configuration. Check assigned IAM roles for this pool.
ただ、よりにもよってこの事象が発生したタイミングが、お客さんから「認証されたロール」に設定している IAM ロール/ポリシーの設定(修正)が完了したと連絡を受けた直後だったのですよね・・。経緯として、元々こちらから設定依頼した IAM ロール/ポリシーの内容が一部不正確だったため修正を依頼していたのですが、その対応をお客さんが完了したと言ってきた側から設定内容の開示や再修正を再度お願いするというのは、お客さんに対して「エラー出てるんだけど本当にできてるんですよね?大丈夫ですか?」みたいなニュアンスを言外に含んでいると捉えられかねないと憂慮してしまい・・内部で相談したところ、念のためもう少し裏取りしてから打診しようという方向性になりました。
具体的には、2.の可能性の有無(=AssumeRole 自体が正常に動作しているかどうか)までをお客さん AWS 環境で確認してから、対象 IAM ロール/ポリシーの設定内容の開示や再修正をお願いすることにしました。お客さん AWS 環境上の権限不足で確認できない場合は一旦諦める他ないですが、打診する前にできることはやっておこうということで。
本題
ということでようやく本題です。調べたところ、AssumeRole のログは CloudTrail に出力されるとのことで、お客さん AWS 環境で CloudTrail を開いたところ問題なく中身が見られたので、ログの確認を進めました。
具体的には、上記 URL における「AssumeRoleWithWebIdentity」イベントが該当します。よって、お客さん AWS 環境のアプリケーションから意図的にエラーを発生させた上で、同時刻前後に「AssumeRoleWithWebIdentity」イベントが発生しているかどうかを確認しました。
その結果、以下スクリーンショットのような「AssumeRoleWithWebIdentity」イベントが発生しているログが確認できました。また、「参照されたリソース」の黒塗りしているリソース名が、Cognito ID プールの「認証されたロール」に設定している IAM ロールと同一でした。よって、アプリケーションから AppSync API を実行しようとした時に、想定通りの IAM ロールが Cognito ID プール 経由で AssumeRole されていることが確認できました。
よって、先述の方針通り Cognito ID プールの「認証されたロール」に設定している IAM ロール/ポリシーの設定が誤っている可能性が高いと判断しお客さんに設定内容の開示及びを依頼したところ、冒頭に記載した通り appsync:graphql 権限の resource 句が正しく設定されていなかったことが判明したため、再修正を依頼して無事クローズと相成りました。
ちなみに、JSON 形式の CloudTrail ログも同画面から以下のように確認できます。今回確認したかった観点は上記画面から確認できたため、詳細については本エントリでは省略します。
{ "eventVersion": "1.08", "userIdentity": { "type": "WebIdentityUser", "principalId": "cognito-identity.amazonaws.com:<COGNITO_APP_ID>:<COGNITO_APP_USR_NAME>", "userName": "<COGNITO_APP_USR_NAME>", "identityProvider": "cognito-identity.amazonaws.com" }, "eventTime": "2025-02-24T14:15:12Z", "eventSource": "sts.amazonaws.com", "eventName": "AssumeRoleWithWebIdentity", "awsRegion": "ap-northeast-1", "sourceIPAddress": "<SOURCE_IP_ADDRESS>", "userAgent": "aws-internal/3 aws-sdk-java/1.12.780 Linux/4.14.355-275.582.amzn2.x86_64 OpenJDK_64-Bit_Server_VM/25.432-b06 java/1.8.0_432 kotlin/1.3.72 vendor/Oracle_Corporation cfg/retry-mode/standard cfg/auth-source#imds", "requestParameters": { "roleArn": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/service-role/<COGNITO_ROLE_NAME>", "roleSessionName": "CognitoIdentityCredentials" }, "responseElements": { "credentials": { "accessKeyId": "ASIA5NBR5LH3GUDX65GT", "sessionToken": "<ENCODED_SESSION_ROKEN_BLOB>", "expiration": "Feb 24, 2025, 3:15:12 PM" }, "subjectFromWebIdentityToken": "<COGNITO_APP_USR_NAME>", "assumedRoleUser": { "assumedRoleId": "AROA5NBR5LH3HYAQNDM6Y:CognitoIdentityCredentials", "arn": "arn:aws:sts::<AWS_ACCOUNT_ID>:assumed-role/<COGNITO_ROLE_NAME>/CognitoIdentityCredentials" }, "provider": "cognito-identity.amazonaws.com", "audience": "<COGNITO_APP_ID>" }, "additionalEventData": { "identityProviderConnectionVerificationMethod": "IAMTrustStore", "RequestDetails": { "endpointType": "regional", "awsServingRegion": "ap-northeast-1" } }, "requestID": "ef3bb0c4-ed23-472e-9a93-3377cd9928dc", "eventID": "93c286a7-1732-4428-93e5-15875fa6b2a3", "readOnly": true, "resources": [ { "accountId": "<AWS_ACCOUNT_ID>", "type": "AWS::IAM::Role", "ARN": "arn:aws:iam::<AWS_ACCOUNT_ID>:role/service-role/<COGNITO_ROLE_NAME>" } ], "eventType": "AwsApiCall", "managementEvent": true, "recipientAccountId": "<AWS_ACCOUNT_ID>", "eventCategory": "Management", "tlsDetails": { "tlsVersion": "TLSv1.3", "cipherSuite": "TLS_AES_128_GCM_SHA256", "clientProvidedHostHeader": "sts.ap-northeast-1.amazonaws.com" } }
まとめ
つらつら書いていたら、本題より前段の話題である背景やエラー発生時の原因切り分けの内容の方が長くなってしまいました。すみません。ただ、お客さんの AWS 環境へのアプリケーション移行に関しては、こちらのエントリで記載した内容も含め最終的に正常に稼働させるまで結構な労力を要したので、備忘がてら忘れない内にまとめておきたかったんですよね。後、具体的な解決策について記載するのはもちろん、そこに至るまでの思考のプロセスも合わせてまとめておいた方が、少なくとも自分にとってはより有意義な記録になるというのもあります。
いずれにせよ、本記事がどなたかの役に立てば幸いです。