【AWS】ECS構築の基礎 −機密情報の参照−

#はじめに
Secrets Manager、SSMパラメータストア、S3に機密情報を環境変数として保存して、実行タスクから参照できるかどうか確かめてみました。

ECS初心者向けの記事を何本か書いていますので、どれか参考になれば幸いです。

https://qiita.com/suzuki0430/items/2745e9ca0ead8db982f9

https://qiita.com/suzuki0430/items/440daea590b22d1621c8

https://qiita.com/suzuki0430/items/6e4e7f513f982dadbf09

https://qiita.com/suzuki0430/items/7e0ad9903d26dd5f2529

https://qiita.com/suzuki0430/items/8b6e99669a8d4242a5ce

#AWS Secrets Manager
AWS Secrets Managerは、AWS内リソース、オンプレミス環境、またはサードパーティアプリケーションにアクセスするための各種機密情報の管理を、簡単にするAWSのサービスです。
Secrets Managerを利用することで、AWS CLI、API、SDKから機密情報へアクセスすることができます。
アプリケーション内に機密情報をハードコードする必要がなくなり、情報漏えいのリスクを減らすことができるようになります。

また、スケジュールに基づいた機密情報のローテーションに対応しており、古くなった機密情報の更新の手間を軽減することができます。
RDSやAuroraでは、機密情報のローテーションとデータベース接続情報のローテーションが統合されています。

#SSMパラメータストア
SSMパラメータストアはSecrets Manager登場前からある機密情報管理サービスです。
Secrets Managerとの違いを比較すると以下のようになります。

|  | Secrets Manager | SSMパラメータストア |
|:-:|:-:|:-:|
|  値段 |  高価 | 安価  |
| ローテーション  | 可能  | 不可  |
| KMS暗号化  | 必須  | 任意  |
| CloudFormationとの統合  | あり  | あり  |

#実装
ECSで機密情報を取り扱うにあたり、以下の3つの方法あります。
下2つの方法について試しに実装してみます。

- タスク定義にハードコーディング
- SSM/Secrets Managerで管理
- S3で管理

##準備
`ecsTaskExecutionRole`のパーミッションの修正、および新しくタスク定義とサービスの作成を行います。

まずは`ecsTaskExecutionRole`にSecrets ManagerやS3に保管したパラメータの取得に関するインラインポリシーを追加します。
`<account-id>`にはアカウントIDを記述します。
![スクリーンショット 2021-07-12 22.42.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/2b0e124b-0e72-53dd-b296-e1abcf3c4767.png)

```json
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "secretsmanager:GetSecretValue",
        "ssm:GetParameters",
        "kms:Decrypt"
      ],
      "Resource": [
        "arn:aws:secretsmanager:ap-northeast-1:<account-id>:secret:*",
        "arn:aws:ssm:ap-northeast-1:<account-id>:parameter/*",
        "arn:aws:kms:ap-northeast-1:<account-id>:*"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetObject"
      ],
      "Resource": [
        "arn:aws:s3:::ecs-course-<account-id>/environment-demo.env"
      ]
    },
    {
      "Effect": "Allow",
      "Action": [
        "s3:GetBucketLocation"
      ],
      "Resource": [
        "arn:aws:s3:::ecs-course-<account-id>"
      ]
    }
  ]
}
```

名前を記述してポリシーをアタッチします。
![スクリーンショット 2021-07-12 22.46.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/e889b381-9d0b-bdf5-efaa-c8b42bfa5c2b.png)


次に、タスク定義を新しく作成します。
EC2起動タイプを選択して、タスク定義名の記述とタスク実行ロールの選択を行います。
![スクリーンショット 2021-07-12 22.49.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/db0f3c79-97d0-33ed-cf2e-d8dc5750da46.png)

コンテナの追加を行います。
今回は`ubuntu:latest`のイメージを取得します。
![スクリーンショット 2021-07-12 22.51.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/a61b5920-b510-6129-c5e4-497aa8c53499.png)

エントリポイントを追加しタスク定義を作成します。
![スクリーンショット 2021-07-12 22.53.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/46aed465-5338-f28a-5e6a-efb83611ae71.png)

最後に、作成したタスク定義を元にサービスを新しく作成します。
![スクリーンショット 2021-07-12 22.55.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/1ed00899-a0c8-7eae-3c76-656a806f19a0.png)

##SSMパラメータストア
下準備が整ったので、SSMパラメータストアから実装を行っていきます。

Systems Managerのパラメーターストアを開いて、"パラメータの作成"を押します。
![スクリーンショット 2021-07-13 7.01.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/1cca0104-3325-ff36-5e63-6b96724eb149.png)

パラメータストアでは機密データの暗号化が任意です。
暗号化の可否でそれぞれ検証してみます。

暗号化を行わないケースでは、タイプに"文字列"を選択します。
![スクリーンショット 2021-07-13 7.03.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/4cf88747-4e44-c336-2b0f-4e0d7625f4ff.png)

暗号化を行うケースでは、タイプに"安全な文字列"を選択します。
![スクリーンショット 2021-07-13 7.06.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/4cd642f6-d90d-92cc-ced8-861019b0abdd.png)

作成したタスク定義を開いて、"新しいリビジョンの作成"を押します。
![スクリーンショット 2021-07-13 7.08.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/1e6fc46a-6ff8-18ec-811c-56112cfce134.png)

コンテナ名をクリックして、環境変数を追加します。
![スクリーンショット 2021-07-13 7.17.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/edd5dec1-1e94-6e61-8612-4f9d7b8fdf8f.png)

"値を追加してください"には、以下のコマンドで取得した`ARN`の中身を記述します。

```
$ aws ssm get-parameter --name plain-from-ssm
```

```
{
    "Parameter": {
        "Name": "plain-from-ssm",
        "Type": "String",
        "Value": "plain variable from SSM parameter store",
        "Version": 1,
        "LastModifiedDate": "2021-07-13T07:04:13.100000+09:00",
        "ARN": "arn:aws:ssm:<<region>>:<<accountid>>:parameter/<<param-name>>",
        "DataType": "text"
    }
}
```

`plain-ubuntu-service`に最新リビジョンのタスク定義を反映させます(`2(latest)`を選択します)。
![スクリーンショット 2021-07-13 7.21.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/f69d9857-ec01-95ba-ec8b-8bd32cdfd961.png)

EC2を開いて、`パブリック IPv4 DNS`をコピーします。
![スクリーンショット 2021-07-13 7.26.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/09c1f737-2f25-eefd-ed60-24f53a2621fd.png)

EC2にSSHログインします。

```
$ ssh -i ~/.ssh/udemysample.pem ec2-user@ec2-18-183-76-54.ap-northeast-1.compute.amazonaws.com
```
`ubuntu`の文字列を含むコンテナの中身を表示します。

```
[ec2-user@ip-172-16-0-156 ~]$ docker container ls | grep ubuntu
```

```
b08bf03cc6c8        ubuntu:latest                                                              "sleep infinity"    17 minutes ago      Up 17 minutes                                   ecs-td-plain-ubuntu-2-plain-ubuntu-ac97cdd3abf9dfc1db01
```
以下のコマンドで指定したコンテナの環境変数`env`を参照します。

```
[ec2-user@ip-172-16-0-156 ~]$ docker exec -it b08bf03cc6c8 "env"
```
`plain-from-ssm`と`encrypted-from-ssm`が確かに含まれています。

```
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=b08bf03cc6c8
TERM=xterm
ECS_CONTAINER_METADATA_URI=http://169.254.170.2/v3/f352209c-4de8-4214-9003-c66f42856952
ECS_CONTAINER_METADATA_URI_V4=http://169.254.170.2/v4/f352209c-4de8-4214-9003-c66f42856952
plain-from-ssm=plain variable from SSM parameter store
encrypted-from-ssm=encrypted variable from SSM Parameter store
AWS_EXECUTION_ENV=AWS_ECS_EC2
HOME=/root
```

##Secrets Manager
次に、Secrets Managerで実装を行います。

Secrets Managerを開いて、"新しいシークレットを保存する"を押すと以下の画面になります。
シークレットの種類を選択し、シークレットに格納するキーと値のペアを指定します。
![スクリーンショット 2021-07-13 22.15.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/fe9c34b5-2668-79a4-2682-24d4396de3a7.png)

シークレットの名前を記述します。
![スクリーンショット 2021-07-13 22.17.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/d62509f8-44c2-68cd-098c-b84112b553f5.png)

とりあえず自動ローテーションは無効にします。
![スクリーンショット 2021-07-13 22.18.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/d91034f7-db8d-34ca-f93a-7d98ff8f2ddd.png)

最後のステップでシークレットを取得するためのサンプルコードが表示されます。
"保存"を押します。
![スクリーンショット 2021-07-13 22.19.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/322f92b9-2f70-2f5e-fec1-e49b66505872.png)

作成したシークレットを開いて、シークレットのARNをコピーします。
作成したタスク定義を開いて、"新しいリビジョンの作成"を押します。
![スクリーンショット 2021-07-13 22.22.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/5bd7c392-6535-e799-69bf-f18dfcabd91c.png)

コンテナの編集から環境変数を追加します。
値にはコピーしたARNをペーストします。
![スクリーンショット 2021-07-13 22.30.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/46cc0971-7af0-c20a-a209-c760722c84c8.png)

サービスの設定で新しいリビジョン`3(latest)`を選択し、`新しいデプロイの強制`にチェックをつけて更新します。
![スクリーンショット 2021-07-13 22.35.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/13203829-bfbb-a553-2594-a9772293e60b.png)

SSMのときと同様に、SSHログインしてから`docker container ls | grep ubuntu`でコンテナIDを調べ、`docker exec -it [container-id] "env"`を実行します。
すると、以下のように追加した環境変数`secret-from-secrets-manager`が表示されます。

```
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=04cf089cf8e0
TERM=xterm
AWS_EXECUTION_ENV=AWS_ECS_EC2
ECS_CONTAINER_METADATA_URI=http://169.254.170.2/v3/541d9a24-900e-4c23-bc22-2e055c039ee6
ECS_CONTAINER_METADATA_URI_V4=http://169.254.170.2/v4/541d9a24-900e-4c23-bc22-2e055c039ee6
plain-from-ssm=plain variable from SSM parameter store
secret-from-secrets-manager={"key-from-secrets-manager":"This is the value from secrets manager"}
encrypted-from-ssm=encrypted variable from SSM Parameter store
HOME=/root
```

##Amazon S3
最後に、S3への環境変数の保存を行います。

以下のコマンドで、UserId、Account、Arnを確認します。

```
$ aws sts get-caller-identity
```
もし別のIAMユーザが設定されていたら、以下のコマンドを実行して、アクセスキーIDやシークレットアクセスキーを設定します。

```
$ aws configure
```
`s3 mb`コマンドで新しくバケットを作成します。
バケット名はグローバルで一意(世界に1つだけ)である必要があります。
今回はバケット名を`ecs-course-[Account]`としました。

```
$ aws s3 mb s3://[バケット名]
```
envファイルを新しく作成します。

```
$ vi environment-demo.env
```
envファイルに環境変数を記述して保存します。

```
variable1-from-s3=value1
variable2-from-s3=value2
```
作成したenvファイルをS3バケットにアップロードします。

```
$ aws s3 cp ./environment-demo.env s3://ecs-course-s3://[バケット名]/
```
以下のコマンドでバケット内のファイルを確認することができます。

```
$ aws s3 ls s3://[バケット名]
```

```
2021-07-18 22:17:24         50 environment-demo.env
```

ここでECSのタスク定義から新しいリビジョンを作成します。
![スクリーンショット 2021-07-18 22.22.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/6c6bfdf1-3380-48ec-40ec-a3196e1c5ff0.png)

”コンテナの定義”で環境ファイルを追加します。
ロケーションには`arn:aws:s3:::[バケット名]/environment-demo.env`を記述します。
![スクリーンショット 2021-07-18 22.25.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/6e1d7d0c-ecdd-9217-5363-82564480f0fc.png)

サービスの更新を行います。
![スクリーンショット 2021-07-18 22.28.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/e89b659c-bbef-daa1-d437-4ec09b32e805.png)

リビジョンの変更と新しいデプロイの強制にチェックします。
![スクリーンショット 2021-07-18 22.28.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/ae5f5a82-5c7f-df6b-019a-080da1d57942.png)

`docker container ls`でコンテナIDを控え、`docker exec -it [コンテナID] "env"`を実行します。
以下のように、envファイルの中身を確認することができます。

```
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=3a39f9eef85b
TERM=xterm
ECS_CONTAINER_METADATA_URI_V4=http://169.254.170.2/v4/46e249e6-1a76-4259-a97d-7e6f1e46e5c7
encrypted-from-ssm=encrypted variable from SSM Parameter store
plain-from-ssm=plain variable from SSM parameter store
secret-from-secrets-manager={"key-from-secrets-manager":"This is the value from secrets manager"}
variable1-from-s3=value1
variable2-from-s3=value2
AWS_EXECUTION_ENV=AWS_ECS_EC2
ECS_CONTAINER_METADATA_URI=http://169.254.170.2/v3/46e249e6-1a76-4259-a97d-7e6f1e46e5c7
HOME=/root
```

#Secrets Manager活用例 -プライベートレジストリのDockerイメージの使用-
Secrets Managerにクレデンシャルを保管して、プライベートレジストリのDockerイメージからタスクを起動することができます。

Secrets Manager開き、"その他のシークレット"を選択して、 usernameとpasswordを記述します。
![スクリーンショット 2021-07-19 21.04.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/912cf9b7-3a48-9434-05bd-6e57fe27c66f.png)

シークレットの名前を記述します。
![スクリーンショット 2021-07-19 21.06.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/78917abc-4f9e-6e38-ae6e-2c78be08dde3.png)

シークレット作成後、ARNをコピーします。
![スクリーンショット 2021-07-19 21.07.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/19f6cd48-7ef8-fb9c-2547-4a42ad66caa0.png)

新しくタスク定義を作成します。
![スクリーンショット 2021-07-19 21.09.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/3a6af127-43e4-2280-6c3a-8338a4cdab9c.png)

コンテナの追加でコンテナ名とイメージを記述します。
また、"プライベートレジストリの認証"にチェックをつけて、Secrets ManagerのARNをペーストします。
![スクリーンショット 2021-07-19 21.15.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/22ef3171-7099-dcd1-1385-4ae0fc5f69dd.png)

クラスターから新しいタスクを実行します。
![スクリーンショット 2021-07-19 21.16.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/cd6368d7-4dbe-51da-201f-245c03f53ed9.png)
![スクリーンショット 2021-07-19 21.18.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/e75c1349-6983-7aba-ba37-f7746d2dec65.png)

無事にタスクが実行されれば成功です。


#参考資料

https://www.udemy.com/course/aws-ecs-fargate/

https://dev.classmethod.jp/articles/about-secrets-manager/

https://qiita.com/mm-Genqiita/items/f93285a6058c64b39f23

https://qiita.com/tomoya_oka/items/a3dd44879eea0d1e3ef5

https://qiita.com/leomaro7/items/6afd607f2df39f714a8e

https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/developerguide/task_execution_IAM_role.html