🛰️

ECS Fargate + RDS の構成でローカルPCからRDSに安全に接続する

に公開

はじめに

WEBアプリケーション開発をしていると、開発環境やステージング環境のRDSのデータを確認したくなったり、データを書き換えたくなることがあると思います。プライベートネットワーク配下に置かれているRDSに対して安全に接続し、DBeaverやpgAdminのようなGUIツールを使って接続できる方法を紹介します。

やりたいこと

  • プライベートVPC内のRDSにローカルから直接アクセス
  • SQLクライアントのGUIツール(pgAdmin、DBeaver等)でRDSに接続
  • 開発・デバッグ作業の効率化

インフラ構成

ECS Fargate と RDS はプライベート VPC ネットワークに置かれているため、直接 ssh することはできません。
今回紹介する方法は、AWS Session Manager の AWS-StartPortForwardingSessionToRemoteHost を使用してECSタスク内からRDSへのポートフォワーディングを実現します。

[ローカルPC] ←→ [AWS Session Manager] ←→ [ECS Fargate] ←→ [RDS]

事前準備

以下のツールを使用します:

1. AWS CLI

brew install awscli

※ 初期設定は割愛します(AWS認証情報の設定が必要です)

2. jq(JSONパーサー)

brew install jq

3. Session Manager Plugin

brew install session-manager-plugin

4. ECSタスクにSession Managerエージェントがインストールされている

以下のポリシーが ECS タスクロールに割当たっている必要があります

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Action": [
				"ssmmessages:CreateControlChannel",
				"ssmmessages:CreateDataChannel",
				"ssmmessages:OpenControlChannel",
				"ssmmessages:OpenDataChannel"
			],
			"Resource": "*"
		}
	]
}

接続スクリプト

rds_port_forward.sh という名前で以下のファイルを作成します:

#!/bin/bash

set -eo pipefail

# 必要なツールの存在確認
check_tool() {
    local tool_name=$1
    local install_command=$2
    
    if ! command -v $tool_name &> /dev/null; then
        echo "$tool_name not found. Install with:"
        echo "  $install_command"
        exit 1
    fi
}

# ツールの存在確認
check_tool "aws" "brew install awscli"
check_tool "jq" "brew install jq"
check_tool "session-manager-plugin" "brew install session-manager-plugin"

# 設定値(環境に合わせて変更してください)
CLUSTER_NAME=example_cluster_name    # ① ECSのクラスター名を設定します
SERVICE_NAME=example_service_name    # ② ECSのサービス名を設定します
REMOTE_PORT_NUMBER=5432              # ③ 接続したい RDS のポート
LOCAL_PORT_NUMBER=5555               # ④ ポートフォワードで使用したいローカルのポート

echo "🚀 RDSポートフォワーディングを開始します..."

# 実行中のタスクARNを取得
TASK_ARN=$(aws ecs list-tasks \
    --cluster $CLUSTER_NAME \
    --service $SERVICE_NAME \
    --desired-status RUNNING | jq -r .taskArns[0])

if [ "$TASK_ARN" = "null" ] || [ -z "$TASK_ARN" ]; then
    echo "❌ 実行中のタスクが見つかりません。サービスが起動しているか確認してください。"
    exit 1
fi

echo "📋 タスクARN: $TASK_ARN"

# タスクの詳細情報を取得してランタイムIDを取得
RUNTIME_ID=$(aws ecs describe-tasks \
    --cluster $CLUSTER_NAME \
    --task $TASK_ARN | jq -r .tasks[0].containers[0].runtimeId)

TASK_ID=${TASK_ARN##*/}

# RDSホスト名を取得(SSMパラメータストアから)
RDS_HOST=$(aws ssm get-parameter --name DATABASE_HOST --with-decryption | jq -r .Parameter.Value)

if [ "$RDS_HOST" = "null" ] || [ -z "$RDS_HOST" ]; then
    echo "❌ DATABASE_HOSTパラメータが見つかりません。SSMパラメータストアを確認してください。"
    exit 1
fi

echo "🗄️  RDSホスト: $RDS_HOST"

# Session Managerのターゲットを構築
TARGET=ecs:${CLUSTER_NAME}_${TASK_ID}_${RUNTIME_ID}

echo "🎯 ターゲット: $TARGET"
echo "🔌 ポートフォワーディング: localhost:$LOCAL_PORT_NUMBER$RDS_HOST:$REMOTE_PORT_NUMBER"
echo "⏳ 接続を開始します...(Ctrl+Cで終了)"

# Session Managerでポートフォワーディングを開始
aws ssm start-session \
    --target $TARGET \
    --document-name AWS-StartPortForwardingSessionToRemoteHost \
    --parameters "{\"host\":[\"$RDS_HOST\"],\"portNumber\":[\"${REMOTE_PORT_NUMBER}\"], \"localPortNumber\":[\"${LOCAL_PORT_NUMBER}\"]}"

使用方法

1. パラメータ設定

環境に合わせて、以下の設定値を編集します:

CLUSTER_NAME=example_cluster_name    # ① ECSのクラスター名を設定します
SERVICE_NAME=example_service_name    # ② ECSのサービス名を設定します
REMOTE_PORT_NUMBER=5432              # ③ 接続したい RDS のポート
LOCAL_PORT_NUMBER=5555               # ④ ポートフォワードで使用したいローカルのポート

RDSのホストを取得する以下の部分

# RDSホスト名を取得(SSMパラメータストアから)
RDS_HOST=$(aws ssm get-parameter --name DATABASE_HOST --with-decryption | jq -r .Parameter.Value)

これは RDS のホスト名がパラメータストアに DATABASE_HOST で登録されている場合の実装になっています。環境に合わせて編集してください。

2. スクリプトの実行

sh rds_port_forward.sh

3. SQLクライアントでの接続

ポートフォワードが開始されたら、SQLクライアントで以下の設定で接続:

  • ホスト: localhost
  • ポート: 5555
  • データベース名: RDSのデータベース名
  • ユーザー名/パスワード: RDSのユーザー名/パスワード

4. 動作の仕組み

  1. ECSタスクの特定: 指定されたクラスターとサービスから実行中のタスクを取得
  2. Session Manager接続: ECSタスク内にSession Managerで接続
  3. ポートフォワーディング: ローカルポート5555からRDSポート5432への転送を開始
  4. GUIツール接続: ローカルポート5555経由でRDSにアクセス

この接続方法のメリット

AWS-StartPortForwardingSessionToRemoteHost を使用してRDSに接続する方法は以下のメリットがあります

1. セキュリティの向上

暗号化通信: Session Manager経由で全ての通信が暗号化される
IAM認証: AWS認証情報のみでアクセス可能(SSH鍵管理不要)
監査ログ: CloudTrailでアクセス履歴が記録される
ネットワーク分離: プライベートVPC内のリソースに安全にアクセス

2. 運用負荷の軽減

SSH鍵管理不要: 従来のBastionホスト方式と違い、SSH鍵の管理が不要
ファイアウォール設定不要: セキュリティグループでSSHポート開放が不要
Bastionホスト不要: 追加のEC2インスタンスが不要

3. スケーラビリティ

ECSタスク自動選択: 実行中のタスクを自動で選択
複数環境対応: 開発・ステージング・本番環境で同じ方式が使える
一時的な接続: 必要な時だけ接続、不要時は切断

4. 開発効率の向上

GUIツール対応: DBeaver、pgAdmin等のGUIツールで直接接続可能
リアルタイム接続: データベースの状態をリアルタイムで確認
デバッグ効率化: 複雑なクエリの実行や結果確認が容易

従来方式との比較

項目 従来方式(Bastion) Session Manager方式
セキュリティ SSH鍵管理が必要 IAM認証のみ
運用負荷 Bastionホスト管理 管理不要
監査 ログ設定が必要 CloudTrailで自動記録
スケーラビリティ 手動設定 自動選択
コスト Bastionホスト費用 追加費用なし

トラブルシューティング

よくある問題と解決方法

  1. タスクが見つからない

    • ECSサービスが起動しているか確認
    • クラスター名とサービス名が正しいか確認
  2. DATABASE_HOSTパラメータが見つからない

    • SSMパラメータストアにDATABASE_HOSTが存在するか確認
    • 適切な権限があるか確認
  3. Session Manager接続エラー

    • ECSタスクにSession Managerエージェントがインストールされているか確認
    • 適切なIAMロールが設定されているか確認

セキュリティ上の注意点

  • このスクリプトは開発用です
  • 本番環境での使用は十分に注意してください
  • 接続はAWS Session Manager経由で暗号化されています
  • ローカルポート5555は一時的な接続用です
  • 使用後は必ず接続を終了してください

関連ドキュメント

We are hiring!

TAIANでは、このような開発・技術・思想に向き合い、未来をつくる仲間を一人でも多く探しています。少しでも興味を持っていただいた方は弊社の紹介ページをご覧ください。

https://taian-inc.notion.site/Entrance-Book-for-Engineer-1829555d9582804cad9ff48ad6cc3605

TAIANテックブログ

Discussion