はじめに
AWSのコンテナオーケストレーションサービスであるAmazon ECS(Fargate)を実務で運用する際、データベースの接続パスワードや外部APIのシークレットキーといった「機密情報」をどのようにコンテナへ渡すかは、セキュリティ設計上の極めて重要なテーマです。
本記事では、AWS Secrets ManagerとECS Fargateを連携させ、機密情報を安全に環境変数としてコンテナに注入する具体的な設定手順を、初心者から中級者向けにステップバイステップで解説します。
前提知識/必要な理由
なぜタスク定義に機密情報を直書きしてはいけないのか
ECSのタスク定義に環境変数をハードコード(直接記述)してしまうと、AWSマネジメントコンソール上で誰でも値が閲覧可能になってしまいます。また、タスク定義をGitなどでコード管理している場合、リポジトリ経由で情報漏洩するリスクが非常に高まります。
Secrets Manager連携の仕組み
AWS Secrets Managerを使用すると、コンテナイメージやタスク定義に機密情報を埋め込むことなく、安全に値を引き渡せます。コンテナが起動する際、ECSエージェントがタスク実行ロール(Task Execution Role)の権限を使ってSecrets Managerから値を安全に取得し、コンテナ内の環境変数として動的に注入(インジェクション)します。
具体的な設定手順・設計方法
ステップ1:Secrets Managerでシークレットを作成する
まずは、安全に管理したい機密情報をSecrets Managerに登録します。
- AWSコンソールで「Secrets Manager」を開き、「新しいシークレットを保存する」をクリックします。
- シークレットのタイプで「その他のシークレットのタイプ」を選択します。
- キーと値のペアを入力します(例:キーに
DB_PASSWORD、値にSuperSecurePassword123!)。 - シークレット名(例:
myapp/production/database)を設定し、保存します。 - 作成完了後に表示される「シークレットのARN(Amazon Resource Name)」を控えておきます。
ステップ2:ECSタスク実行ロールに権限(IAMポリシー)を付与する
ECSが起動時にSecrets Managerから値を読み取れるよう、タスク実行ロール(Task Execution Role)に権限を付与します。既存のロールに以下のインラインポリシーを追加するか、カスタムポリシーを作成してアタッチしてください。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"secretsmanager:GetSecretValue"
],
"Resource": [
"arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:myapp/production/database-XXXXXX"
]
}
]
}
※ Resource 部分には、ステップ1で作成したシークレットのARNを指定してください。末尾のランダムな文字列(ハッシュ値)も含める必要があります。
ステップ3:ECSタスク定義でシークレットを参照する
ECSのタスク定義ファイル(JSON)の containerDefinitions 内にある secrets ブロックで、以下のようにシークレットを指定します。これにより、コンテナ起動時に DATABASE_PASS という環境変数にSecrets Managerの値が自動で注入されます。
{
"containerDefinitions": [
{
"name": "my-app-container",
"image": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/myapp:latest",
"essential": true,
"secrets": [
{
"name": "DATABASE_PASS",
"valueFrom": "arn:aws:secretsmanager:ap-northeast-1:123456789012:secret:myapp/production/database-XXXXXX:DB_PASSWORD::"
}
]
}
]
}
※ valueFrom の末尾に :DB_PASSWORD:: と記述することで、シークレット(JSONオブジェクト)内の特定のキー(ここでは DB_PASSWORD)のみをピンポイントで取得できます。
(補足)アプリケーションコード内で直接SDKを用いて取得する場合
ECSによる自動注入ではなく、アプリケーション起動後にPythonなどのコード内から動的にAWS SDK(boto3)を利用してSecrets Managerから値を取得したい場合、対象のコンテナにあらかじめSDKを導入しておく必要があります。
まずは以下のコマンドでライブラリをインストールします。
pip install boto3
その後、アプリケーションコード内で以下のように実装します(※この場合、権限は「タスク実行ロール」ではなく「タスクロール(Task Role)」に付与する必要がある点に注意してください)。
import boto3
import json
def get_secret():
secret_name = "myapp/production/database"
region_name = "ap-northeast-1"
session = boto3.session.Session()
client = session.client(service_name='secretsmanager', region_name=region_name)
response = client.get_secret_value(SecretId=secret_name)
return json.loads(response['SecretString'])
実務での注意点
- タスクロールとタスク実行ロールの違いに注意する: ECSが起動時に環境変数としてシークレットを注入するために必要な権限は「タスク実行ロール(Task Execution Role)」です。アプリケーションプログラム内部からSDKを叩いて取得する場合は「タスクロール(Task Role)」が必要になります。この2つの役割を混同しないようにしてください。
- プライベートネットワークでのネットワーク経路に注意する: FargateをパブリックIPを持たない完全なプライベートサブネットで起動する場合、NAT Gateway経由でインターネットに出るか、あるいは「Secrets Manager用のVPCエンドポイント(Interface型)」をVPC内に配置しなければ、FargateからSecrets Managerへの名前解決や接続ができず、タスク起動時にタイムアウトエラーが発生します。
まとめ
本記事では、ECS FargateでAWS Secrets Managerを使って安全に機密情報をコンテナ環境変数へ注入する設計と実装手順を解説しました。
インフラのソースコード(タスク定義)と秘密情報を明確に分離することは、実務におけるセキュリティのベストプラクティスです。最初の設定手順さえ理解してしまえば横展開もしやすいため、安全なコンテナインフラ構築の標準テンプレートとしてぜひ活用してください。