ECS (Fargate) 上で動くPHPアプリからAWS SDKを使ったとき、metadata endpointへアクセスできない問題を踏んだのでそのことを残しておく。
ざっくり要点まとめ
- AWS SDKなどがしれっと見に行く
metadata endpoint
にはEC2用とECS用で分かれており、AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
という環境変数がAWS SDKから見えなければAWS SDKはEC2用metadata endpointに繋ぎに行く - php-fpmはデフォルト設定ではサーバ環境変数を引き継がない (exportしない)
- 上記の仕様が重なって、PHPアプリをECS上で動かすときは明示的にその環境変数をexportしてあげないとEC2用metadata endpointに繋ぎに行って死ぬ
背景
AWSをセキュアに使うために、権限管理は出来るだけアクセストークンを発行せずにTask Role (EC2でいうInstance Profile) を使いたい。
これまでアクセストークン ( AWS_ACCESS_KEY_ID
AWS_SECRET_ACCESS_KEY
) を使って権限管理を行っていたWebシステムを、Task Roleによる権限管理に移行しようとしたところPHPアプリからAWS SDKを叩いてるところでmetadata endpointへのアクセスができなくなって死んだ。
この原因で死んだときにAWS SDKが吐くエラー:
Error retrieving credentials from the instance profile metadata service. (cURL error 7: (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for http://169.254.169.254/latest/meta-data/iam/security-credentials/)
調べたことメモ
FargateにおけるIAM Role
- Task Execution Role … コンテナを起動する人に与えられるロール
- Task Role … コンテナそのものに与えられるロール
AWS SDKがどうやってTask Roleを使ってAWS APIを叩くか
AWS SDKがAWS APIを叩くとき、アクセストークン ( AWS_ACCESS_KEY_ID
と AWS_SECRET_ACCESS_KEY
) が必要。それが環境変数などに直接設定されている場合はそれを使うし、されていなければmetadata serviceへ内部的に問い合わせを行ってそこで払い出されたtemporaryなアクセストークンを使ってAPIを叩く振る舞いをする。
AWS SDKが使うアクセストークンを解決する実装は下記のあたり
読み進めると、環境変数に AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
か AWS_CONTAINER_CREDENTIALS_FULL_URI
が設定されている場合はECS用のmetadata endpointに問い合わせるっぽいことが読み取れる
php-fpmはデフォルト設定では環境変数を引き継がない
php-fpmのデフォルト設定では clear_env = true
となっているのでサーバ環境変数を引き継がない。そのため、phpアプリから AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
環境変数を読めない。
clear_env bool FPM ワーカー内の環境をクリアする。 任意の環境変数が FPM ワーカープロセスに到達してしまうことを防ぐために、 ワーカー内の環境をいったんクリアしてから、このプールの設定で指定された環境変数を追加します。 デフォルト値: Yes
上記から、AWS SDKは自分がECS上で実行されていることを認識できず、ECS用metadata endpointではなくEC2用metadata endpointにつなぎに行ってしまって failed to connect
となって死ぬことが分かった。
Aws\Exception\CredentialsException: Error retrieving credentials from the instance profile metadata service. (cURL error 7: (see https://curl.haxx.se/libcurl/c/libcurl-errors.html) for http://169.254.169.254/latest/meta-data/iam/security-credentials/)
対処方法
- 設定を
clear_env = false
とする方法 - 設定に
ENV
パラメータを書いて明示的に引き継がせる方法
1. 設定を clear_env = false
とする方法
デフォルト true
となっている設定を false
へ変更すると、サーバに設定されている環境変数が引き継がれるのでAWS SDK (PHPアプリ) からAWS_CONTAINER_CREDENTIALS_RELATIVE_URI
環境変数が見えるようになる。
www.conf:
- ;clear_env = no + clear_env = no
2. 設定に ENV
パラメータを書いて明示的に引き継がせる方法
clear_env = true
のままでも下記のような設定を記載すると AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
環境変数だけを引き継がせる (exportする) ことができる。
www.conf:
env[AWS_CONTAINER_CREDENTIALS_RELATIVE_URI] = $AWS_CONTAINER_CREDENTIALS_RELATIVE_URI`