17
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SSM Session Managerを使ってDBに接続する

17
Last updated at Posted at 2026-10-30

はじめに

プライベートサブネットにあるRDS/AuroraへローカルのDBクライアントから接続するとき、「パブリックサブネットに踏み台EC2を置き、22番ポートを開けてSSHトンネルで運用」していたのですが、現在はSession Managerを使用する運用が主流のようなので、今回はその接続手順をまとめてみました。

AWS Systems Manager(SSM)のSession Managerを使うと、インバウンドポートを一切開けず、SSH鍵も管理せずにEC2へ接続でき、そのままRDSへのポートフォワードもできます。

目次

  1. 従来の踏み台サーバー(SSH)のデメリット
  2. Session Managerの仕組みと前提条件
  3. 実際に運用してみた
  4. コスト・権限・ログまわりについて

1. 従来の踏み台サーバー(SSH)のデメリット

一般的な構成は以下のようになっているかと思います。

[ローカルPC] --SSH(22)--> [踏み台EC2(パブリックサブネット)] --3306/5432--> [RDS(プライベートサブネット)]

22番ポートがインターネットに露出する

踏み台のセキュリティグループで22番を開ける以上、常にスキャンやブルートフォースの標的になりえます。接続元IPを絞っても、以下のような問題が残ります。

  • リモートワークやモバイル回線で接続元IPが固定できない
  • 結局0.0.0.0/0で開けてしまいがち。または許可接続元IPリストの追加・削除が日常業務になる
  • OpenSSH自体の脆弱性(例: regreSSHion/CVE-2024-6387)が出るたびに、パッチ当てを急ぐ必要がある

SSH鍵の管理が属人化する

  • メンバーの加入・離脱のたびにauthorized_keysの追加・削除が必要
  • 1本の鍵を複数人で使い回すと、誰が接続したのか分からなくなる
  • 退職者の鍵を消し忘れると、それがそのまま侵入経路になる
  • 鍵ファイル(.pem)がSlackやメールで共有されがち

「誰が・いつ・何をしたか」を追いにくい

SSHのログはOS内(/var/log/secureなど)に残るだけです。

  • IAMのユーザー/ロールと紐づかないため、AWS側の監査ログ(CloudTrail)と突き合わせられない
  • インスタンスを作り直すとログも消える
  • 操作内容まで残すには、自前で仕組みを用意する必要がある

認可がIAMと二重管理になる

AWSのアクセス権はIAM(SSO)で管理しているのに、踏み台だけはOSユーザーとSSH鍵という別の仕組みになります。権限の棚卸しが2か所に分かれ、IAMを剥がしてもSSH鍵が生きている、という状態が起こり得ます。

2. Session Managerの仕組みと前提条件

Session Managerとは

Session ManagerはAWS Systems Managerの機能の一つで、マネージドノード(EC2やオンプレサーバー)に対して、ブラウザやAWS CLIからシェル接続できるサービスです。シェル接続のほか、ポートフォワードもできます。

仕組み

[ローカルPC / マネジメントコンソール]
        │ HTTPS (443)
        ▼
[AWS Systems Managerエンドポイント]
        ▲
        │ HTTPS (443) ※EC2側からのアウトバウンド通信のみ
[EC2 (SSM Agent)]

ポイントは、EC2上のSSM AgentがAWS側へアウトバウンドで接続しにいくことです。
そのため、EC2側のセキュリティグループにインバウンドルールは不要になります。

従来のSSH踏み台との比較

項目 SSH + 踏み台 SSM Session Manager
インバウンドポート 22番を開放 不要
接続元IPの管理 必要 不要
鍵管理 SSH鍵の配布・ローテーションが必要 不要(IAM権限で制御)
アクセス制御 セキュリティグループ / authorized_keys IAMポリシー(SSOと統合)
監査 OS内のログのみ CloudTrail + セッションログ(CloudWatch Logs / S3)
中継用EC2 必要 RDSへ中継する場合は必要(後述)

私は最初「SSMなら踏み台が要らない」、と勘違いしていましたが、正確にはSSH用に公開する踏み台が要らない であって、RDS/Aurora自体にはSSM Agentが入らないため、RDSへポートフォワードするには中継役のEC2(マネージドノード)が1台必要になります。
ただしこのEC2はインバウンドを一切開けず、鍵も持たないので、従来の踏み台とは性質がまったく違います。

前提条件

SSMで接続するには、次の3つが揃っている必要があります。

a. SSM AgentがEC2にインストールされ、起動している
b. EC2にIAMロール(インスタンスプロファイル)がアタッチされている
c. EC2からSSMのエンドポイントへHTTPS(443)で到達できる

a. SSM Agent

Amazon Linux 2 / Amazon Linux 2023 / Ubuntu Server(公式 AMI)/ Windows Server(公式 AMI)などにはプリインストール済みです。

EC2上の「SSM Agent」が動いているか確認するコマンド(EC2の中で実行)
# Amazon Linux 2 / 2023
sudo systemctl status amazon-ssm-agent

# Ubuntu (snap でインストールされている場合)
sudo snap services amazon-ssm-agent

b. IAM ロール

EC2に、AWS管理ポリシーAmazonSSMManagedInstanceCoreを付与したIAMロールをアタッチします。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": { "Service": "ec2.amazonaws.com" },
      "Action": "sts:AssumeRole"
    }
  ]
}

アカウント単位でDefault Host Management Configuration(DHMC)を有効にすると、インスタンスごとに IAMロールを付けなくてもSSMの管理対象にできます(IMDSv2が必要)

c. ネットワーク

EC2から次のエンドポイントへ443番で通信できる必要があります。

  • ssm.<region>.amazonaws.com
  • ssmmessages.<region>.amazonaws.com
  • ec2messages.<region>.amazonaws.com

経路は大きく 3 通りあります。

経路 構成 コスト感(東京リージョン目安)
パブリックサブネット + IGW EC2にパブリックIPを付け、アウトバウンドのみ使う パブリックIPv4 $0.005/時(起動中だけ。常時なら約 3.7/月)
プライベートサブネット + NAT Gateway NAT経由で外へ出る $0.062/時 + $0.062/GB(約 $45/月〜)
プライベートサブネット + VPC エンドポイント 上記3つのInterface型エンドポイントを作る $0.014/時×3個×AZ数(2AZで約$61/月)

VPCエンドポイントを使う場合は、エンドポイントに付けるセキュリティグループでEC2からの443番のインバウンドを許可します。また、VPCの「DNS ホスト名」「DNS 解決」を有効にしておく必要があります。

「パブリックサブネットに置く」と聞くと身構えるかもしれませんが、インバウンドが空のセキュリティグループであれば、外から到達できる経路はありません。中継用EC2しか置かない用途なら、これが最も安価になります。

3. 実際に運用してみた

ここからは、実際に運用している構成を紹介します。
構成例: SSM専用踏み台でAurora PostgreSQLに接続する

全体像

[ローカルPC]
   │ localhost:15432(ポートフォワード)
   │ aws ssm start-session (HTTPS 443 / IAM認可)
   ▼
[SSMエンドポイント]
   ▲ HTTPS 443(アウトバウンドのみ)
   │
[踏み台 EC2]  ← パブリックサブネット / SGインバウンド: なし / キーペア: なし
   │ 5432(踏み台SG → クラスタSGのみ許可)
   ▼
[Aurora PostgreSQL Serverless v2]  ← プライベートサブネット(インターネット経路なし)

※SG:セキュリティグループ

設計のポイントは次のとおりです。

  • Auroraはプライベートサブネットに置き、インターネットへの経路を一切持たせない(NAT Gatewayもなし)
  • 踏み台はパブリックサブネット + IGWに置く。SSMエンドポイントへ出るためだけに使い、インバウンドは空にする
  • 踏み台はSSHを使わない、キーペアなし、セキュリティグループのインバウンドなし
  • DB への到達は踏み台SG → クラスタSGの5432番のみ許可する
  • 認可はSSOロールのIAM(ssm:StartSession)で一元管理する
  • 踏み台はt4g.nanoを普段は停止しておき、使うときだけ起動する(コスト最安)

アプリケーション(Lambda)からのDBアクセスとマイグレーションはRDS Data API(HTTPS + IAM)経由で行っており、踏み台は使用していません。踏み台は「人がGUIクライアントでDBを覗く」ためだけの経路です。用途を絞ることで、普段は停止しておく運用にできています。

合わせて読みたい記事:(AWS)RDS Data APIを使ってみた

CDK での定義

AWS CDKのBastionHostLinuxを使うと、SSM接続に必要なインスタンスロール(AmazonSSMManagedInstanceCore)を自動で付けてくれます。

import {
  AmazonLinuxCpuType, BastionHostLinux, BlockDeviceVolume, InstanceClass,
  InstanceSize, InstanceType, IpAddresses, MachineImage, SubnetType, Vpc,
} from 'aws-cdk-lib/aws-ec2'

const vpc = new Vpc(this, 'DbVpc', {
  ipAddresses: IpAddresses.cidr('10.0.0.0/16'),
  maxAzs: 2,
  natGateways: 0, // NAT Gatewayは作らない
  subnetConfiguration: [
    // Aurora用。インターネット経路なし
    { name: 'db-isolated', subnetType: SubnetType.PRIVATE_ISOLATED, cidrMask: 24 },
    // 踏み台用。SSMエンドポイントへのアウトバウンドのためだけに使う
    { name: 'bastion-public', subnetType: SubnetType.PUBLIC, cidrMask: 24 },
  ],
})

// const cluster = new DatabaseCluster(this, 'DbCluster', { vpc, vpcSubnets: { subnetType: SubnetType.PRIVATE_ISOLATED }, ... })

const bastion = new BastionHostLinux(this, 'Bastion', {
  vpc,
  subnetSelection: { subnetType: SubnetType.PUBLIC },
  instanceName: 'myapp-dev-bastion',
  instanceType: InstanceType.of(InstanceClass.T4G, InstanceSize.NANO),
  machineImage: MachineImage.latestAmazonLinux2023({
    cpuType: AmazonLinuxCpuType.ARM_64,
    // AMIをcdk.context.jsonに固定する(理由は後述)
    cachedInContext: true,
  }),
  requireImdsv2: true,
  blockDevices: [
    { deviceName: '/dev/xvda', volume: BlockDeviceVolume.ebs(8, { encrypted: true }) },
  ],
})

// 踏み台SG → クラスタSGの5432 番だけを許可する
cluster.connections.allowDefaultPortFrom(bastion, 'SSM port forwarding from bastion')

BastionHostLinuxはキーペアを設定せず、セキュリティグループのインバウンドも空のまま作成されます。

AMIはcachedInContext: trueで固定します。latestAmazonLinux2023()は既定ではSSMパラメータで最新AMIを解決します。そのため、新しいAMIが出るたびにcdk deployでインスタンスが置き換えになり、停止中の踏み台が勝手に起動した状態で作り直されます。
AMIを更新したいときは、cdk.context.jsonから該当キーを消して再synthします。

接続手順

前提:

  • AWS CLI v2とSession Managerプラグインがインストール済み
# Session Manager プラグイン(macOS)
brew install --cask session-manager-plugin
session-manager-plugin --version
  • 対象アカウントのSSOプロファイルでログイン済み(aws sso login --profile <profile>)

  1. 踏み台のインスタンスIDとDBエンドポイントを確認する(CDKのOutputなどから確認できるようにしておくと楽)

  2. 踏み台が停止していれば起動する(SSMに登録されるまで1〜2分かかるかも)

    aws ec2 start-instances --instance-ids i-0123456789abcdef0
    
    # PingStatus が Online になれば接続可能
    aws ssm describe-instance-information \
      --query "InstanceInformationList[].[InstanceId,PingStatus]" --output table
    
  3. ポートフォワードを開始する(このターミナルは開いたままにする)

    aws ssm start-session \
      --target i-0123456789abcdef0 \
      --document-name AWS-StartPortForwardingSessionToRemoteHost \
      --parameters '{"host":["<クラスタのライターエンドポイント>"],"portNumber":["5432"],"localPortNumber":["15432"]}'
    
  4. DB クライアントから localhost:15432 に接続する(他の数字を設定した場合、後の手順を置き換える)

    psql "host=localhost port=15432 dbname=<DB名> user=<ユーザー名> sslmode=require"
    

    ※パスワードはSecrets Managerに保存したDB認証情報から取得します。DBeaverなどのGUIクライアントを使う場合は、下記「DBeaverで接続する場合」を参照してください。

  5. 使い終わったらCtrl+Cでポートフォワードを止め、踏み台を停止する(コスト削減)

    aws ec2 stop-instances --instance-ids i-0123456789abcdef0
    

DBeaverで接続する場合

GUIクライアントのDBeaverからも、手順3のポートフォワードを開いたままlocalhost:15432に接続するだけで使えます。
DBeaver側のSSHトンネル設定は使いません(トンネルはSession Managerが担当するため)。

  1. DB認証情報をSecrets Managerから取得する

    aws secretsmanager get-secret-value \
      --secret-id <シークレット名またはARN> \
      --query SecretString --output text \
      --profile <profile>
    # => {"username":"...","password":"...", ...}
    
  2. DBeaverを起動し、メニューの「データベース(Database)」→「新しいデータベース接続(New Database Connection)」を開く

  3. 一覧から「PostgreSQL」を選んで「次へ(Next)」

スクリーンショット 2026-10-30 15.15.05.png

  1. 「メイン(Main)」タブに接続情報を入力する

    項目 設定値
    接続方法(Connect by) Host
    ホスト(Host) localhost
    ポート(Port) 15432(手順3のlocalPortNumberおそらく5432)
    データベース(Database) <DB名>
    認証(Authentication) Database Native
    ユーザー名 / パスワード 1で取得したusername / password

スクリーンショット 2026-10-30 15.16.07.png

  1. 「接続のテスト(Test Connection)」を押す(初回はPostgreSQLドライバーのダウンロードを求められるので、ダウンロードする)
  2. 「Connected」と表示されたら「完了(Finish)」で保存する
  • ポートフォワードのターミナルを閉じる、またはCtrl+Cで止めると、DBeaverの接続も切れます
  • Session Managerのセッションは一定時間操作がないと切断されます(アイドルタイムアウト、既定20分)。切れたら手順3を再実行し、DBeaverで再接続します

4. コスト・権限・ログまわりについて

コスト比較

Session Manager自体はEC2に対して追加料金なしで使えます。つまり「SSHかSSMか」だけで料金はほとんど変わりません。

接続するユーザー側のIAM権限管理

「誰がどのインスタンスに、どの方法で接続できるか」はIAMポリシーで制御します。次の例は、「Env=devタグが付いた踏み台に、RDS向けのポートフォワードだけを許可する」場合の例です。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "ssm:StartSession",
      "Resource": "arn:aws:ec2:ap-northeast-1:123456789012:instance/*",
      "Condition": {
        "StringEquals": { "ssm:resourceTag/Env": "dev" }
      }
    },
    {
      "Effect": "Allow",
      "Action": "ssm:StartSession",
      "Resource": "arn:aws:ssm:ap-northeast-1::document/AWS-StartPortForwardingSessionToRemoteHost"
    },
    {
      "Effect": "Allow",
      "Action": ["ssm:TerminateSession", "ssm:ResumeSession"],
      "Resource": "arn:aws:ssm:*:*:session/${aws:userid}-*"
    }
  ]
}
  • SSMドキュメントをAWS-StartPortForwardingSessionToRemoteHostに絞れば、シェルは開けず、ポートフォワードだけできる**権限になります
  • TerminateSession / ResumeSessionのResourceを${aws:userid}-*に絞ることで、自分が開始したセッションだけを操作できるようにできます
  • SSOの権限セットに組み込めば、メンバーの離脱はSSOから外すだけで接続できなくなります。SSH鍵の消し忘れのような問題は起きません

セッションログの記録

  • セッションの開始・終了はCloudTrailにIAMプリンシパル付きで記録されます。「誰が・いつ・どの踏み台に」接続したかは追加設定なしで追うことが可能です
  • Session Managerの「設定」でシェル操作のログをCloudWatch Logs / S3に保存することもできます

ポートフォワードのセッションでは、DBに流れるSQLの中身までは記録されません。
クエリ単位の監査が必要な場合は、DB側の監査ログ(PostgreSQLのpgauditなど)を併用します。

おわりに

今回は、Session Managerを使って、ローカルのDBクライアントからプライベートサブネットのRDS/Auroraへ接続する構成を紹介しました。
導入して一番便利だと感じたのは、SSH鍵や接続元IPの管理が不要になった点です。
今回の構成では中継用EC2は引き続き必要ですが、用途をローカルからのDB接続に絞ることで、必要なときだけ起動する運用にできました(コスト削減)

最後まで読んでいただきありがとうございました!

参考

17
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?