はじめに
AWS上でWebアプリケーションを構築する際、もっとも標準的な構成の一つが「ECS FargateからPrivateサブネット内のRDS(Aurora)への接続」です。しかし、実務においては「セキュリティグループの設定が正しくなく接続できない」「データベースのパスワードをタスク定義に直書きしてしまっている」といった設計・設定上のトラブルが多発します。
本記事では、セキュリティのベストプラクティスに準拠した、ECS FargateからRDSへのセキュアな接続手順を、セキュリティグループの設計からSSMパラメータストアを用いた認証情報の引き渡しまでステップバイステップで解説します。
前提知識/必要な理由
1. セキュリティグループの相互参照(グループID指定)の重要性
ECS Fargateは起動のたびにプライベートIPアドレスが動的に変更されます。そのため、RDS側のセキュリティグループで「特定IPからの許可」を設定することはできません。ECS用のセキュリティグループIDを、RDS用のセキュリティグループのインバウンドルールで「ソース(送信元)」として指定する「相互参照」の設定が必要不可欠です。
2. 認証情報の環境変数直書きの危険性
データベースの接続パスワードをECSのタスク定義にプレーンテキストで直接記述することは、漏洩リスクを高めるためNGとされています。AWSの推奨は、AWS Systems Manager(SSM)パラメータストアやAWS Secrets Managerに暗号化して保存し、ECSタスク起動時に環境変数として安全に注入する方法です。
具体的な設定手順・設計方法
手順1:セキュリティグループの作成と設定
まず、ECS用とRDS用の2つのセキュリティグループ(SG)を作成し、適切なルールを設定します。
- ECS用セキュリティグループ(例: sg-ecs-app)
- インバウンドルール: 必要に応じて、ALB(ロードバランサー)からのHTTP/HTTPS通信のみを許可します。
- アウトバウンドルール: すべての通信(0.0.0.0/0)を許可します。
- RDS用セキュリティグループ(例: sg-rds-db)
- アウトバウンドルール: デフォルト(すべての通信を許可)のままで構いません。
- インバウンドルール: 以下のように設定します。
プロトコル: TCP
ポート範囲: 5432(PostgreSQLの場合。MySQLは3306)
ソース: カスタム -> sg-ecs-app(ECS用のセキュリティグループIDを選択)
手順2:SSMパラメータストアへの接続情報の登録
データベースのパスワードを安全に管理するため、AWS Systems Managerのパラメータストアに登録します。
AWS CLIを使用する場合は、以下のコマンドを実行してSecureString(暗号化文字列)として保存します。
aws ssm put-parameter \
--name "/prod/db/password" \
--value "YourSuperSecurePassword123!" \
--type "SecureString"
手順3:ECSタスク実行ロール(IAM Role)の権限追加
ECS Fargateが起動時にSSMパラメータストアから値を取得できるようにするため、ECSの「タスク実行ロール(task-execution-role)」に以下のIAMポリシーを追加します。
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"ssm:GetParameters",
"secretsmanager:GetSecretValue",
"kms:Decrypt"
],
"Resource": [
"arn:aws:ssm:ap-northeast-1:123456789012:parameter/prod/db/*"
]
}
]
}
※ Resource のARNは、ご自身のAWSアカウントIDおよびパラメータ名に書き換えてください。
手順4:ECSタスク定義での環境変数設定
ECSタスク定義のJSON、またはAWSコンソールのコンテナ定義画面で、環境変数を「値(Value)」ではなく「値の元(ValueFrom)」として設定します。これにより、コンテナ起動時に自動でデシリアライズされた値が環境変数に注入されます。
{
"containerDefinitions": [
{
"name": "web-app",
"image": "123456789012.dkr.ecr.ap-northeast-1.amazonaws.com/my-app:latest",
"environment": [
{
"name": "DB_HOST",
"value": "rds-instance.xxxxxx.ap-northeast-1.rds.amazonaws.com"
},
{
"name": "DB_USER",
"value": "db_admin"
}
],
"secrets": [
{
"name": "DB_PASSWORD",
"valueFrom": "arn:aws:ssm:ap-northeast-1:123456789012:parameter/prod/db/password"
}
]
}
]
}
手順5:アプリケーション側での接続実装例
Node.js(Express)からPostgreSQLに接続する場合を例にします。あらかじめ、コンテナのDockerイメージ構築用フォルダにて、接続用ドライバー(外部パッケージ)を導入しておきます。
# 接続に必要なnpmパッケージのインストール
npm install pg
アプリケーションコードでは、ECSから注入された環境変数をそのまま読み込みます。
const { Pool } = require('pg');
const pool = new Pool({
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASSWORD, // タスク定義のsecrets経由で安全に注入された値
database: 'production_db',
port: 5432,
ssl: {
rejectUnauthorized: true // 実務ではSSL/TLS接続を有効化することを推奨
}
});
pool.query('SELECT NOW()', (err, res) => {
if (err) {
console.error('データベース接続エラー:', err);
} else {
console.log('データベース接続成功:', res.rows[0]);
}
});
実務での注意点
1. プライベートサブネット運用の落とし穴とVPCエンドポイント
ECS FargateをパブリックIPを持たない「完全なプライベートサブネット」で起動する場合、タスク定義の起動時(コンテナイメージのプルやSSMからのパラメータ取得時)に、AWSの各種APIと通信できずにタスク起動がタイムアウトするエラーが発生します。
これを防ぐためには、VPC内に以下の「インターフェイス型VPCエンドポイント(AWS PrivateLink)」を配置するか、NAT Gatewayを配置してアウトバウンド通信を確保する必要があります。
- ecr.api および ecr.dkr(ECRからのイメージ取得用)
- ssm(SSMパラメータストアからの値取得用)
- s3(ゲートウェイ型:ECRイメージレイヤーの実態が置かれているS3へのアクセス用)
2. データベースの接続タイムアウト設定
コンテナのオートスケーリングやデプロイにともない、データベースへの接続コネクションが一時的に急増することがあります。アプリケーション側でコネクションプール(Connection Pooling)の最大数を適切に制御し、RDSの最大接続数を超過して接続エラーが発生しないよう設計してください。
まとめ
ECS FargateからRDSへの安全な接続には、IPアドレスに依存しない「セキュリティグループの相互参照」と、タスク定義にパスワードを露出させない「SSMパラメータストアによる環境変数注入」の組み合わせがデファクトスタンダードです。
インフラの構築時だけでなく、開発段階からこの構成を意識してセキュリティ設計を行うことで、本番環境へのスムーズなデプロイと強固なセキュリティの両立を実現できます。ぜひ実務の設定で役立ててください。