「SSH鍵を複数人で共有している。誰がいつ何のために接続したか分からなくて怖い」
こういった声は、AWSでインフラを運用しはじめたLinuxエンジニアからよく聞きます。踏み台サーバー(Bastion Host)方式は「公開鍵認証+ポート22開放」が必須のため、鍵の紛失・共有・ローテーションが運用負荷になりがちです。
この記事では、AWS Systems Manager(SSM)のSession Managerを使って踏み台サーバーなしでEC2に接続する方法を解説します。IAMロールによるアクセス制御、Run Commandを使った一括コマンド実行、Parameter StoreによるDBパスワードなどの秘密情報管理まで、実務で使えるSSMの設計パターンをひとまとめに解説します。
動作確認環境: Amazon Linux 2023 / AWS CLI 2.17.x / AWS Systems Manager Agent 3.3.x
この記事のポイント
・aws ssm start-session でSSHなし・鍵なしでEC2に接続できる
・IAMロール(AmazonSSMManagedInstanceCore)のアタッチが唯一の前提条件
・Run Commandでタグ指定した複数EC2へ同時コマンドを実行できる
・Parameter StoreでDBパスワードをKMS暗号化して安全に管理できる
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
なぜ踏み台サーバーを廃止してSSMに移行すべきか
従来の踏み台サーバー方式では次のような問題が生じます。・ポート22の開放が必要: 踏み台EC2のセキュリティグループでポート22を外部に開放しなければなりません。ポート22は攻撃者が最初に狙うポートのひとつです
・SSH鍵の管理コスト: 開発者ごとに鍵を発行・失効する管理が必要です。誰かが退職した際のローテーションを忘れると、不正アクセスのリスクが残ります
・踏み台EC2自体の管理: 「管理するためのサーバー」を別途メンテナンスしなければならず、OS更新や設定変更が本来業務の足を引っ張ります
・接続ログの取得が煩雑: bashの履歴だけでは「誰がいつ何を実行したか」の監査証跡が不完全です
AWS Systems ManagerのSession Managerに移行すると、次のような設計が実現できます。
・ポート22を完全に閉じられる: セキュリティグループのインバウンドルールにSSHポートが不要になります。攻撃対象を大幅に削減できます
・IAMによる認証: SSH鍵の代わりにAWS IAMポリシーでアクセス制御します。IAMロールの無効化で即時に接続を遮断できます
・全セッションがCloudWatch Logsに記録: 誰がいつどのEC2に接続し何を実行したか、コマンドの出力まで含めた監査ログが自動で残ります
・踏み台サーバーが不要: プライベートサブネットのEC2へも直接接続できます(VPCエンドポイント設定時)
Session Managerの設定手順
1. EC2にIAMロールをアタッチする
Session Managerを使うにはEC2にSSMへのアクセス権限を持つIAMロールをアタッチする必要があります。# Step 1: IAMロールを作成(EC2がSSMを呼び出せる信頼ポリシー) aws iam create-role --role-name EC2-SSMRole --assume-role-policy-document '{ "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": {"Service": "ec2.amazonaws.com"}, "Action": "sts:AssumeRole" }] }' # Step 2: AWS管理ポリシー「AmazonSSMManagedInstanceCore」をアタッチ aws iam attach-role-policy --role-name EC2-SSMRole --policy-arn arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore # Step 3: EC2インスタンスプロファイルを作成してロールを紐づける aws iam create-instance-profile --instance-profile-name EC2-SSMProfile aws iam add-role-to-instance-profile --instance-profile-name EC2-SSMProfile --role-name EC2-SSMRole # Step 4: 既存EC2にインスタンスプロファイルをアタッチ aws ec2 associate-iam-instance-profile --instance-id i-0a1b2c3d4e5f67890 --iam-instance-profile Name=EC2-SSMProfile
2. SSM Agentのインストール確認
Session ManagerはEC2上で動作するSSM Agentを通じてコマンドを送信します。Amazon Linux 2023・Amazon Linux 2・Ubuntu Server 18.04以降はAmazon提供のAMIにSSM Agentがプリインストールされています。# SSM Agentの起動状態を確認 sudo systemctl status amazon-ssm-agent # 出力例(正常動作時) # amazon-ssm-agent.service - amazon-ssm-agent # Loaded: loaded (/lib/systemd/system/amazon-ssm-agent.service; enabled) # Active: active (running) since Tue 2026-07-07 10:23:15 UTC; 2h 14min ago # Main PID: 1247 (amazon-ssm-agen) # インストールされていない場合(Amazon Linux 2023) sudo dnf install -y amazon-ssm-agent sudo systemctl enable amazon-ssm-agent --now
3. VPCエンドポイントの設定(プライベートサブネット使用時)
プライベートサブネットのEC2にインターネットへのアウトバウンドがない場合、VPCエンドポイント(AWS PrivateLink)を使ってSSMとプライベートに通信させます。Session Managerには3つのエンドポイントが必要です。# SSM用VPCエンドポイントを3つ作成(ap-northeast-1の例) for svc in ssm ssmmessages ec2messages; do aws ec2 create-vpc-endpoint --vpc-id vpc-0abc1234 --service-name com.amazonaws.ap-northeast-1.${svc} --vpc-endpoint-type Interface --subnet-ids subnet-0def5678 subnet-0ghi9012 --security-group-ids sg-0ssm1234 --private-dns-enabled echo "Created: ${svc}" done # 出力例 # Created: ssm # Created: ssmmessages # Created: ec2messages
Session ManagerでSSHなしでEC2に接続する方法
1. Session Manager用プラグインのインストール(初回のみ)
`aws ssm start-session` コマンドの実行には、作業するPC(自分のPC)へ「Session Managerプラグイン」をインストールする必要があります。# Linux(Amazon Linux 2023 / RHEL9 / AlmaLinux 9) sudo dnf install -y https://s3.amazonaws.com/session-manager-downloads/plugin/latest/linux_64bit/session-manager-plugin.rpm # macOS(Homebrewを使う場合) brew install --cask session-manager-plugin # インストール確認 session-manager-plugin --version # 1.2.650.0
2. aws ssm start-session でEC2に接続する
インスタンスIDを指定してセッションを開始します。パスワードも鍵ファイルも不要です。# 接続先のEC2インスタンスIDをタグ名で確認 aws ec2 describe-instances --filters "Name=tag:Name,Values=web-server-prod" --query 'Reservations[*].Instances[*].{ID:InstanceId,State:State.Name}' --output table # 出力例 # ------------------------------------------ # | DescribeInstances | # +---------------------+------------------+ # | ID | State | # +---------------------+------------------+ # | i-0a1b2c3d4e5f67890 | running | # +---------------------+------------------+ # Session Managerで接続(SSH不要・鍵不要) aws ssm start-session --target i-0a1b2c3d4e5f67890 # 接続後のプロンプト(シェルが起動する) # Starting session with SessionId: tomohiro.miyazaki-0a1b2c3d4e5f67890 # sh-5.2$ whoami # ssm-user # sh-5.2$ sudo su - # [root@ip-10-0-1-42 ~]# hostname # ip-10-0-1-42.ap-northeast-1.compute.internal
3. 接続ログをCloudWatch Logsに保存する設定
Session Managerのセッションログはデフォルトでは保存されません。監査証跡を残すにはSSMのセッション設定(SSM-SessionManagerRunShell)を更新します。# CloudWatch Logsのロググループを作成 aws logs create-log-group --log-group-name /ssm/session-manager # セッション設定を更新(ログ出力を有効化) aws ssm update-document --name SSM-SessionManagerRunShell --document-version "\$LATEST" --content '{ "schemaVersion": "1.0", "sessionType": "Standard_Stream", "inputs": { "cloudWatchLogGroupName": "/ssm/session-manager", "cloudWatchEncryptionEnabled": false, "idleSessionTimeout": "20" } }' # セッション終了後にログを確認 aws logs get-log-events --log-group-name /ssm/session-manager --log-stream-name i-0a1b2c3d4e5f67890 --query 'events[].message' --output text | head -30
Run CommandでEC2に一括コマンドを実行する方法
Run CommandはSSH接続なしに複数EC2へ同時コマンドを送信できる機能です。パッチ適用やサービス再起動などの定型作業を、踏み台サーバーを経由せずに実行できます。1. 単一EC2にコマンドを送信する
# AWS-RunShellScriptドキュメントを使って単一EC2でコマンド実行 aws ssm send-command --instance-ids i-0a1b2c3d4e5f67890 --document-name "AWS-RunShellScript" --parameters '{"commands":["df -h","systemctl status nginx | head -10"]}' --comment "ディスク確認とnginxステータス" # 出力例(コマンドIDが返る) # { # "Command": { # "CommandId": "ab12cd34-ef56-7890-gh12-ij3456klmnop", # "DocumentName": "AWS-RunShellScript", # "Status": "Pending" # } # } # 実行結果を取得 aws ssm get-command-invocation --command-id "ab12cd34-ef56-7890-gh12-ij3456klmnop" --instance-id i-0a1b2c3d4e5f67890 --query '{Status:Status,Output:StandardOutputContent}'
2. タグを使って複数EC2に一括実行する
# tag:Environment=production の全EC2にセキュリティアップデートを一括適用 aws ssm send-command --targets 'Key=tag:Environment,Values=production' --document-name "AWS-RunShellScript" --parameters '{"commands":["sudo dnf update -y --security"]}' --comment "セキュリティアップデート一括適用" --timeout-seconds 600 # 全対象インスタンスの実行状況を一覧表示 aws ssm list-command-invocations --command-id "ab12cd34-ef56-7890-gh12-ij3456klmnop" --details --query 'CommandInvocations[*].{ID:InstanceId,Status:Status}' --output table # 出力例(3台のEC2に実行した場合) # ------------------------------------------- # | ListCommandInvocations | # +---------------------+-------------------+ # | ID | Status | # +---------------------+-------------------+ # | i-0a1b2c3d4e5f67890 | Success | # | i-0b2c3d4e5f678901 | Success | # | i-0c3d4e5f6789012 | Success | # +---------------------+-------------------+
Parameter Storeで秘密情報を安全に管理する設計
Parameter Storeを使うとDBパスワードやAPIキーをコードやEC2のファイルに直書きせずに管理できます。KMSによる暗号化(SecureString型)と階層パスによる整理が設計の基本です。1. パスの命名規則(階層設計)
# 推奨の命名規則(/{環境}/{アプリ名}/{パラメータ名}) /prod/api-server/db-password /prod/api-server/db-endpoint /prod/api-server/stripe-secret-key /staging/api-server/db-password # パスに階層をつけることで、アプリ単位・環境単位でまとめて取得できる
2. SecureStringでKMS暗号化して登録・取得する
# SecureString型でパラメータを登録(KMSで自動暗号化される) aws ssm put-parameter --name "/prod/api-server/db-password" --value "P@ssw0rd!Secure2026" --type SecureString --description "本番DBの接続パスワード" # パスワードを復号して取得(--with-decryptionが必要) aws ssm get-parameter --name "/prod/api-server/db-password" --with-decryption --query 'Parameter.Value' --output text # 出力例 # P@ssw0rd!Secure2026 # 階層パス配下のパラメータを一覧表示 aws ssm get-parameters-by-path --path "/prod/api-server/" --recursive --query 'Parameters[*].{Name:Name,Type:Type}' --output table # 出力例 # ---------------------------------------------------------- # | GetParametersByPath | # +-------------------------------------+-----------------+ # | Name | Type | # +-------------------------------------+-----------------+ # | /prod/api-server/db-endpoint | String | # | /prod/api-server/db-password | SecureString | # | /prod/api-server/stripe-secret-key | SecureString | # +-------------------------------------+-----------------+
3. EC2のスクリプトからParameter Storeを参照する
#!/bin/bash # アプリ起動スクリプト(Parameter Storeから秘密情報を取得する例) DB_PASS=$(aws ssm get-parameter --name "/prod/api-server/db-password" --with-decryption --query 'Parameter.Value' --output text --region ap-northeast-1) DB_HOST=$(aws ssm get-parameter --name "/prod/api-server/db-endpoint" --query 'Parameter.Value' --output text --region ap-northeast-1) # 取得した値を環境変数にセットしてアプリを起動 export DATABASE_PASSWORD="${DB_PASS}" export DATABASE_HOST="${DB_HOST}" ./myapp --config /etc/myapp/config.yaml
AWSのLinuxサーバー構築と管理を体系的に学びたい方には、AWSでLinuxサーバーを学ぶ実践ロードマップもあわせてご覧ください。
トラブルシュート|Session Manager接続できない時の確認手順
1. IAMロールのアタッチ状況を確認する
接続できない最多原因はIAMロールがアタッチされていないことです。# EC2に紐付いているIAMロールを確認 aws ec2 describe-instances --instance-ids i-0a1b2c3d4e5f67890 --query 'Reservations[0].Instances[0].IamInstanceProfile.Arn' # 出力例(設定済みの場合) # "arn:aws:iam::123456789012:instance-profile/EC2-SSMProfile" # IAMロールに必要なポリシーが付いているか確認 aws iam list-attached-role-policies --role-name EC2-SSMRole --query 'AttachedPolicies[*].PolicyName' --output text # 出力例(正常) # AmazonSSMManagedInstanceCore
2. SSM Agentの動作確認とマネージドインスタンス登録状態
# EC2にSSH接続できる場合はAgentのログを確認 sudo journalctl -u amazon-ssm-agent -n 30 --no-pager # 典型的なエラーと原因 # "Failed to load instance info from metadata" # → EC2のIAMロールが設定されていない # "No credential providers found" # → IMDSv2のみ有効な環境でIAMロール未設定 # Agentを再起動して状態確認 sudo systemctl restart amazon-ssm-agent sudo systemctl status amazon-ssm-agent # マネジメントコンソール側でのインスタンス登録状態確認 aws ssm describe-instance-information --query 'InstanceInformationList[*].{ID:InstanceId,Status:PingStatus,Agent:AgentVersion}' --output table # 出力例(正常登録済み) # --------------------------------------------------------- # | DescribeInstanceInformation | # +---------------------+---------+---------------------+ # | ID | Agent | Status | # +---------------------+---------+---------------------+ # | i-0a1b2c3d4e5f67890 | 3.3.xxx | Online | # +---------------------+---------+---------------------+ # "Online"でなく一覧に表示されない場合はIAMロールかSSM Agentを確認する
本記事のまとめ
| やりたいこと | コマンド / 設定 |
|---|---|
| EC2にSession Managerで接続する | aws ssm start-session --target i-xxxx |
| マネージドインスタンスの登録状態を確認する | aws ssm describe-instance-information |
| 単一EC2にコマンドを送信する | aws ssm send-command --instance-ids i-xxxx --document-name AWS-RunShellScript |
| タグ指定で複数EC2に一括実行する | aws ssm send-command --targets Key=tag:Env,Values=prod ... |
| コマンドの実行結果を取得する | aws ssm get-command-invocation --command-id xxx --instance-id i-xxx |
| SecureStringでパラメータを登録する | aws ssm put-parameter --type SecureString ... |
| Parameter Storeの値を復号して取得する | aws ssm get-parameter --with-decryption ... |
| VPCエンドポイントを作成する(プライベートサブネット用) | aws ec2 create-vpc-endpoint --service-name com.amazonaws.ap-northeast-1.ssm ... |
AWSのインフラをSSHなし・IAM管理の「現代的な設計」で構築しませんか?
Session Managerのコマンドは調べれば分かります。でも「IAMポリシーをどう設計するか」「VPCエンドポイントをどう組み合わせるか」を説明できますか?
現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼントしています。
「独学の時間がもったいない」「プロから直接、現場の技術を最短で学びたい」という本気の方には、2日で実務レベルのスキルが身につく【初心者向けハンズオンセミナー】も開催しています。
3,100名以上が実践した「型」を無料で公開中
プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。
登録10秒/合わなければ解除3秒 / 詳細はこちら

無料メルマガで学習を続ける
Linuxの実践スキルをメールで毎週お届け。
登録は1分、解除もいつでも可。
登録無料・いつでも解除できます