前言

當從本機 DB 用戶端連線到位於私有子網路的 RDS/Aurora 時,過去多半是「在公有子網路放一台跳板 EC2,開放 22 埠,透過 SSH 通道運作」,但現在看起來已經以使用 Session Manager 的做法為主流,因此這次就把連線步驟整理如下。

使用 AWS Systems Manager(SSM)的 Session Manager,可以完全不開入站埠,也不用管理 SSH 金鑰,就能連到 EC2,並直接做 RDS 的埠轉送。

目錄

  1. 傳統跳板伺服器(SSH)的缺點
  2. Session Manager 的運作方式與前提條件
  3. 實際運用看看
  4. 成本、權限、日誌相關事項

1. 傳統跳板伺服器(SSH)的缺點

一般的架構大概會像下面這樣。

[本機電腦] --SSH(22)--> [跳板 EC2(公有子網路)] --3306/5432--> [RDS(私有子網路)]

22 埠會暴露在網際網路上

只要在跳板機的安全群組開放 22 埠,就會一直成為掃描與暴力破解的目標。即使把來源 IP 限縮,仍然會有以下問題。

  • 遠端工作或行動網路情境下,來源 IP 很難固定
  • 最後常常還是會開成 0.0.0.0/0,或是允許來源 IP 清單的增刪變成日常工作
  • 每次 OpenSSH 本身出現漏洞(例如:regreSSHion / CVE-2024-6387)時,都得趕緊補丁

SSH 金鑰的管理會變得仰賴個人

  • 每次成員加入或離開,都要新增/刪除 authorized_keys
  • 如果多人共用同一把金鑰,就無法知道到底是誰連上去的
  • 離職者的金鑰若忘了刪除,就會直接變成入侵管道
  • 金鑰檔(.pem)常常透過 Slack 或 Email 共享

「誰、什麼時候、做了什麼」很難追蹤

SSH 的日誌只會留在作業系統內(例如 /var/log/secure)。

  • 因為不會與 IAM 使用者/角色綁定,所以無法和 AWS 端的稽核日誌(CloudTrail)對照
  • 如果重新建立執行個體,日誌也會一起消失
  • 若要記錄到操作內容,必須自行建立機制

授權會變成與 IAM 另一套管理

AWS 的存取權限已經用 IAM(SSO)管理,但跳板機還是要再用 OS 使用者與 SSH 金鑰這套不同機制。權限盤點會分散在兩個地方,甚至可能出現 IAM 已經移除,但 SSH 金鑰還有效的情況。

2. Session Manager 的運作方式與前提條件

Session Manager 是什麼

Session Manager 是 AWS Systems Manager 的一項功能,可讓你透過瀏覽器或 AWS CLI,對受管節點(EC2 或內部部署伺服器)建立 Shell 連線。除了 Shell 連線之外,也支援埠轉送。

運作方式

[本機電腦 / 管理主控台]
        │ 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 整合) 稽核 只有作業系統內的日誌 CloudTrail + 工作階段日誌(CloudWatch Logs / S3) 轉接用 EC2 需要 轉接到 RDS 時需要 (後述) 我一開始誤以為「有了 SSM 就不需要跳板機」,但更精確地說,是不需要專門公開給 SSH 用的跳板機。因為 RDS/Aurora 本身不會安裝 SSM Agent,所以若要對 RDS 做埠轉送,仍然需要一台充當中繼的 EC2(受管節點)。
不過這台 EC2 不需要開任何入站,也不持有金鑰,因此性質與傳統跳板機完全不同。

前提條件

要用 SSM 連線,必須符合以下 3 個條件:

a. EC2 已安裝並啟動 SSM Agent
b. EC2 已掛載 IAM 角色(Instance Profile)
c. EC2 可透過 HTTPS(443) 連到 SSM 端點

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

整體架構

[本機電腦]
   │ 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 的存取與 migration 是透過 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 刪除對應的 key 後重新 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 endpoint(如果能從 CDK Output 取得就更方便)
  2. 若跳板已停止,先啟動它(可能要 1~2 分鐘才會註冊到 SSM)

    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":["<叢集 writer endpoint>"],"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 Tunnel 設定(因為 tunnel 由 Session Manager 負責)。

  1. 從 Secrets Manager 取得 DB 驗證資訊

    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,就無法開啟 shell,只能使用埠轉送權限
  • 將 TerminateSession / ResumeSession 的 Resource 限定為 ${aws:userid}-*,就能只操作自己建立的工作階段
  • 如果整合進 SSO 的權限集,只要把成員從 SSO 移除,就無法再連線,不會發生像 SSH 金鑰忘記刪除那樣的問題

工作階段日誌的記錄

  • 工作階段的開始與結束會透過 CloudTrail 連同 IAM 主體一起記錄。「誰、什麼時候、連到哪一台跳板」可以在不額外設定的情況下追蹤
  • 也可以在 Session Manager 的「設定」中,將 Shell 操作日誌保存到 CloudWatch Logs / S3

對於埠轉送的工作階段,不會記錄實際送往 DB 的 SQL 內容。
若需要以查詢為單位的稽核,則應併用 DB 端的稽核日誌(例如 PostgreSQL 的 pgaudit)。

結語

這次介紹了如何使用 Session Manager,從本機 DB 用戶端連線到私有子網路中的 RDS/Aurora。
實際導入後,最方便的地方是不用再管理 SSH 金鑰與來源 IP。
在這個架構下,雖然仍然需要一台中繼用 EC2,但只要將用途限定為本機端的 DB 連線,就能採用平常關機、需要時才開機的運作方式(節省成本)

感謝你讀到最後!

參考


原文出處:https://qiita.com/j-yasukawa/items/701554f14fe8b02ff34e


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝6   💬1   ❤️1
256
🥈
boluoji
📝8   💬2   ❤️3
154
🥉
mike
1
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登