ECS/Fargate 上のコンテナで使われている AWS Credentials を調べる

ECS/Fargate は便利ですが、この上で動作する ECS コンテナにどんな IAM 権限が割り当てられているかを調べるのは意外と面倒くさく、思わぬ落とし穴にハマりがちです。
Assume Role で動かしていたつもりが予期せず環境変数が設定されていてそちらが使われていた、みたいなことも、稀にあったりします。

ここでは簡単に、割り振られている権限を調べる方法について記述していきます。

## aws configure list
aws コマンドを用いて、今の動作環境でどのクレデンシャル情報が使用できるかを知ることができます。

```bash

$ aws configure list
      Name                    Value             Type    Location
      ----                    -----             ----    --------
   profile                     sada           manual    --profile
access_key     ****************SADA shared-credentials-file    
secret_key     ****************ASHI shared-credentials-file    
    region           ap-northeast-1      config-file    ~/.aws/config
```

- profile
- access_key
- secret_key
- region

それぞれについて、どこから情報が参照されているかが表示されます。

### Type

- env : 環境変数
- shared-credentials-file : 設定ファイル
- iam-role : インスタンスプロファイル
- container-role : ECS に割り当てられたコンテナRole

Fargate で ECS を動作させる際に、 TaskRole に割り当てられた権限がただしくコンテナに AssumeRole されているか確認する場合は、このコマンドの結果で `Type = container-role` がされていることを確認します。

## $AWS_CONTAINER_CREDENTIALS_RELATIVE_URI
ECS で動かすタスクに TaskRole を割り当てると、その上で動作するコンテナには自動的に `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` という環境変数が割り当てられます。

生の EC2 インスタンスでは `169.254.169.254` に curl などで接続することでクレデンシャル情報をふくむ様々なメタ情報を取得でき、 AssumeRole の仕組みをこちらを使って実現されています。

Docker などのように一つのホスト上に様々に文脈の異なるコンテナを動作させる際にも、同じようにコンテナ間で独立して AssumeRole できるような仕組みを提供する工夫として、 `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` のようなコンテナ固有の接続文字列が提供されているのだと思います。

<https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task-iam-roles.html>

> Amazon ECS エージェントは、ロール認証情報を含む追加のフィールドを使用してタスクを開始するペイロードメッセージを受け取ります。Amazon ECS エージェントは、一意のタスク認証情報 ID を識別トークンとして設定し、内部認証情報のキャッシュを更新して、タスクの識別トークンがペイロードで受け取ったロールの認証情報を指すようにします。Amazon ECS エージェントでは、次の相対 URI を使用して、このタスクに属するすべてのコンテナの Env オブジェクト (docker inspect container_id コマンドで利用可能) に AWS_CONTAINER_CREDENTIALS_RELATIVE_URI 環境変数を設定します。/credential_provider_version/credentials?id=task_credential_id

以下のような形で curl で呼び出すと、そのコンテナに割り当てられた TaskRole 権限に基づくクレデンシャル情報が返却されます。

```bash

$ curl --max-time 5 169.254.170.2$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI

{
  "RoleArn": "arn:aws:iam::1234567890:role/sada_masashi",
  "AccessKeyId" : "......",
  "SecretAccessKey" : "......",
  "Token" : "......",
  "Expiration": "2019-08-29T16:21:26Z"
}
```

こちらを見ることで、どの IAM Role の権限が割り当てられているか、実際のアクセスキー情報は何か、といったことを知ることができます。

また、 `AWS_CONTAINER_CREDENTIALS_RELATIVE_URI` に環境変数が設定されていない場合は基本的に TaskRole が設定されていないので、この環境変数の値をチェックするだけでも AssumeRole できる状況にあるのかどうかの判断ができます。

## ECSコンテナ上でのデバッグ
これらの仕組みを ECS で動かす Docker に仕込んでおくと、意図通りの権限が付与されず正しく動かない際のデバッグに便利です。

私が個人的に ECS を動かす際は、 Docker の entrypoint などに以下の仕組みを入れています。

```bash

echo "Verify aws configure list"
aws configure list

echo "Verify assume role [curl --max-time 5 169.254.170.2${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}]"
curl --max-time 5 169.254.170.2${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI} | jq -r ".RoleArn"
```

`169.254.170.2${AWS_CONTAINER_CREDENTIALS_RELATIVE_URI}` を叩いた結果は、すべてをログに残してしまうと Access Key などのクレデンシャル情報が丸見えになってしまうため、ここでは `RoleArn` だけをログに残すようにしています。

こういったちょっとした工夫で、期待した権限が付与されていない、もしくは予期せぬ権限が付与されている、という際の原因調査の手がかりになると思います。

## Appenditx. credentials provider chain
補足資料として、一応。
AWS の認証情報の解決は、使用しているライブラリなどにより順番が決まっています。
概ね優先順位はどのツールも共通ですが、ツールにより微妙な誤差があります。

### boto3 (python)
<https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html>

> 1. Passing credentials as parameters in the boto.client() method
> 2. Passing credentials as parameters when creating a Session object
> 3. Environment variables
> 4. Shared credential file (~/.aws/credentials)
> 5. AWS config file (~/.aws/config)
> 6. Assume Role provider
> 7. Boto2 config file (/etc/boto.cfg and ~/.boto)
> 8. Instance metadata service on an Amazon EC2 instance that has an IAM role configured.

python のプログラムの中で直接指定する方法が一番優先順位が高く、その後は

- 環境変数
- credential file / config file
- Assume Role
- Instance Profile

という順番は他の SDK などともおそらく共通です。 Boto2 のファイルの読み込みが Assume Role と Instance Profile の間に落ち着いたのは、どういう経緯があったのかは興味深いですが調べていません。

### Java SDK
<https://docs.aws.amazon.com/ja_jp/sdk-for-java/v2/developer-guide/credentials.html>

> 1. Java のシステムプロパティ
> 2. 環境変数
> 3. デフォルトの認証情報プロファイルファイル 
> 5. Amazon ECS コンテナの認証情報 
> 6. インスタンスプロファイル認証情報

### Go SDK
<https://docs.aws.amazon.com/ja_jp/sdk-for-go/v1/developer-guide/configuring-sdk.html>

> 1. Environment variables.
> 2. Shared credentials file.
> 3. If your application is running on an Amazon EC2 instance, IAM role for Amazon EC2.

記述は簡潔ですが、概ね挙動はほかの言語と同じです。