ECS FargateでGoogleCloudとのWorkoadIdentity連携の際のハマった話
はじめに
Google CloudとWorkload Identityで連携されているECS Fargateで動いているアプリからGoogle Drive APIを使おうとしたら以下のエラーが出ました。
Post \"https://www.googleapis.com/drive/v3/files/1234/copy?alt=json&prettyPrint=false&supportsAllDrives=true\": Get \"http://169.254.169.254/latest/meta-data/iam/security-credentials\": dial tcp 169.254.169.254:80: connect: invalid argument
別のWorkload Identityで連携されているLambdaではGoogle関連のAPIは使用できていることは確認できていました。
調査していくとこのエラーがECS Fargate特有の問題であることがわかりその問題を解決したので、それらについて解説していきます。
エラーの原因
エラー文に記載してあるhttp://169.254.169.254/latest/meta-data/iam/security-credentials
というのは、Workload Identity連携で使用している構成ファイルのcredential_source.urlです。
この169.254.169.254というものがEC2の認証情報を取得できるアドレスであり、EC2専用のものとなっています。
ECS Fargateのアドレスは公式ページの記載通り、169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
となっており、AWS側が作った環境変数があるので動的に変わります。
解決方法
エラーの原因を見て、Workload Identity連携で使用している構成ファイルのcredential_source.urlを169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
に変えればすぐに解決すると思いますが、そう簡単ではありません。
$AWS_CONTAINER_CREDENTIALS_RELATIVE_URIの環境変数が展開されないことと、たとえ展開できたとしても環境変数は動的に変わるのに対し構成ファイルは動的に変更できないため、この方法は使えません。
ですので、方法としては以下の2つのやり方があります。
- 方法1 : AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_SESSION_TOKENの環境変数に設定する方法(お勧めしない)
- 方法2 : Google Cloud SDKの外部アカウント認証を使用する方法
下で解説しますが、1の環境変数のやり方は、よくブログなどにもあり簡単に実装できるがあまりお勧めはしないです。(個人的に罠にハマってしまった)
方法1:AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_SESSION_TOKENの環境変数に設定する方法(お勧めしない)
GETで169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
にアクセスし、Fargateの認証情報を取得します。
その認証情報をアプリ側で環境変数AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY、AWS_SESSION_TOKENに設定するだけです。
具体的な実装方法については、以下の記事が参考になります:
この方法をお勧めしない理由
GETで169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
で取得したFargateの認証情報には6時間という期限があり、認証情報を環境変数に設定しているので、期限が過ぎるとAWSサービスへの接続ができなくなります。
6時間以内に認証情報を再取得すれば問題ありませんが、ECSを使用する場合、Google Cloud関連以外にも様々な機能を利用することが多く、常にGoogle Cloud APIを呼び出しているわけではないため、この方法はお勧めしません。
私はS3からのファイル取得を試みましたが、期限が切れて以下の様なエラーが出ました。IAMロールの権限周りは問題ないことが確認できていました。
*fmt.wrapError: failed to find resume cv file: failed to get file blob: failed to get object from S3: operation error S3: HeadObject, https response error StatusCode: 400, RequestID: test1234, HostID: test1234, api error BadRequest: Bad Request
方法2:Google Cloud SDKの外部アカウント認証を使用する方法
この方法は認証情報を取得しても環境変数に設定する必要がなく、期限切れの影響を受けないのでお勧めです。
GETでFargateの認証情報の取得までは同じですが、その後に取得した認証情報とWorkload Identityの構成ファイルを使用して、Google Cloud SDKのNewCredentialsで認証情報を自分で作成します。その認証情報を使用するGoogle CloudのAPIに第2引数として渡します。
実装については以下が参考になります:
まとめ
- ECS FargateでWorkload Identity連携を行うときは、構成ファイルがそのままだとできない
- ECS Fargateは認証情報のアドレスが動的に変わる
- 実装する時に環境変数に設定する方法は認証情報の期限切れの影響を受けるのでお勧めしない
- Google Cloud SDKの外部アカウント認証を使用する方法でやりましょう
ご不明点やご指摘、もっと良いやり方などありましたらコメントいただけると幸いです。
Discussion