目次
9 章

EC2 運用 — security group, key pair, SSM

EC2 運用の日常の道具たち。Security Group ルールの設計、NACL との違い、key pair の限界と SSM Session Manager、IMDSv2、そして AMI でインスタンスの骨格を固める方法まで整理します。

第8章 EC2 と VPC の基礎 で EC2 を1台立ち上げる図をつかみました。本章はその EC2 を扱う方法です。セキュリティルールはどう設計し、接続はどう行い、同じインスタンスを何度も立ち上げるには何を固めるべきかを扱います。

運用 EC2 の仕事は、おおむね3つに要約されます。第一に、Security Group で誰が入ってこられるかを制御します。第二に、接続は昔は SSH と key pair、最近は SSM Session Manager で行います。第三に、AMI で骨格を固めて同じインスタンスを素早く再生成します。この3つを一本につなぐと、EC2 運用の日常が単純になります。

ここで整理する SG パターンは 第13章 ALB / NLB と ACM のロードバランサーのセキュリティルールへ、AMI と ASG は 第15章 ECS と Fargate のコンテナ運用へつながります。

Security Group の構造 #

Security Group (SG) はインスタンス(正確には ENI)に付く stateful なファイアウォールです。インスタンス1台に SG を複数付けられ、SG 1つを複数のインスタンスで共有できます。

Inbound vs Outbound #

SG のルールは2方向です。

InboundOutbound
何を制御入ってくるトラフィック出ていくトラフィック
デフォルトすべて塞ぐすべて許可
よく触るよくほぼない

デフォルト値を覚えます。Inbound はデフォルト遮断、Outbound はデフォルト許可です。そのため SG 作業の大部分は Inbound ルールの追加です。

ルールの構造 #

Web サーバー SG の inbound ルールの例
Protocol  Port      Source              Description
TCP       80        0.0.0.0/0           HTTP from anywhere
TCP       443       0.0.0.0/0           HTTPS from anywhere
TCP       22        198.51.100.10/32    SSH from my home IP

各ルールはプロトコル、ポート、ソースの組み合わせです。ソース(Source)項目には2つが入り得ます。

  • CIDR ブロック0.0.0.0/0 (すべての IP)、10.0.0.0/16 (VPC 内部)、198.51.100.10/32 (単一 IP)
  • 別の SG の IDsg-0abc... で、これが本当に強力です。

SG を SG で指す #

運用で核心となるパターンは、SG が別の SG を指すことです。

ALB → アプリ EC2 パターン
ALB SG  (sg-alb)
  Inbound:  TCP 443 from 0.0.0.0/0

App SG  (sg-app)
  Inbound:  TCP 8080 from sg-alb     ← IP ではなく SG 自体

ALB の IP が変わっても(実際に ALB は複数の IP を動的に割り当てます)、SG 指しは自動で追従します。ルールの保守がはるかに単純になります。

よく使う SG パターン #

3-tier Web アプリの SG 設計
ALB SG (sg-alb)
  in:  443 ← 0.0.0.0/0
  out: all

App SG (sg-app)
  in:  8080 ← sg-alb              ← ALB だけが入ってこられる
       22   ← sg-bastion          ← Bastion から SSH (昔の方式)
  out: all

DB SG  (sg-db)
  in:  5432 ← sg-app              ← アプリサーバーだけが DB アクセス
  out: all (または閉じる)

Bastion SG (sg-bastion)
  in:  22 ← 198.51.100.10/32      ← 自分の IP のみ
  out: all

ルールが SG から SG へ流れるのが核心です。IP ではありません。

Outbound を塞ぐ場合 #

デフォルトは outbound すべて許可ですが、攻撃に露出したときにデータ漏洩を防ぐため、outbound も狭めるパターンがあります。通常は運用環境の DB や内部リソースから適用します。

狭めた outbound の例
App SG outbound:
  TCP 5432 → sg-db                 ← DB のみ
  TCP 443  → 0.0.0.0/0             ← 外部 API 呼び出し用
  TCP 53   → 0.0.0.0/0             ← DNS
  UDP 53   → 0.0.0.0/0             ← DNS

NACL — もう1つの層 #

VPC の2つ目のファイアウォールは NACL (Network Access Control List) です。サブネット単位で動きます。

Security GroupNACL
適用単位インスタンス (ENI)サブネット
Statefulそうであるそうでない (応答も明示許可が必要)
ルール種別Allow onlyAllow + Deny
評価順すべてのルール番号順 (低い番号が先)
日常で毎日触るほぼ触らない

NACL はあまり使いません。デフォルト NACL がすべてのトラフィックを許可し、SG が十分に細かいためです。NACL を触る場合は次のとおりです。

  • 特定の IP 帯域を遮断するとき(Deny が必要なとき — SG には Deny がありません)。
  • 攻撃を受けているとき、一時的に遮断するとき。
  • コンプライアンス要求でサブネット単位の明示遮断が必要なとき。

NACL の stateless の落とし穴 #

NACL は stateless なので、応答トラフィックも明示的に許可しなければなりません。

NACL ルールの例 — TCP 80 outbound が応答を受けるには
Inbound  Allow  TCP  1024-65535  0.0.0.0/0   ← ephemeral port 応答
Outbound Allow  TCP  80          0.0.0.0/0

1024-65535 が ephemeral port 領域です。これを抜かすと応答が戻ってきません。SG では stateful なので自動ですが、NACL は明示が必要です。

Key pair の限界 #

昔から EC2 の SSH 接続は key pair で行いました。

key pair 作成 + SSH 接続
# キーペアを作る
aws ec2 create-key-pair --key-name my-key --query 'KeyMaterial' --output text > my-key.pem
chmod 400 my-key.pem

# インスタンスを立ち上げるときに key を指定
aws ec2 run-instances --key-name my-key ...

# 接続
ssh -i my-key.pem ec2-user@<public-ip>

EC2 が立ち上がるときにインスタンスの ~/.ssh/authorized_keys に自動でキーが追加され、SSH が可能になります。

key pair モデルの限界 #

key pair モデルは運用規模が大きくなると崩れます。

  • キー紛失 — 失うと再び作れません。インスタンスを再生成するか、EBS をマウントした後に手動で追加しなければなりません。
  • キー共有のリスク — チームメンバーに渡す必要があるのに、一度漏れると回収できません。
  • 監査の難しさ — 誰がいつ入ったかを別途ロギングする必要があります。
  • インターネットに 22 ポートを露出 — 攻撃面になります。
  • MFA 不可 — キーさえあれば通過します。

EC2 Instance Connect #

コンソールが作る一時的な SSH キーを一度だけ使う方式です。SG に 22 ポート許可は依然として必要です。コンソールの「Connect」ボタンがこれを使います。

SSM Session Manager — キーなしの接続 #

SSM (AWS Systems Manager) の Session Manager は EC2 接続の新しい標準です。22 ポートも開けず、キーもなしで EC2 の中のシェルへ入ります。

Session Manager の流れ
[自分のコンピュータ] ──HTTPS──▶ [SSM Endpoint] ◀──HTTPS──[EC2 内の SSM Agent]
                   IAM 権限を確認

EC2 の中で動く SSM Agent が AWS API へ outbound 接続を作り、そのチャネルを通じてコンソールのシェル入力が流れます。方向が逆(EC2 が outbound)なので、SG inbound の 22 ポートが不要です。

Session Manager のセットアップ #

  1. SSM Agent がインストールされた AMI — Amazon Linux 2023 / Ubuntu の最新はデフォルトで含まれます。
  2. EC2 の IAM RoleAmazonSSMManagedInstanceCore ポリシーを付けます。
  3. outbound インターネット または VPC Endpoint — Private サブネットの EC2 でも SSM が可能です。
コンソールの代わりに CLI で接続
aws ssm start-session --target i-0abc1234def567890

# ポートフォワーディングも可能
aws ssm start-session --target i-0abc... \
  --document-name AWS-StartPortForwardingSession \
  --parameters '{"portNumber":["80"],"localPortNumber":["8080"]}'

key pair vs Session Manager #

key pair (SSH)Session Manager
22 ポート開ける必要あり開けなくてよい
キー管理自分なし
認証SSH キーIAM (MFA 可能)
監査ログ別途CloudTrail / S3 で自動
Private サブネットBastion が必要VPC Endpoint で直接
ポートフォワーディングssh -Lstart-session で可能

運用では Session Manager がほぼ常に正解です。詳しい IAM 設定は 第2章 IAM、セキュリティは 第6章 セキュリティ基本 を参照します。

CloudShell と混同しません。 第5章 CloudShell と IAM Identity Center の CloudShell は AWS コンソールの中のブラウザ端末で、自分の IAM 資格で aws cli を使う方式です。Session Manager は EC2 インスタンスの中のシェルです。

EC2 のメタデータサービス (IMDS) #

EC2 の中で自分のインスタンスの情報(インスタンス ID、リージョン、IAM ロールの資格情報など)を受け取るサービスが IMDS (Instance Metadata Service) です。

IMDSv2 — トークンを受けてメタデータを照会
TOKEN=$(curl -X PUT http://169.254.169.254/latest/api/token \
  -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")

curl -H "X-aws-ec2-metadata-token: $TOKEN" \
  http://169.254.169.254/latest/meta-data/instance-id

169.254.169.254 という link-local アドレスが、EC2 の中でのみ応答するメタデータエンドポイントです。IAM Role の一時的な資格情報もここで受け取ります。aws cli が EC2 の中で自動でその資格を使う理由です。

IMDSv1 vs IMDSv2 #

昔はトークンなしで GET で受け取りました(IMDSv1)。SSRF 攻撃でトークンを抜き取る事故が多く、IMDSv2 に変わりました。PUT でトークンを受け取り、それで GET します。新しいインスタンスは IMDSv2 のみを有効にすることが推奨されます。

IMDSv2 を強制
aws ec2 modify-instance-metadata-options \
  --instance-id i-0abc... \
  --http-tokens required \
  --http-endpoint enabled

AMI 作り — 骨格を固める #

同じセットアップのインスタンスを何度も素早く立ち上げるには2つの道があります。1つは AMI を作ることで、もう1つは空の AMI で立ち上げてセットアップスクリプトを自動実行する User data 方式です。

AMI 作り #

コンソールでインスタンスを右クリックして「イメージの作成」をするか、CLI で作ります。

AMI を作る
aws ec2 create-image \
  --instance-id i-0abc... \
  --name "my-app-2026-05-24" \
  --description "Node 20 + nginx + my-app v1.2.3" \
  --no-reboot     # オプション — 再起動なしで (ディスクの一貫性は少し下がる可能性)

作られた AMI はインスタンスの EBS スナップショットとメタデータです。その AMI で新しいインスタンスを立ち上げると、同じディスク状態で始まります。AMI はリージョン単位なので、別のリージョンでは copy-image を使います。

User data — 起動スクリプト #

AMI の代わりに空の OS イメージで立ち上げ、起動スクリプトでセットアップするパターンです。AMI より柔軟で、変更の追跡も容易です。

User data の例 — Amazon Linux 2023
#!/bin/bash
yum update -y
yum install -y nginx
systemctl enable --now nginx

# アプリコードを受け取る
aws s3 cp s3://my-bucket/app.tar.gz /tmp/
tar -xzf /tmp/app.tar.gz -C /opt/myapp

User data はインスタンスの初回起動時に一度実行されます。ログは /var/log/cloud-init-output.log に残ります。

Golden AMI vs User data #

Golden AMIUser data
起動速度速い遅い (スクリプト実行時間)
変更管理AMI を新しくビルドスクリプト修正
再現性非常に高い外部依存(yum repo、S3)が変わり得る
適合ASG の速いスケール / 安定開発 / 速い変更

運用では両方を一緒に使います。Golden AMI で OS と依存を埋め込み、User data でアプリのバージョンだけを差し込む形です。

Auto Scaling Group — 自動復旧 #

インスタンスが死んだら新しく立ち上げて ALB に自動で接続してくれる機能が ASG (Auto Scaling Group) です。

ASG の構造
Launch Template (インスタンステンプレート: AMI, タイプ, SG, key, user data)
   ┌─────────┐
   │   ASG   │  desired=2  min=2  max=10
   └─────────┘
        ├─── EC2 (AZ a)  ← health check 失敗 → terminate + 新しく立ち上げ
        ├─── EC2 (AZ b)
        └─── EC2 (AZ b)

基本構成は次のとおりです。

  • Launch Template — どんな EC2 を立ち上げるかを定義します(AMI、タイプ、SG、IAM、user data)。
  • Desired / Min / Max — 常に維持する個数、最小、最大です。
  • Health Check — EC2 自体(EC2)または ALB target group(ELB)基準です。

コンテナワークロードなら 第15章 ECS と Fargate がより滑らかな代替です。ECS がコンテナ単位の ASG を吸収します。

よく出会う落とし穴 #

  • 「なぜ ALB が EC2 に届かないのか?」 — 上から下へ確認します。(1) ALB SG outbound が EC2 SG inbound と一致するか、(2) EC2 SG inbound に ALB SG が source として入っているか、(3) EC2 の OS 内のファイアウォール(firewalldufw)もそのポートを許可するか、(4) ALB target group の health check 経路が 200 を応答するか、(5) EC2 がそのポートで listen 中か(ss -tlnp)。大部分は (1) または (2) で、SG 項目を IP で書いているなら SG 自体に変えるのが運用の正解です。
  • キーがないのに EC2 の中に入る必要がある — Session Manager が ON になっていれば aws ssm start-session を使います。OFF なら、インスタンスを停止して EBS を切り離し、別の EC2 にマウントした後に ~/.ssh/authorized_keys を修正して再接続するか、EBS スナップショットを取って新しいキーで新しいインスタンスを立ち上げます。
  • Outbound all のままにしてデータ漏洩 — EC2 が侵害されたとき outbound がすべて開いていると、攻撃者が任意の IP へデータを送ります。DB サーバーや内部システムは outbound も狭めるのが最も安全です。
  • NACL の任意遮断で応答が来ない — NACL の stateless の性質を忘れて outbound だけ許可すると、inbound 応答が塞がれます。ほぼ常に NACL はデフォルト値のままにして SG だけを触るのが安全です。
  • IMDSv1 のまま運用 — 古い AMI や旧セットアップが IMDSv1 のままで運用中なら、SSRF 攻撃面になります。すべてのインスタンスに --http-tokens required を適用します。
  • AMI が大きすぎて起動が遅くなる — 長く運用したインスタンスをそのまま AMI にして 5GB 以上になると、起動時間が増えます。AMI を作る前にログ / キャッシュ / 一時ファイルを片付け(yum clean all など)、cloud-init clean で次の起動に再び init が回るようにし、swap と journal を空にします。

練習問題 #

  1. §「3-tier Web アプリの SG 設計」の4つの SG (alb · app · db · bastion) を見ずに再び描き、各 inbound ルールの source が IP か SG かを表示してください。そのうえで、ALB の IP が変わってもルールを直さなくてよい理由を §「SG を SG で指す」を根拠に一文で説明してみてください。
  2. key pair と Session Manager の比較表を見て、§「key pair モデルの限界」の5項目のうち Session Manager がそれぞれどう解決するかをつなげてみてください。第6章 セキュリティ基本 の最小権限とどう噛み合うかも一行書いておいてください。
  3. 同じセットアップのインスタンスを自動復旧したい状況で Golden AMI と User data を一緒に使うなら、それぞれに何を入れるか(OS · 依存 · アプリバージョン)を分けて書いてみてください。この分担は 第15章 ECS と Fargate でコンテナイメージとタスク定義として再び現れます。

一行まとめ: SG はインスタンス単位の stateful ファイアウォールで、SG が SG を指すパターンが IP より強力、NACL はサブネット単位の stateless でほぼ触らない。接続は 22 ポートもキーも使わない SSM Session Manager が新しい標準で、IMDSv2 の強制で SSRF を防ぐ。骨格は Golden AMI と User data を一緒に使って固め、ASG が自動復旧を担う。

次の章 #

EC2 の基本は整理できました。次の 第10章 S3 では、EC2 と一緒によく扱うオブジェクトストレージへ移ります。バケットの形、ポリシーと Public Access Block、静的サイトホスティング、presigned URL のような日常のパターンを整理します。

X