[AWS Secrets Manager] ECS デプロイ時に気をつけたい秘匿情報の扱いについて

# はじめに
ECSタスクのコンテナからRDSなどの外部ストレージにアクセスする場合、`AWS Secrets Manager`　のような秘匿情報を管理するマネージドサービスを使用して、セキュリティ面に配慮する必要があります。
チームの方針によってはプロダクション環境以外では`AWS Secrets Manager`で管理することの優先度を下げる場合があるかと思いますが、基本的には`AWS Secrets Manager`経由で秘匿情報を参照するのが望ましいと思います。

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/specifying-sensitive-data.html

## ECSから`AWS Secrets Manager`にアクセスする
ECSから`AWS Secrets Manager`にアクセスする際に少し躓くポイントがあったので、ここで整理をしてみます。

### VCPエンドポイントと紐付ける
あらかじめVPCエンドポイントを作成しておき、デプロイするECSのサービスを作成したVPC内に所属させる必要があります。

https://docs.aws.amazon.com/ja_jp/vpc/latest/privatelink/vpc-endpoints.html

### IAMポリシーを適切に付与する
秘匿情報が保管されている`AWS Secrets Manager`と、暗号化された秘匿情報を複合化するための`AWS Key Management Service`にアクセスするための権限をECSタスク実行ロールに付与する必要があります。

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "kms:Decrypt"
      ],
      "Resource": [
        "arn:aws:secretsmanager:<region>:<aws_account_id>:secret:<secret_name>",
        "arn:aws:kms:<region>:<aws_account_id>:key/<key_id>"
      ]
    }
  ]
}
```

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_execution_IAM_role.html#task-execution-secrets


#### 発生していたエラー
ECSのタスク実行ロールに上記の権限を付与できていないと、タスク実行時に以下のようなエラーが出てしまいECSクラスターにデプロイすることができません。

```
ResourceInitializationError: unable to pull secrets or registry auth: execution resource retrieval failed: unable to retrieve secret from asm: service call has been retried 5 time(s): failed to fetch secret
arn:aws:secretsmanager:ap-northeast-1:xxxxxxxx:secret:prod/project/xxxxxxxxxx from secrets manager: RequestCanceled: request context canceled caused by: context deadline exceeded
```

## (補足)`AWS Key Management Service`
データの暗号化、複合化に関するマネージドサービスです。

https://aws.amazon.com/jp/kms/

# タスク定義について

```yaml:task-definition.json
{
  "containerDefinitions": [
    {
      "name": "server",
      "image": "xxxxxxxxxx.dkr.ecr.ap-northeast-1.amazonaws.com/grpc-server",
      "cpu": 0,
      "memoryReservation": 300,
      "portMappings": [
        {
          "containerPort": 50051,
          "hostPort": 50051,
          "protocol": "tcp"
        }
      ],
      "essential": true,
      "secrets": [
        {
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-1:xxxxxxxxxx:secret:prod/project/xxxxxxxxx:host::",
          "name": "DB_HOST"
        },
        {
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-1:xxxxxxxxxx:secret:prod/project/xxxxxxxxx:dbClusterIdentifier::",
          "name": "DB_NAME"
        },
        {
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-1:xxxxxxxxxx:secret:prod/project/xxxxxxxxx:password::",
          "name": "DB_PASS"
        },
        {
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-1:xxxxxxxxxx:secret:prod/project/xxxxxxxxx:port::",
          "name": "DB_PORT"
        },
        {
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-1:xxxxxxxxxx:secret:prod/project/xxxxxxxxx:username::",
          "name": "DB_USER"
        }
      ],
      "mountPoints": [],
      "volumesFrom": [],
      "logConfiguration": {
        "logDriver": "awslogs",
        "options": {
          "awslogs-group": "/ecs/grpc-server-task",
          "awslogs-region": "ap-northeast-1",
          "awslogs-stream-prefix": "ecs"
        }
      }
    }
  ],
  "family": "grpc-server-task",
  "taskRoleArn": "arn:aws:iam::xxxxxxxxxx:role/ecsTaskExecutionRole",
  "executionRoleArn": "arn:aws:iam::xxxxxxxxxx:role/ecsTaskExecutionRole",
  "networkMode": "awsvpc",
  "volumes": [],
  "placementConstraints": [],
  "runtimePlatform": {
    "operatingSystemFamily": "LINUX"
  },
  "requiresCompatibilities": ["FARGATE"],
  "cpu": "256",
  "memory": "512",
  "tags": []
}
```

## `containerDefinitions`の`secrets`フィールドを使用する

```yaml
      "secrets": [
        {
          "valueFrom": "arn:aws:secretsmanager:ap-northeast-1:xxxxxxxxxx:secret:prod/project/xxxxxxxx:host::",
          "name": "DB_HOST"
        },
```
`AWS Secrets Manager`に登録した秘匿情報を参照するためには、上記のようにsecretsフィールドに環境変数を定義する必要があります。
AWSコンソールから`AWS Secrets Manager`→`シークレット`とアクセスし、「シークレットのarn」を取得しましょう。

### シークレットの値を参照するための記法
シークレットに定義された各変数を参照するためには以下のように記載する必要があります。

`シークレットのarn:キー名::`

上記の記法でtask定義を行わないとシークレットの値を正しく参照できないので注意しましょう。

## **RoleArnについて

```yaml
  "taskRoleArn": "arn:aws:iam::xxxxxxxxxx:role/ecsTaskExecutionRole",
  "executionRoleArn": "arn:aws:iam::xxxxxxxxxx:role/ecsTaskExecutionRole",
```
`AWS Secrets Manager`および`AWS Key Management Service`へのアクセス権限を付与したtaskRoleを使用するようにtask定義ファイルの中できちんと明示しましょう。

# 終わりに
今回の記事に書かれたポイントを押さえると、秘匿情報を`AWS Secrets Manager`から取得した上でコンテナを起動することができると思います。
ECSへのtask定義ファイルに秘匿情報をベタ書きして運用をするようなことがないように注意しましょう。

# 参考記事
https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/specifying-sensitive-data-tutorial.html

https://dev.classmethod.jp/articles/tsnote-ecs-resourceinitializationerror/