【祝!】ECSへの機密情報受け渡しがCloudFormationに対応しました!

先日、みんな大好きCloudFormationに、ECSタスク定義において待望のアップデートがやってきました!

端的にいうと、ECSのタスク定義で機密情報をSecrets ManagerやParameter Storeをキー指定で取得する方法が、今まではJSONやWebコンソールのみ設定可能だったのが、CloudFormationでも設定可能となったというものです。

AWS::ECS::TaskDefinition
In the ContainerDefinition property type, use the ResourceRequirements property to specify the type and amount of a resource to assign to a container. The only supported resource is a GPU.

In the ContainerDefinition property type, use the Secrets property to specify the secrets to pass to the container.

Use the Tags property to apply metadata to task definitions to help you categorize and organize them.
引用:Release History - AWS CloudFormation(2019/06/18)

頻出機能なのに、CloudFormationだけで設定できなかった悲しみが一気に解消される素晴らしいアップデートなので、みなさんも試してみましょう!

CFnでECS秘密情報きたか…!!

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

CloudFormation対応してなかった時代の悲しみ

2018年12月、下記ブログで書いたように、ECSのタスク定義に対して機密情報をSecretsManagerやParameter Storeから直接指定できる、めちゃくちゃ便利なアップデートがリリースされました。

しかし、これらの設定方法に対応していたのは、以下の2点だけで、CloudFormationには対応していなかったのです。

AWSで新しい機能(API)がリリースされたとき、CloudFormationのプロパティが後からの対応となることは多くあります。その間はじっと我慢の子なのですが、今回は約半年の時を超えての対応となりました。

今までは、ECSのクラスターやサービスをCloudFormationで定義しても、タスク定義はAWS CLIからJSONで更新、という使い分けが必須だったのが、タスク定義含めてCloudFormationで完結する利点は大きいです。

ECSコンテナ定義のCloudFormationの変更内容

具体的には、ECSのTaskDefinition内のContainerDefinitionにおいてSecretsが、新しくサポートされています。

Secretsの定義はこちら。

YAMLの定義は、以下の通り。

  Name: String
  ValueFrom: String
  • Name
    • コンテナ内で利用する環境変数名を指定
  • ValueFrom
    • コンテナ内に埋め込む機密情報を指定サポートしているのは、以下の2種
    • Secrets ManagerのフルARN
    • Parameter StoreセキュアストリングのフルARN(もしくは、同じリージョンにParameter Storeが存在するなら、そのキー名)

以前から、JSONで登録していた人にはおなじみのプロパティですね。Webコンソールで登録していた人には、環境変数のEnvironmentではないことに、注意が必要です。

環境変数確認用コンテナの準備

最初に、Webアクセスで環境変数を表示するコンテナを用意します。

環境変数確認用コンテナの用意

Fargateでは、コンテナにSSHログインできないので、以下のPHPコンテナを用意します。

<?php
  phpinfo();
?>
FROM php:7-apache
 
COPY phpinfo.php /var/www/html

上記コンテナは、DockerHubにアップ済みです。

環境変数確認用コンテナの動作確認

適当なdockerクライアントで、動作確認用コンテナを起動してみてください。以下のコマンドではローカルホストのポート8080でリクエストを待ちます。

docker container run -it -p 8080:80 hamako9999/list-environments-php-container:latest

コンテナ起動後、http://localhost:8080/phpinfo.phpにアクセスして、こんな感じのphpinfoの結果が表示されればOKです。

CloudFormationによる機密情報のタスク定義への読込

それでは、実際にCloudFormationを使って、コンテナ定義に機密情報を登録してみます。

Parameter Storeへのシークレット情報の登録

シークレット情報の登録は、以下のブログの記事を参考に実施してください。

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

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

CloudFormationによるタスク定義

いよいよ、CloudFormationでタスク定義を登録します。以下のCloudFormationテンプレートファイルを用意します。

AWSTemplateFormatVersion: '2010-09-09'
Description: ecs task definition

Resources:
# ------------------------------------------------------------#
#  Role
# ------------------------------------------------------------#
  ecsTaskExecutionRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement: 
          - Effect: "Allow"
            Principal: 
              Service: 
                - "ecs-tasks.amazonaws.com"
            Action: 
              - "sts:AssumeRole"
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy
        - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess
        - arn:aws:iam::aws:policy/SecretsManagerReadWrite

# ------------------------------------------------------------#
#  TaskDefinition
# ------------------------------------------------------------#
  Taskdefinition:
    Type: AWS::ECS::TaskDefinition
    DependsOn: ecsTaskExecutionRole
    Properties:
      Family: cfn-secrets-sample
      RequiresCompatibilities:
        - FARGATE
      Cpu: 256
      Memory: 512
      NetworkMode: awsvpc
      ExecutionRoleArn: !GetAtt ecsTaskExecutionRole.Arn
      TaskRoleArn: !GetAtt ecsTaskExecutionRole.Arn
      ContainerDefinitions:
        - Name: cfn-secrets-sample-container
          Image: hamako9999/list-environments-php-container:latest
          PortMappings:
            - ContainerPort: 80
          Secrets:
            - Name: HAMADA_SECRETS
              ValueFrom: hamako-favorite-word

以下、テンプレートの解説です。

アクセスポリシー付与

        - arn:aws:iam::aws:policy/AmazonSSMReadOnlyAccess
        - arn:aws:iam::aws:policy/SecretsManagerReadWrite

ECSのタスク実行ロールに対して、Parameter StoreとSecrets Managerへのアクセスポリシーを付与します。

DockerHubイメージへのリンク指定

          Image: hamako9999/list-environments-php-container:latest

自分がDockerHubにあげている、環境変数をWeb画面から確認するためのイメージへのURLを指定。

機密情報の取得設定

今回のアップデートの一番重要な部分。環境変数名とParameter Storeに登録した機密情報のパスをセットします。

          Secrets:
            - Name: HAMADA_SECRETS
              ValueFrom: hamako-favorite-word

aws cliを最新バージョンに更新しておきます(以下は、2019年6月17日時点)。

$ aws --version
aws-cli/1.16.179 Python/3.6.4 Darwin/18.5.0 botocore/1.12.169

上記テンプレートファイルが用意できたら、aws cliよりテンプレートを実行しCloudFormationのスタックを作成します。もちろん、Webコンソールから実行してもOkです。

$ aws cloudformation deploy --stack-name cfn-sample-secrets --template-file crn-sample-secrets.yml  \
  --capabilities CAPABILITY_NAMED_IAM

Waiting for changeset to be created..
Waiting for stack create/update to complete
Successfully created/updated stack - cfn-sample-secrets

無事、スタックが作成されましたでしょうか?

ECSコンソールからタスク定義を確認し、タスク定義cfn-secrets-sampleが作成されていればOKです。

定義をJSONで表示し、secretsが設定されていれば、成功やで!

登録したECSタスクを実行して動作確認

後は、タスクを実行して動作確認します。テンポラリで動かすタスクであれば、ECSサービスを作らなくても、タスク定義の画面から、「アクション」→「タスクの実行」が早いです。

タスクの実行画面で、適当なパブリックサブネットにタスクを配置し、セキュリティグループを設定して、パブリックIPを付与します(詳細は環境によって適宜変更ください)。

無事、正常にタスクが実行したら、タスクに付与されたパブリックIPを利用して、https://[public-ip]/phpinfo.phpにアクセスしてみてください。無事、環境変数HAMADA_SECRETSに、やんごとなき機密情報wassyoiが登録されていることが確認できたでしょうか。お疲れ様でした!

CloudFormationだけでECS周辺を管理できる嬉しみを貴方に

コンテナアプリケーションにおいて、DBアクセスするためのパスワードや、他のWebサービスへの接続用APIトークンなどの機密情報の扱いは、ほぼ必須の要件です。

ECSへの機密情報受け渡しがマネージドサービスでできるようになったことは非常に大きな喜びでしたが、CloudFormationでECS周辺の各リソースを定義していた現場においては、その必須要件がCloudFormationに対応していないのは、大きなストレスでした。

今回のアップデートで、機密情報のマネージドサービス(Parameter Storeや、Secrets Manager)からコンテナへの受け渡しも設定できることで、より一貫したCloudFormationによるインフラ管理が実現できるってもんです。素晴らしい!

待ってましたよ!この機能!みんなも現場でどんどん使っていきましょう!

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