Amazon Web Services ブログ
AWS Fargate で AWS Secrets Managerを使用して認証情報を保護する
本投稿は AWS コンテナサービスのプリンシパルディベロッパーアドボケートである Massimo Re Ferreによる寄稿です
クラウドのセキュリティはAWSの最優先事項であり、コンテナチームの取り組みがその証でもあります。およそ1か月前、私たちは AWS Secrets Manager や AWS Systems Manager パラメータストアと、AWS Fargate タスクの統合機能を発表しました。 これにより Fargate のお客様は、ご自身のタスク定義から秘密情報を安全に、パラメータを透過的に利用することができます。
この記事では、秘密情報を確実に保護しつつ Secrets Manager と Fargate の統合を利用する方法の例を紹介します。
概要
AWS は複数の重要なセキュリティ上の基準を以って Fargate を高度にセキュアに設計しました。その 1つは、各Fargate タスクはそれぞれに分離境界があり、下層のカーネル、CPUリソース、メモリリソース、またはElastic Network Interface(ENI)を他のタスクと共有していないところです。
セキュリティに重点を置くもう 1 つの領域は、Amazon VPC ネットワーキング統合です。これにより、ネットワーキングの観点から Amazon EC2 インスタンスを保護する手法で Fargate タスクを保護できます。
この発表は、責任共有モデルの観点でも重要です。例えば、AWSのプラットフォーム上でソリューションを構築して実行するようなDevOpsチームは、アプリケーションコードの実行時に秘密情報、パスワード、機密パラメータをセキュアに管理する適切な仕組みや機能を必要とします。そして、プラットフォームの機能によって彼ら/彼女らがまさにそのようなことを可能な限り簡単に実行できるようにすることが私たちの役割なのです。
私たちは、時に一部のユーザーがセキュリティ面をトレードオフとして俊敏性を得るために、ソースコードに AWS 認証情報を埋め込んだままパブリックリポジトリにプッシュしたり、プライベートに格納された設定ファイルに平文でパスワードを埋め込んでいるのを見てきました。私たちは、さまざまなAWSサービスを利用している開発者が IAM ロールを Fargate タスクに割り当てることができるようし、AWS 認証情報を透過的に取り扱えるようにすることで、この課題を解決しました。
これはネイティブな AWS サービスを利用する場合に便利でしたが、IAMロールとIAMポリシーの範囲外にあるサービスやアプリケーションへのアクセスはどうでしょうか?多くの場合、このような認証情報を取り扱わなければならないという負担は一般に開発者や AWS ユーザーに課せられてきました。このような形である必要性はありません。Secrets Manager と Fargate の統合に進みましょう!
Fargate プラットフォームバージョン1.3.0以降では、Fargate タスクから Secrets Manager を経由してシークレットをセキュアに取得できるようになりました。これにより、これらのシークレットはプライベートな設定ファイルにすら公開されてしまうことはありません。
加えて、これはシークレットをセキュアに保つことに手間をかける負担から解放してくれます。さらに Secrets Manager はシークレットのローテーションをサポートしているため、追加で労力をかけずにセキュリティレベルを高めることができます。
Twitter matcherの例
この例では、Twitter からデータストリームを読み取り、ツイート内のメッセージを特定のパターンに一致させ、そのツイートに関する情報を DynamoDB テーブルに記録する Fargate タスクを作成します。
PythonのTwitterライブラリであるTweepyを使用して Twitter からストリームを読み取り、AWS Boto 3 Python library を使用して Amazon DynamoDB に書き込みを行います。
下図が大まかなフローです。
この例の目的は、タスクに割り当てられた IAM ロールを使用して AWS サービス(DynamoDB など)を操作する簡単なユースケースを示すことです。また、認証情報がセキュアに格納される必要のある外部サービス(Twitter など)の使用も含まれています。
Fargateタスクを起動すると、次のようになります。
- タスクが開始され、IAM からタスク実行ロール (1) とタスクロール (2) が継承されます。
- タスク実行ロールによって継承された認証情報を使用して Secrets Manager (3) から Twitter 認証情報を取得し、環境変数としてタスクに渡します。
- Secrets Managerに格納されている認証情報を使用して、Twitter(4)からストリームを読み取ります。
- ストリームと設定可能なパターンを照合し、タスクロールによって継承された認証情報を使用して DynamoDB テーブル (5) に書き込みます。
- タスクロールによって継承された認証情報を使用して CloudWatch(6)にログを書き込みます。
補足すると、この例では、機密性の高い認証情報を必要とする外部サービスとして Twitter を使用していますが、パスワードやキーを使用して何らかの認証を行う外部サービスであれば何でも適用できます。必要に応じて、関連するデータを独自のサービスから取得して DynamoDB テーブルに書き込むように Python スクリプトを修正してみてください。
この例の手順は次のとおりです。
- Python スクリプトを作成する
- Dockerfile を作成する
- コンテナイメージをビルドする
- イメージレジストリを作成する
- DynamoDB テーブルを作成する
- 認証情報をセキュアに保存する
- Fargate のタスク用に IAM ロールと IAM ポリシーを作成する
- Fargate タスクを作成する
- 後片付けする
前提条件
この演習を実行するには、次の依存関係で構成された環境が必要です。
- AWS コマンドラインインターフェイス(AWS CLI)の最新バージョンがインストールされている
- Docker ランタイム
- AWS アカウントへのアクセス認証情報
AWS Cloud9 インスタンスを起動してこの設定部分をスキップすることもできます。
ここでは例として、us-west-2
リージョンで動作するように設定された AWS CLI を使用しています。別のリージョンで作業することも可能です。この記事のコード例をご利用のリージョンに応じて書き換えてください。
AWS の前提条件のリストに加えて、Twitter 開発者アカウントが必要です。 Twitter 開発者アカウントにてアプリケーションを作成し、Twitter API に接続するために提供された認証情報を使用します。ブログ記事の後半で AWS Secrets Manager に追加する際に、この認証情報を利用します。
注:このブログ記事で提案されているコマンドの多くは、$REGION と $AWSACCOUNT を使用しています。 デプロイするリージョンと独自のアカウントを指す環境変数を設定するか、コマンド自体の環境変数をリージョンとアカウント番号に置き換えることができます。また、同じパターンを使用するいくつかの設定ファイル(json)があり、最も簡単なオプションは、$REGION および $AWSACCOUNT プレースホルダを実際のリージョンとアカウント番号に置き換えることです。
Pythonスクリプトを作成する
このスクリプトは、Tweepy streaming example に基づいています。 Boto 3 ライブラリと DynamoDB テーブルにデータを書き込む命令を含めるようにスクリプトを変更しました。 さらに、スクリプトは同じデータを標準出力に出力するように修正しています(コンテナログに記録されるように)。
以下がそのPythonスクリプトです。
このファイルを twitterstream.py
としてディレクトリに保存します。
この Docker イメージには 7 つのパラメータが必要です。これらのパラメータは、スクリプトの先頭にシステム変数としてはっきりと表示されています。
- DynamoDB のテーブル名
- 操作するリージョン
- フィルタする単語やパターン
- Twitter API サービスへの接続に使用する4つのキー。後で、これらの変数をコンテナに渡す方法を取り上げます。これらの変数は他の変数よりも機密性が高いものであることに注意してください。
Dockerfileを作成する
実際の Docker イメージを構築します。以下のような Dockerfile を作成します。
Dockerfile
として、twitterstream.py
と同じディレクトリに保存します。
コンテナイメージをビルドする
次に、後で Fargate タスクとしてインスタンス化するコンテナイメージを作成します。 先ほどと同じディレクトリ上で以下のコマンドを実行してコンテナイメージを構築します。
docker build -t twitterstream:latest .
コマンドの最後のピリオド(.)を見落とさないようにしてください。
現在のディレクトリ内の Dockerfile を見つけるように Docker に指示しています。
これで、適切にパラメータが設定された後、最終的に Twitter API から読み取り、DynamoDB テーブルにデータを保存できるローカルの Docker イメージが作成されました。
イメージリポジトリを作成する
次に、このイメージを適切なコンテナレジストリに格納します。 次のコマンドを使用して Amazon ECR リポジトリを作成します。
aws ecr create-repository --repository-name twitterstream --region $REGION
結果として、以下のような値が返却されます。
次のコマンドを使用して、ローカルイメージにタグ付けをします。
docker tag twitterstream:latest $AWSACCOUNT.dkr.ecr.$REGION.amazonaws.com/twitterstream:latest
AWS アカウント ID とデプロイするリージョンを指定して、適切なリポジトリを参照してください。
AWS STS から認可トークンを取得します:
$(aws ecr get-login --no-include-email —region $REGION)
次に、作成したばかりの ECR リポジトリにローカルイメージをプッシュします。
docker push $AWSACCOUNT.dkr.ecr.$REGION.amazonaws.com/twitterstream:latest (http://region.amazonaws.com/twitterstream:latest)
次のような結果が表示されます。
これで、コンテナイメージは ECR リポジトリに安全に保存されました。
DynamoDB のテーブルを作成する
次に、バックエンドの DynamoDB テーブルを扱います。生成される Twitter ストリームの抽出データを格納します。
具体的には、ツイートを公開した user
、ツイートが公開された date
、およびツイートの text
を保存します。
ここでは例として、twitterStream
という名前のテーブルを作成します。 これは、Fargate タスクに渡す必要のあるパラメータの1つとしてカスタマイズできます。 次のコマンドを実行して、テーブルを作成します。
認証情報をセキュアに保存する
先ほど説明したように、Python スクリプトでは、Fargate タスクでいくつかの情報を変数として渡す必要があります。 テーブル名、Region、およびフィルタ用のテキストを標準タスク変数として渡します。これらは機密情報ではないため、懸念なく共有可能です。
ただし、Twitter API キーのような他の設定は機密性が高く、平文で渡すべきではありません。このため、Secrets Manager を使用してその機密情報をセキュアに保存し、Fargate タスク内からセキュアに読み取ることができます。 これは、新しく発表した、Fargate と Secrets Manager の統合によって可能になったことです。
機密情報の格納には Secrets Manager コンソール、もしくは CLI を利用することができます。
コンソールを使用して操作する場合、[other types of secrets] を選択します。 [Plaintext] に、コンシューマキーを入力します。 以下のスクリーンショットに示すように、[Select the encryption key] で、[DefaultEncryptionKey] を選択します。 詳細については、「基本的なシークレットを作成する」を参照してください。
ただし、この例では、AWS CLI を使用して必要な 4 つのシークレットを作成する方が簡単です。 以下のコマンドを実行しますが、自身の Twitter 認証情報を使用するようにカスタマイズしてください。
各コマンドの実行後、シークレットが作成されたことを確認する以下のメッセージが表示されます。
これ以降、これらの 4 つの API キーはどの設定にも現れません。
次のスクリーンショットは、コマンドが実行された直後のコンソールを示しています。
Fargate タスク用の IAM ロールと IAM ポリシーを作成する
Python コードを正しく実行するには、Fargate タスクにはいくつかの特定の機能が必要です。 Fargate タスクは、次の操作を実行できる必要があります。
twitterstream
コンテナイメージ(先ほど作成したもの)を ECR から pull する- Secrets Manager から、Twitter の認証情報(先ほどセキュアに保存したもの)を取得する
- 特定の Amazon CloudWatch ロググループにログインする(ログ取得はオプションですが、ベストプラクティス)
- DynamoDB テーブルに書き込みます(先ほど作成したもの)
最初の 3 つの処理を実行するための権限は、ECS タスク実行ロールにアタッチされる必要があります。4つ目は ECS タスクロールにアタッチされる必要があります。詳細については、「Amazon ECS タスク実行 IAM ロール」を参照してください。
別の言葉で表現すると、ECS エージェントやコンテナインスタンスに関連付けられている機能性は ECS タスク実行ロールに設定される必要があります。 タスクの中から使用できる必要がある機能は、ECS タスクロールで設定されていなければなりません。
最初に、Fargate タスクにアタッチされる 2 つの IAM ロールを作成します。
以下の内容の ecs-task-role-trust-policy.json
というファイルを作成します($REGION、$AWSACCOUNTと適切なシークレットのARN を置き換えてください)。
次のコマンドを実行して、twitterstream-task-role
と twitterstream-task-execution-role
を作成します。
aws iam create-role --region $REGION --role-name twitterstream-task-role --assume-role-policy-document file://ecs-task-role-trust-policy.json
aws iam create-role --region $REGION --role-name twitterstream-task-execution-role --assume-role-policy-document file://ecs-task-role-trust-policy.json
次に、ECS タスクロール (twitterstream-task-role
) に必要な機能をコード化する JSON ファイルを作成します。
ファイルを twitterstream-iam-policy-task-role.json
として保存します。
次に、ECS タスク実行ロール (twitterstream-task-Execution-role
) に必要な機能をコード化するJSONファイルを作成します。
ファイルを twitterstream-iam-policy-task-execution-role.json
として保存します。
次の 2 つのコマンドで、IAM ポリシードキュメントを作成し、先ほど作成した IAM ロールに関連付けます。
aws iam put-role-policy --region $REGION --role-name twitterstream-task-role --policy-name twitterstream-iam-policy-task-role --policy-document file://twitterstream-iam-policy-task-role.json
aws iam put-role-policy --region $REGION --role-name twitterstream-task-execution-role --policy-name twitterstream-iam-policy-task-execution-role --policy-document file://twitterstream-iam-policy-task-execution-role.json
Fargate タスクを作成する
今こそ、すべてを結び付ける時です。これまでの手順で、以下の作業が完了しています。
- Python コードを含むコンテナイメージを作成
- Twitter ストリームからのデータを保存する DynamoDB テーブルの作成
- Secrets Manager に Twitter API の認証情報をセキュアに保存
- DynamoDB に書き込んだり、シークレットマネージャから読み取りできる特定の IAM ポリシーが紐づいたIAMロールの作成
これで、コンテナイメージを実行する Fargate タスクを作成することで、すべてを結び付けることができます。 これを行うには、twitterstream-task.json
という名前のファイルを作成し、次の設定を入力します。
検索文字列を微調整するには、FILTER
変数の値を変更します(現在は「Cloud Computing」に設定されています)。
Twitter API 認証情報は、これらの設定ファイル内で平文で公開されることはありません。シークレット名であるAmazon リソースネーム (ARN) への参照のみが存在します。たとえば、以下は Fargate タスク定義のシステム変数 CONSUMERKEY
です。
このディレクティブは、Fargate インスタンス(指定された IAM 実行ロールを引き受ける)で実行されている ECS エージェントに、次の操作を実行するよう要求します。
- Secrets Manager へ接続
- 機密情報をセキュアに取得
- システム変数 CONSUMERKEY にその値を割り当て、Fargate タスクで使用できるようにする
次のコマンドを実行して、このタスクを登録します。
aws ecs register-task-definition --region $REGION --cli-input-json file://twitterstream-task.json
タスクを実行する準備として、次のコマンドを使用して CloudWatch ロググループを作成します。
aws logs create-log-group --log-group-name twitterstream --region $REGION
ロググループを事前に作成しないと、タスクの開始に失敗します。
ECS クラスタを作成する
Fargate タスクを起動する前の最後のステップは、ECS クラスターを作成することです。ECS クラスターには、次の2つの異なる起動モードがあります。
- EC2 起動モード: コンピュートキャパシティは、ECS コンテナインスタンスとしてお客様によって管理されます。
- Fargate 起動モード: コンピュートキャパシティは AWS によって透過的に管理されます。
この例では Fargate ディメンションを使用するため、ECS クラスターは論理的な名前空間として利用されることになります。
次のコマンドを実行して、twitterstream_cluster
(必要に応じて名前は変更してください)という名前のクラスターを作成します。 選択したリージョンに default
クラスターが既に作成されている場合は、そのクラスターを利用することも可能です。
aws ecs create-cluster --cluster-name "twitterstream_cluster" --region $REGION
ここで、作成したばかりの ECS クラスタ(us-west-2 リージョン)で、Fargate 起動タイプでタスクを起動します。 次のコマンドを実行します。
このコマンドで注意すべき点は以下のとおりです。
- aws ecs register-task-definition コマンドを再実行して複数のリビジョンを作成した場合は、必ず aws ecs run-task コマンドの最後に適切なリビジョン番号で実行してください。
- ご使用の環境に合わせて、コマンドのネットワークの箇所をカスタマイズしてください
- Fargate タスクはアウトバウンド接続のみを必要とするため、VPC でデフォルトのセキュリティグループを使用します。
- Fargate タスクを実行する 2 つのパブリックサブネットを使用します。
Fargate タスクは数秒で起動し、次のスクリーンショットに示すように、ECS コンソールから確認できます。
同様に、次のスクリーンショットに示すように、タスクで実行されているスクリプトによって収集された情報が DynamoDB テーブルに書き込まれます。
最後に、次のスクリーンショットに示すように、Fargate タスクはすべてのアクティビティをCloudWatch ロググループに記録します。
ログが読み込まれて CloudWatch に集約されるまでに数分かかる場合があります。
後片付け
検証が完了したので、作成したすべてのリソースを解体して、今後の課金を回避できます。
まず、開始した ECS タスクを停止します。
aws ecs stop-task --cluster twitterstream_cluster --region $REGION --task 4553111a-748e-4f6f-beb5-f95242235fb5
タスク番号は独自のものです。 ECS コンソールまたは AWS CLI から取得できます。AWS CLI から読み込む方法は次のとおりです。
次に、作成した ECS クラスターを削除します。
aws ecs delete-cluster --cluster "twitterstream_cluster" --region $REGION
次に、CloudWatch ロググループを削除します。
aws logs delete-log-group --log-group-name twitterstream --region $REGION
IAM ロールを削除するための高速なワークフローがコンソールにて用意されています。IAM コンソールで [Roles] を選択し、twitter
で検索しフィルタリングします。 作成した 2 つのロールが表示されます。
2 つのロールを選択し、[ロールの削除] を選択します。
作成されたシークレットのクリーンアップは簡単です。それぞれについて delete-secret コマンドを実行します。
aws secretsmanager delete-secret --region $REGION --secret-id CONSUMERKEY
aws secretsmanager delete-secret --region $REGION --secret-id CONSUMERSECRETKEY
aws secretsmanager delete-secret --region $REGION --secret-id ACCESSTOKEN
aws secretsmanager delete-secret --region $REGION --secret-id ACCESSTOKENSECRET
次のステップでは、DynamoDB テーブルを削除します。
aws dynamodb delete-table --table-name twitterStream --region $REGION
最後のステップは、ECR リポジトリを削除します。デフォルトでは、コンテナイメージがまだ含まれるリポジトリは削除できません。 これを解決するには、–force ディレクティブを追加します。
aws ecr delete-repository --region $REGION --repository-name twitterstream --force
ECS コンソールで次の手順に従って、twitterstream
タスク定義の登録を解除できます。タスク定義は非アクティブですが、システム上は表示されます。
これで、作成したすべてのリソースが削除されました。
まとめ
この記事では、Fargate が Secrets Manager とやり取りして機密データ(Twitter API 認証情報など)を取得する方法を紹介しました。Fargate タスク内のコンテナで実行されているコードで機密データをセキュアに利用できます。
また、特定の IAM ロールを持つ Fargate タスクが他の AWS サービス(DynamoDB など)にアクセスする方法についても説明しました。
翻訳はソリューションアーキテクト濵が担当しました。原文はこちらです