SSH | AWS Session Manager
Session Manager と SSH についてまとめています。
SSH over Session Manager
ローカルのシェルから EC2 にログインする際、以下の SSH 設定で Session Manager をプロキシとして使えます。 EC2 との接続に 22 番ポートを使わなくて良い点がメリットになります。
host i-* mi-*
ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
この方法では Session Manager はあくまでネットワーク経路を置き換えるだけで、SSH 鍵は引き続き必要です。
通常の SSH:
ローカル ---[TCP port 22]---▶ EC2
└─ SSH 鍵で認証
SSH over Session Manager:
ローカル ---[SSM トンネル]---▶ EC2
└─ SSH 鍵で認証(変わらず必要)
接続方式の比較
| 通常の SSH | SSH over Session Manager | Session Manager のみ(start-session) | |
|---|---|---|---|
| ネットワーク経路 | port 22 直接 | SSM トンネル | SSM トンネル |
| SSH 鍵 | 必要 | 必要 | 不要 |
| 認証 | SSH 鍵 | SSH 鍵 | IAM ロールのみ |
| port 22 開放 | 必要 | 不要 | 不要 |
なぜ SSH over Session Manager を使うか
SSH 鍵が残るにもかかわらず使う理由:
- SG の port 22 を開けなくてよい(ネットワーク攻撃面の縮小)
- 踏み台サーバー不要
- SCP / SFTP / ポートフォワードなど SSH の機能をそのまま使える
長期認証情報の排除の観点
Session Manager のみ(aws ssm start-session)で shell に入る方法であれば SSH 鍵も不要になり、IAM ロールだけで完結します。SSH over Session Manager はセキュリティを改善しつつ SSH の利便性を保つ折衷案という位置づけです。
踏み台サーバーが不要になる理由
踏み台サーバーが必要なのは、EC2 がプライベートサブネットに配置されており外部から TCP で到達できない場合です。
【踏み台サーバーが必要な構成】
インターネット
│
▼
踏み台 EC2(パブリックサブネット、port 22 open)
│
▼
対象 EC2(プライベートサブネット)
Session Manager は SSM Agent(EC2 側)が AWS の SSM エンドポイントに対してアウトバウンド接続を張ります。そのためにインバウンドの port 22 が不要になります。
【Session Manager を使った構成】
EC2(プライベート)─── アウトバウンド HTTPS ───▶ SSM エンドポイント(AWS)
▲
ローカル ─────────────────────────────────────────────┘
外から EC2 に入る」のではなく EC2 が外(SSM)に繋ぎに行く構造のため、プライベートサブネットでも踏み台なしで接続できます。
SSM エンドポイントの位置づけ
SSM エンドポイント(ssm.ap-northeast-1.amazonaws.com)は VPC とは独立して AWS が提供するサービスエンドポイントです。
AWS グローバルネットワーク
┌──────────────────────────────────────────────────────┐
│ SSM エンドポイント(ssm.ap-northeast-1.amazonaws.com)│
└──────────────────────────────────────────────────────┘
▲
│ HTTPS(アウトバウンド)
┌────────────────────────┐
│ VPC │
│ ┌──────────────────┐ │
│ │ EC2(プライベート)│ │
│ └──────────────────┘ │
└────────────────────────┘
EC2 から SSM エンドポイントへの疎通方法は 2 つあります。
| 方法 | 概要 |
|---|---|
| NAT Gateway | プライベートサブネット → NAT GW → インターネット → SSM エンドポイント |
| VPC エンドポイント(PrivateLink) | VPC 内に SSM 用のエンドポイントを作り、インターネットを経由しない |
EC2 側の前提条件
- SSM Agent がインストールされ、起動していること(Amazon Linux や Ubuntu の主要 AMI ではプリインストール)
- インスタンスプロファイルに SSM の権限(例:AWS 管理ポリシー
AmazonSSMManagedInstanceCore)が付与されていること - SSM への疎通経路(NAT Gateway または VPC エンドポイント)があること
VPC エンドポイントを使う場合、ssm だけでは足りません。Session Manager には以下のエンドポイントが必要です。
| エンドポイント | 用途 |
|---|---|
ssm |
SSM API |
ssmmessages |
Session Manager のセッション通信(データチャネル) |
ec2messages |
古いバージョンの SSM Agent(3.3.40.0 未満)でのみ必要 |
Session Manager による接続
GitHub Action
準備
vars.XXXが登録されていることAWS_ACCOUNT_ID:AWS アカウント IDAWS_EC2_INSTANCE:接続先の EC2 インスタンス IDAWS_EC2_AZ:接続先の EC2 の AZSSH_HOST_KEY:EC2 の SSH ホスト公開鍵
- AWS に deployment-role を作成して信頼関係でリポジトリを許可していること
- deployment-role に接続に必要な権限を付与していること
- EC2 側で EC2 Instance Connect が利用可能で、OS ユーザー
github-actionsが存在すること(事前に作成しておく)
deployment-role の信頼関係
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {
"Federated": "arn:aws:iam:::oidc-provider/token.actions.githubusercontent.com"
},
"Action": "sts:AssumeRoleWithWebIdentity",
"Condition": {
"StringEquals": {
"token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
},
"StringLike": {
"token.actions.githubusercontent.com:sub": "repo:sample-org/sample-repo:*"
}
}
}
]
}
GitHub の sub は repo:<org>/<repo>:... の形式です(sample-org/sample-repo:* のように repo: を付けないと一致しません)。
ワークフローで environment を使っている場合、sub は repo:<org>/<repo>:environment:<環境名> になります。可能であれば repo:sample-org/sample-repo:environment:production のように環境まで絞り込んでください。
deployment-role の権限ポリシー
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": "ssm:StartSession",
"Resource": [
"arn:aws:ec2:ap-northeast-1:123456789012:instance/i-1234567890abcdef0",
"arn:aws:ssm:ap-northeast-1::document/AWS-StartSSHSession"
]
},
{
"Effect": "Allow",
"Action": "ssm:TerminateSession",
"Resource": "arn:aws:ssm:*:*:session/${aws:userid}-*"
},
{
"Effect": "Allow",
"Action": "ec2-instance-connect:SendSSHPublicKey",
"Resource": "arn:aws:ec2:ap-northeast-1:123456789012:instance/i-1234567890abcdef0",
"Condition": {
"StringEquals": {
"ec2:osuser": "github-actions"
}
}
}
]
}
※ 123456789012 と i-1234567890abcdef0 はダミーです。
.github/deployment.yaml
name: Deployment
#...
jobs:
deployment:
name: Deployment
runs-on: ubuntu-22.04
environment: $
permissions:
contents: read
id-token: write
steps:
- uses: actions/checkout@v4
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@v4
with:
role-duration-seconds: 1800
# AWS の IAM に同名のロールを作成し、信頼関係でこのリポジトリを許可する。
# OIDC の場合はロール名だけでは指定できないため、ARN で指定する。
role-to-assume: arn:aws:iam::$:role/deployment-role
aws-region: ap-northeast-1
- name: Setup SSH through AWS SSM
run: |
mkdir -p ~/.ssh
echo "$ ssh-ed25519 $" >> ~/.ssh/known_hosts
cat >> ~/.ssh/config <<EOT
Host i-* mi-*
ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
EOT
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N ''
aws ec2-instance-connect send-ssh-public-key \
--instance-id $ \
--instance-os-user github-actions \
--availability-zone $ \
--ssh-public-key file://~/.ssh/id_ed25519.pub
until ssh github-actions@$ exit; do
echo "Waiting for SSH connection..."
sleep 3
done
AWS が GitHub(ワークフロー)を認証
- name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: role-duration-seconds: 1800 role-to-assume: arn:aws:iam::$:role/deployment-role aws-region: ap-northeast-1
GitHub が発行した OIDC トークンを AWS(IAM / STS)が検証し、ロールの信頼関係(aud / sub の条件)を満たす場合のみ一時認証情報を発行します。
GitHub が EC2 の正当性を認証
ワークフローが不正なサーバーに接続しないためです。
echo “i-1234567890 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyDataHere1234567890abcdefghijklmnopqrstuvwxyz” » ~/.ssh/known_hosts
AAAAC3NzaC1lZDI1NTE5AAAAIExampleKeyDataHere1234567890abcdefghijklmnopqrstuvwxyz は EC2 サーバーの公開鍵(値はダミー)。
known_hosts に登録したホスト鍵と接続先の鍵が一致するかを SSH が検証します。この検証を有効にするため、StrictHostKeyChecking no は設定しません(設定すると known_hosts による検証が無効になり、なりすましを検出できなくなります)。
プラグイン
ローカルのターミナルで Session Manager を使用するには Session Manager プラグイン が必要。
macOS での Session Manager プラグインのインストール