ECSでごっつ簡単に機密情報を環境変数に展開できるようになりました!

477件のシェア(そこそこ話題の記事)

先日のアップデートで、ECSコンテナ内への機密情報の受け渡しが非常に簡単になりました。

従来は機密情報の展開にアプリケーション側での処理が必要だったものが、マネージドな仕組みで実現可能となっているので、既存ECSユーザーには必見のアップデートとなっております。

参考:AWS Launches Secrets Support for Amazon Elastic Container Service

あんなことやこんなこと!?

  ( ゚д゚) ガタッ
  /   ヾ
__L| / ̄ ̄ ̄/_
  \/   /

従来の方法の面倒くささ(自前で機密情報を展開していた場合)

コンテナにおいて、同一コンテナイメージをあらゆる環境(開発環境〜本番環境まで)で利用するために、環境に依存する情報を環境変数で定義することは必須です。ただ、センシティブな情報(DBへの接続情報など)を平文で渡すのはリスクが伴います。

ECSでコンテナに対して機密情報を渡すとき、パラメータストアのSecureStringを使うことがベストプラクティスでした。主な利用手順は以下の通り。

  1. Parameter Storeに秘匿情報を登録
  2. ECSのタスク定義で環境変数にParameter Storeのパスを設定
  3. タスク定義にSSMへのアクセス権限を付与
  4. コンテナ起動時のエントリポイントでシェルスクリプトを指定
  5. シェル内のaws cliでパラメータを復号化し、環境変数に設定

この場合、4〜5において、アプリケーション側で復号化処理が必要になります。よくある実装としては、コンテナ起動時に以下のシェルスクリプトでパラメータストアから秘密情報を取得し、環境変数に設定するという方法があります。

#!/bin/bash

export CONNECTION_STRING=$(aws ssm get-parameters --name $(OPTION_CONNECTION_STRING_PATH) --with-decryption | jq -r '.Parameters[].Value')

これをアプリケーション側で都度実装するのも手間ですし、awsアクセスのためのライブラリ(上の場合aws cli)も別途必要となるので、ただでさえイメージの最小化が求められるコンテナにおいては、つらいものがありました。

今回のアップデートによる素晴らしさ

今回のアップデートでは、このアプリケーション側での復号処理が不要となります。具体的には、ECSのタスクを定義する時、環境変数にパラメータストアから取得するSecureStringのパスを指定するだけで、タスク起動時にその内容を自動的に復号して、環境変数に設定してくれます。

これぞマネージド。ありがたい。

設定方法

というわけで、具体的な設定方法を解説していきます。以下手順ですが、既にECS(on EC2)で、タスク定義やサービスを起動したことがある方向けとなっておりますので、設定差分のみの提示となっていること、ご了承ください。

設定の前提条件

ECSコンテナバージョンが1.22.0以上であること

今回のアップデートでは、新しい形式のタスク定義(環境変数をパラメータストアのパスから取得)が必要になるため、ECSエージェントの最新化が必須です。

ECS コンテナエージェンが1.22.0以上が必須のため、下記を参照にECSエージェントの更新を行いましょう。

ちなみに、最新版のECS-optimized Amazon Linux AMIは、こちらのページで確認可能です。

(重要)Fargateは対応していない

ECSエージェント側でパラメータの取得と復元を実行する関係上、この機能はFargateでは利用できません。全国のFargateファンにはこれは悲しいですね。その内アップデートされるかと思いますので、首を長くして待ちましょう。

パラメータストアーへの、秘密情報登録

Systems Managerのコンソールを開きます。

AWS Systems Manager

左側メニューから「パラメータストア」→「パラメータの作成」をクリック。パラメータの詳細画面が表示されるので、パラメータのキー名と値を入力します。タイプには「安全な文字列」を選択し、KMSの主要なソースとKMSキーIDを指定します。

そうすると、SecureStringなパラメータが登録されます。hamako-favorite-wordをキーとした、機密情報が登録されました。

ECSのタスクで利用するIAMのロールへのポリシー追加

ECSのサービス起動時にタスクがSSMとKMSを利用するので、そのためのポリシーをタスク実行ロールに追加します。

  • ssm:GetParameters
    • 必須。パラメータストアからのパラメータ取得に利用
  • secretsmanager:GetSecretValue
    • 任意。パラメータストアの値が、Secrets Managerを参照している場合に必要
  • kms:Decrypt
    • 任意。カスタムKMSキーを使用している場合に必要

以下に、追加するポリシーの例を示します。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "ssm:GetParameters",
        "secretsmanager:GetSecretValue",
        "kms:Decrypt"
      ],
      "Resource": "*"
    }
  ]
}

ECSタスク定義への登録

次に、ECS側でタスク定義を設定していきます。ECSメニューのタスク定義新規作成より、起動タイプ「EC2」を選択。

任意のタスク定義名を指定したのち、コンテナ定義を追加。コンテナ名とイメージを指定して下に行くと、環境変数の入力箇所が、従来と変わってます。一瞬ビックリした。

  • Key
    • コンテナ内で参照する環境変数名を指定
  • Value or ValueFrom
    • 従来の文字列 or パラメータストア から取得かを選択
    • パラメータストアからの取得の場合ValueFromを指定
    • ValueFromの場合は、パラメータストアの名前を指定。別リージョンのキーやカスタムキーを利用する場合は、ARNの指定が必要

タスク定義の変更点はこれだけです。新しく定義したタスク定義を利用し、ECSサービスを更新してタスクを起動してください。

設定後の動作確認

タスク起動後、ECSインスタンスからdocker execで、コンテナの中に入り展開された環境変数を確認します。無事、環境変数HAMADA_SECRETSに、やんごとなき機密情報wassyoiが格納されています。

[ec2-user@ip-10-0-2-41 ~]$ docker exec -it ecs-hamada-task-5-hamada-container-f8d8bde786ecb3e45900 bash
root@ip-10-0-2-89:/# env | grep HAMADA_SECRETS
HAMADA_SECRETS=wassyoi

上の方で、パラメータストアにSecureStringのwassyoiを格納していたわけですが、その内容がきちんとコンテナ内の環境変数に展開されていることが確認できました。

タスク定義のJson

aws cliでタスク定義を確認すると新しいパラメータが増えていました。以下、タスク定義のcontainerDefinitions部分の抜粋です。

        "containerDefinitions": [
            {
                "name": "hamada-container",
                "image": "nginx:1.15.6",
                "cpu": 512,
                "memoryReservation": 512,
                "portMappings": [
                    {
                        "containerPort": 80,
                        "hostPort": 80,
                        "protocol": "tcp"
                    }
                ],
                "essential": true,
                "environment": [],
                "mountPoints": [],
                "volumesFrom": [],
                "secrets": [
                    {
                        "name": "HAMADA_SECRETS",
                        "valueFrom": "hamako-favorite-word"
                    }
                ],
                "logConfiguration": {
                    "logDriver": "awslogs",
                    "options": {
                        "awslogs-group": "/ecs/hamada-task",
                        "awslogs-region": "ap-northeast-1",
                        "awslogs-stream-prefix": "ecs"
                    }
                }
            }
        ],

Webコンソール上では環境変数で指定しているように見えましたが、実態はenvironmentではなく、secretsとして定義されています。

AWSマネージドな仕組みだけで機密情報を渡せることのありがたさ

DBへの接続情報や他アプリケーション接続のためのアクセスキーなどは、機密情報のため平文で設定するのは避けておくべきです。今回のアップデートで、SecureStringを扱う仕組みがあるパラメータストアとECSがマネージドな仕組みだけで連携できるようになり、簡単な設定で機密情報の受け渡しができるようになりました。

 特に、コンテナ側でパラメータストアにアクセスするためのAWS関連のライブラリが不要になるのは、イメージサイズの最適化の観点でも嬉しい現場は多いかと思います。コンテナのベストプラクティスに則った形で、皆さんのECSも進化させていきましょう。

それでは、今日はこのへんで。濱田(@hamako9999)でした。