AWS VPC設計入門|サブネット・セキュリティグループ・NATの基礎ハンズオン

宮崎智広 この記事の監修:宮崎智広(Linux実務・教育歴20年以上・受講者3,100名超)
HOMELinux技術 リナックスマスター.JP(Linuxマスター.JP)AWS(Amazon Linux) > AWS VPC設計入門|サブネット・セキュリティグループ・NATの基礎ハンズオン
「AWS VPCって設計が複雑そうで、どこから手をつければいいか分からない」
そう感じているエンジニアは多いはずです。VPC(Virtual Private Cloud)はAWSの基盤となるネットワーク機能ですが、
CIDRブロック・サブネット・セキュリティグループ・NATゲートウェイと用語が多く、最初は混乱しがちです。

この記事では、aws vpc 設計の基礎からマルチAZ構成まで、実際の設計パターンをハンズオン形式で解説します。
Webサービスの本番環境で主流になっている「3層アーキテクチャ(ALB・EC2・RDS)」での実装パターンを中心に、VPCフローログを使った通信の可視化とセキュリティ監査の方法も含めて、「まず動くVPCを作りたい」「セキュリティ設定が正しいか確認したい」という初級~中級者に向けて体系的にカバーします。
設計の詳細な冗長化・マルチAZ応用については、AWS冗長設計入門もあわせて参照してください。

この記事のポイント

・VPCはAWS上の仮想ネットワーク。CIDRブロックで範囲を決める
・サブネットはパブリック(ALB)・アプリプライベート(EC2)・DBプライベート(RDS)の3層に分ける
・セキュリティグループはALB-SG→EC2-SG→RDS-SGの順にSG参照で連携させる
・VPCフローログでACCEPT/REJECTを記録し、SG/NACLの設定検証と通信監査に活用する


「このままじゃマズい」と感じていませんか?
参考書を開く気力もない、同年代に取り残される不安——
でも安心してください。プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

AWS VPCとは何か(基本概念)

AWS VPC(Virtual Private Cloud)は、AWSクラウド上に作成する論理的な仮想ネットワーク空間です。
物理的なデータセンターでいう「社内LAN」を、クラウド上に丸ごと再現するイメージを持つと分かりやすいです。

VPCを使うと、以下のことが実現できます。
・IPアドレスの範囲を自分で決められる(CIDRブロック)
・インターネットに公開するゾーンと非公開ゾーンを分けられる(パブリック・プライベートサブネット)
・どのIPからどのポートへのアクセスを許可するか細かく制御できる(セキュリティグループ・ネットワークACL)
・複数のアベイラビリティゾーン(AZ)にまたがる冗長構成を組める(マルチAZ)
・VPCフローログでネットワーク通信のACCEPT/REJECTを記録して可視化できる

AWSの多くのサービス(EC2・RDS・ELBなど)はVPC内に配置するため、
aws vpc 設計の基礎を押さえることが、AWSの実践スキル全体に直結します。

VPCの主な構成要素

VPCを構成する要素を整理しておきましょう。

要素 役割
VPC 仮想ネットワーク空間全体。リージョン単位で作成する
サブネット VPC内を細かく区切ったIPアドレス範囲。AZ単位で作成する
インターネットゲートウェイ(IGW) VPCとインターネットを接続する出入り口
ルートテーブル サブネットごとの通信経路を定義する
セキュリティグループ インスタンス単位のファイアウォール(ステートフル)
ネットワークACL(NACL) サブネット単位のファイアウォール(ステートレス)
NATゲートウェイ プライベートサブネットからのアウトバウンド通信を中継する
VPCフローログ ENIを通過するIPトラフィックのACCEPT/REJECTを記録する
VPC DNSホスト名 EC2インスタンスにDNSホスト名を付与する(RDS接続に必須)
AWSをゼロから学びたい方は、Amazon Linux入門講座もあわせてご覧ください。

VPC設計の基本:CIDRブロックの考え方

VPCを作成する際に最初に決めるのが「CIDRブロック」です。
CIDR(Classless Inter-Domain Routing)は、IPアドレスの範囲を「アドレス/プレフィックス長」で表す記法です。

例えば 10.0.0.0/16 と指定すると、10.0.0.010.0.255.255 の65,536個のIPアドレスを使えます。
AWSでは /16(最大65,536アドレス)から /28(最小16アドレス)まで指定できます。

1. CIDRブロックの設計指針

実務でよく使われるVPC CIDRの目安を示します。

CIDRブロック 使用可能IPアドレス数 適したケース
10.0.0.0/16 65,536個 本番環境・大規模構成。サブネット分割の余裕が大きい
10.0.0.0/24 256個 学習・検証環境。小規模で素早く試したい場合
172.16.0.0/16 65,536個 オンプレ環境とIPが重複しないよう別レンジを使いたい場合

後からCIDRブロックを変更するのは非常に困難なため、最初に「少し大きめ」に設定しておくことが鉄則です。
本番環境では 10.0.0.0/16 を基本とし、サブネットに /24(256アドレス)を割り当てる設計がよく使われます。
また、ALBやRDSはENI(Elastic Network Interface)を複数消費するため、/28のような小さいサブネットは避けてください。

2. プライベートIPアドレス範囲(RFC 1918)を使う理由

VPC内では必ずプライベートIPアドレス範囲を使います。
インターネットには直接ルーティングされない専用の範囲であり、安全にVPC内部で自由にIPを割り振れます。

プライベートIPレンジ アドレス範囲
10.0.0.0/8 10.0.0.0 ~ 10.255.255.255
172.16.0.0/12 172.16.0.0 ~ 172.31.255.255
192.168.0.0/16 192.168.0.0 ~ 192.168.255.255

3. AWS CLIでVPCを作成する

AWSコンソールだけでなく、CLIからも作成できます。手順を覚えておくとインフラのコード化(IaC)に役立ちます。

# VPCを作成する(東京リージョン・ap-northeast-1を前提) $ aws ec2 create-vpc --cidr-block 10.0.0.0/16 --tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=web-prod-vpc}]' # 実行結果(抜粋) { "Vpc": { "VpcId": "vpc-0a1b2c3d4e5f60001", "CidrBlock": "10.0.0.0/16", "State": "available" } } # VPCのDNSホスト名を有効にする(RDSのエンドポイント名解決に必要) $ aws ec2 modify-vpc-attribute --vpc-id vpc-0a1b2c3d4e5f60001 --enable-dns-hostnames

パブリックサブネットとプライベートサブネットの設計

VPCの設計で最も重要な概念が「パブリックサブネット」と「プライベートサブネット」の使い分けです。
この2種類を適切に分けることで、インターネットに公開すべきリソースと非公開にすべきリソースを安全に管理できます。

1. パブリックサブネットの役割

パブリックサブネットは、インターネットゲートウェイ(IGW)へのルートを持ち、
インターネットと直接通信できるサブネットです。

典型的な用途は以下の通りです。
・Webサーバー(EC2インスタンス)
・ロードバランサー(ALB/NLB)
・踏み台サーバー(Bastion Host)
・NATゲートウェイ本体(後述)

# ルートテーブルに以下のルートを追加する # 送信先: 0.0.0.0/0 → ターゲット: インターネットゲートウェイ(igw-xxxxxxxxx) # これによりパブリックサブネット内のインスタンスはインターネットと通信可能になる # また、EC2インスタンスには「パブリックIPの自動割り当て」を有効にする # または Elastic IP(固定パブリックIP)を割り当てる

2. プライベートサブネットの役割

プライベートサブネットは、IGWへの直接ルートを持たず、インターネットから直接アクセスできないサブネットです。

典型的な用途は以下の通りです。
・データベースサーバー(RDS)
・アプリケーションサーバー(APIサーバーなど)
・バッチ処理サーバー
・機密データを扱うサービス

プライベートサブネットに置いたリソースは直接インターネットからアクセスできないため、セキュリティが大幅に向上します。
ただしソフトウェアのアップデートなどでインターネットへのアウトバウンド通信が必要な場合は、NATゲートウェイを使います(後述)。

3. CIDRブロックの分割例

VPCを 10.0.0.0/16 で作った場合のサブネット割り当て例です。

サブネット名 CIDRブロック AZ 種別
public-subnet-1a 10.0.1.0/24 ap-northeast-1a パブリック
public-subnet-1c 10.0.2.0/24 ap-northeast-1c パブリック
private-subnet-1a 10.0.11.0/24 ap-northeast-1a プライベート
private-subnet-1c 10.0.12.0/24 ap-northeast-1c プライベート

パブリックは 10.0.1.x10.0.9.x、プライベートは 10.0.10.x 以降のような「番号で種別を見分けやすい」設計にしておくと、
後から見返したときに混乱しにくくなります。

4. 3層アーキテクチャでのサブネット分割(Web・AP・DB層)

本番品質のWebシステムでは、プライベートサブネットをさらに「アプリ層(EC2)」と「DB層(RDS)」に分割するのがベストプラクティスです。
アプリ層とDB層を同じサブネットに混在させると、セキュリティグループの制御が複雑になり、
EC2が侵害された場合にRDSへも直接アクセスされるリスクが生じます。

役割 AWSサービス 配置サブネット例
Web層 HTTPS受付・SSL終端・負荷分散 ALB(Application Load Balancer) パブリック(10.0.1.0/24, 10.0.2.0/24)
アプリ層 ビジネスロジック実行・DBアクセス EC2(Auto Scalingグループ) アプリプライベート(10.0.11.0/24, 10.0.12.0/24)
DB層 データ永続化・トランザクション RDS(マルチAZ有効) DBプライベート(10.0.21.0/24, 10.0.22.0/24)

AWS CLIでアプリ層とDB層のプライベートサブネットを作成する例です。

# アプリ層用プライベートサブネット(ap-northeast-1a) $ aws ec2 create-subnet --vpc-id vpc-0a1b2c3d4e5f60001 --cidr-block 10.0.11.0/24 --availability-zone ap-northeast-1a --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=app-prv-1a}]' # アプリ層用プライベートサブネット(ap-northeast-1c) $ aws ec2 create-subnet --vpc-id vpc-0a1b2c3d4e5f60001 --cidr-block 10.0.12.0/24 --availability-zone ap-northeast-1c --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=app-prv-1c}]' # DB層用プライベートサブネット(ap-northeast-1a) $ aws ec2 create-subnet --vpc-id vpc-0a1b2c3d4e5f60001 --cidr-block 10.0.21.0/24 --availability-zone ap-northeast-1a --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=db-prv-1a}]' # DB層用プライベートサブネット(ap-northeast-1c) $ aws ec2 create-subnet --vpc-id vpc-0a1b2c3d4e5f60001 --cidr-block 10.0.22.0/24 --availability-zone ap-northeast-1c --tag-specifications 'ResourceType=subnet,Tags=[{Key=Name,Value=db-prv-1c}]'

セキュリティグループとネットワークACLの使い分け

VPCのセキュリティ制御は「セキュリティグループ」と「ネットワークACL(NACL)」の2層構造です。
この2つは似て非なるものであり、役割を理解して使い分けることが大切です。
なお、設定後はVPCフローログのactionフィールドで実際のACCEPT/REJECTを確認する習慣をつけると、意図通りに動作しているかすぐ検証できます(後述)。

1. セキュリティグループ(Security Group)

セキュリティグループは、EC2インスタンスなどのリソースに直接アタッチするファイアウォールです。

主な特徴は以下の通りです。
ステートフル:インバウンドで許可した通信のレスポンスは自動的に許可される
許可ルールのみ:「拒否」のルールは設定できない(ルールにないものは自動的に拒否)
インスタンス単位:1つのインスタンスに複数のセキュリティグループを適用可能

# インバウンドルール(受信許可)設定例(Webサーバー用セキュリティグループ) # ルール1: HTTP (TCP 80) 送信元: 0.0.0.0/0 インターネットからのアクセスを許可 # ルール2: HTTPS (TCP 443) 送信元: 0.0.0.0/0 HTTPS接続を許可 # ルール3: SSH (TCP 22) 送信元: 203.0.113.50/32 自分のPCのIPアドレスのみ許可 # アウトバウンドルール(送信許可) # デフォルト: すべてのアウトバウンドを許可(0.0.0.0/0) # ステートフルのため、インバウンドで受けた通信のレスポンスは別途許可不要

2. ネットワークACL(NACL)

ネットワークACLはサブネット全体に適用するファイアウォールです。セキュリティグループとの違いをまとめます。

比較項目 セキュリティグループ ネットワークACL
適用対象 インスタンス単位 サブネット単位
ステート ステートフル ステートレス
拒否ルール 設定不可 設定可能
ルールの評価 全ルールをまとめて評価 番号順に評価(マッチしたら終了)
デフォルト動作 全拒否(許可ルールのみ追加) 全許可(デフォルトNACL)

実務上はセキュリティグループを主軸に設計し、NACLは追加の防御層として使うケースが多いです。
特定のIPアドレスを明示的にブロックしたい場合(DDoS対策など)は、NACLの「拒否ルール」が有効です。

NACLはステートレスのため、インバウンドを許可した通信のレスポンスについても
アウトバウンドのルールで明示的に許可する必要があります。
エフェメラルポート(一般的に1024~65535)を戻り通信用に開ける必要がある点に注意してください。

3. セキュリティグループの設計ベストプラクティス

セキュリティグループを設計する際の実践的なポイントをまとめます。
最小権限の原則:必要なポートのみ、必要なソースIPのみを許可する
SSH(22番ポート)は必ず特定のIPに制限0.0.0.0/0(全世界)に開放しない
セキュリティグループ間参照を使う:WebサーバーからDBサーバーへのアクセスは、IPではなくWebサーバー用SGを送信元に指定する
名前・説明文を丁寧に書く:後から見てどのリソース用か分かるように管理する

# DBサーバーのセキュリティグループのインバウンドルール # ポート: MySQL/Aurora (TCP 3306) # 送信元: sg-0123456789abcdef0 (Webサーバー用セキュリティグループのID) # →「WebサーバーのSGがアタッチされたインスタンスからのみ3306番を許可」という意味 # IPアドレスではなくSGを参照することで、インスタンスのIP変動に依存しない柔軟な設計になる

4. 3層アーキテクチャでのセキュリティグループ連携設計

3層アーキテクチャ(ALB・EC2・RDS)のセキュリティグループ設計の核心は、送信元をIPアドレスではなくセキュリティグループIDで指定する点です。
これにより、EC2インスタンスのIPが動的に変わってもルールが自動で追従します。

# ステップ1: ALB-SG(インターネットからHTTPS/HTTPを受け付ける) $ aws ec2 create-security-group --group-name alb-sg --description "SG for ALB web layer" --vpc-id vpc-0a1b2c3d4e5f60001 # 実行結果: {"GroupId": "sg-0alb1234567890ef0"} # インターネットからHTTPS(443)とHTTP(80)を許可する $ aws ec2 authorize-security-group-ingress --group-id sg-0alb1234567890ef0 --protocol tcp --port 443 --cidr 0.0.0.0/0 $ aws ec2 authorize-security-group-ingress --group-id sg-0alb1234567890ef0 --protocol tcp --port 80 --cidr 0.0.0.0/0 # ステップ2: EC2-SG(ALB-SGからのみアプリポートを受け付ける) $ aws ec2 create-security-group --group-name ec2-app-sg --description "SG for EC2 app layer" --vpc-id vpc-0a1b2c3d4e5f60001 # 実行結果: {"GroupId": "sg-0ec21234567890fg0"} # --source-group でSGIDを指定(IPアドレスではなくSG参照) $ aws ec2 authorize-security-group-ingress --group-id sg-0ec21234567890fg0 --protocol tcp --port 8080 --source-group sg-0alb1234567890ef0 # → EC2へのアクセスはALBを経由したものだけに限定される # ステップ3: RDS-SG(EC2-SGからのみDBポートを受け付ける) $ aws ec2 create-security-group --group-name rds-db-sg --description "SG for RDS DB layer" --vpc-id vpc-0a1b2c3d4e5f60001 # 実行結果: {"GroupId": "sg-0rds1234567890gh0"} $ aws ec2 authorize-security-group-ingress --group-id sg-0rds1234567890gh0 --protocol tcp --port 3306 --source-group sg-0ec21234567890fg0 # ルール確認(IpRangesが空でUserIdGroupPairsのみが正しい状態) $ aws ec2 describe-security-groups --group-ids sg-0rds1234567890gh0 --query 'SecurityGroups[0].IpPermissions'

このSG連携設計(ALB-SG→EC2-SG→RDS-SGのチェーン)により、
・EC2へはALBを経由した通信だけが届く(直接攻撃をALB手前でブロック)
・RDSへはEC2からの通信だけが届く(EC2が侵害されてもRDSへは直接到達できない)
という2段階の防御が自動的に機能します。

NATゲートウェイの役割と設計パターン

プライベートサブネット内のEC2インスタンスは、直接インターネットと通信できません。
しかしソフトウェアの更新(yum/apt)や外部APIへのアクセスなど、インターネットへのアウトバウンド通信が必要な場面は多くあります。
このときに使うのが「NATゲートウェイ(NAT Gateway)」です。

1. NATゲートウェイの仕組み

NAT(Network Address Translation)ゲートウェイは、プライベートIPアドレスをパブリックIPアドレスに変換して
インターネットとの通信を中継するサービスです。

# 通信フロー(プライベートサブネットからインターネットへ) # プライベートサブネット内のEC2(10.0.11.10) # → NATゲートウェイ(パブリックサブネット・10.0.1.x) # → インターネットゲートウェイ(IGW) # → インターネット(yumリポジトリ等) # 設定のポイント: # 1. NATゲートウェイ自体はパブリックサブネットに置く(必須) # 2. NATゲートウェイにはElastic IP(固定パブリックIP)を割り当てる # 3. プライベートサブネットのルートテーブルに追加するルート: # 送信先: 0.0.0.0/0 → ターゲット: nat-xxxxxxxxxxxxxxxxx

2. NATゲートウェイの設定手順(概要)

AWSコンソールでの設定手順を示します。

# Step 1: Elastic IPを取得する # VPCコンソール → Elastic IP → 新しいElastic IPの割り当て # Step 2: NATゲートウェイを作成する # VPCコンソール → NATゲートウェイ → NATゲートウェイを作成 # - サブネット: パブリックサブネットを選択(プライベートサブネットではない) # - 接続タイプ: パブリック # - Elastic IP割り当てID: Step1で取得したEIPを選択 # Step 3: プライベートサブネットのルートテーブルを更新する # VPCコンソール → ルートテーブル → プライベートサブネット用ルートテーブルを選択 # → ルートを編集 → ルートの追加 # 送信先: 0.0.0.0/0 / ターゲット: NATゲートウェイ(nat-xxxxxxxxxxxxxxxxx)

3. NATゲートウェイのコスト注意点

NATゲートウェイは有料サービスであり、時間課金(約0.062 USD/時間)+データ転送量課金がかかります。
開発環境や検証環境では、コスト削減のためにNATゲートウェイの代わりに
「NATインスタンス(EC2インスタンスでNATを行う)」を使う選択肢もあります。
ただし可用性・運用負荷を考えると、本番環境ではNATゲートウェイを使うことを強くすすめます。

また、検証が終わったらNATゲートウェイを削除し、Elastic IPも解放しておくことも忘れずに。
削除し忘れると課金が続くため注意してください。

マルチAZ構成で可用性を高める

AWSではデータセンターのような物理施設を「アベイラビリティゾーン(AZ)」と呼びます。
東京リージョン(ap-northeast-1)には複数のAZがあり、それぞれが物理的に独立した電源・ネットワークを持ちます。

マルチAZ構成とは、複数のAZにサブネット・リソースを分散配置することで、
1つのAZに障害が発生しても残りのAZでシステムを継続稼動させる設計
です。

1. マルチAZ構成の基本パターン

典型的なWebアプリケーションのマルチAZ構成を示します。

レイヤー AZ-1a(東京) AZ-1c(東京)
パブリックサブネット 10.0.1.0/24(ALB・踏み台) 10.0.2.0/24(ALB・NATゲートウェイ)
プライベートサブネット(Web) 10.0.11.0/24(EC2 Webサーバー) 10.0.12.0/24(EC2 Webサーバー)
プライベートサブネット(DB) 10.0.21.0/24(RDSプライマリ) 10.0.22.0/24(RDSスタンバイ)

この構成のポイントは次の通りです。
・ALB(Application Load Balancer)を2つのAZのパブリックサブネットに配置し、Webサーバーへのトラフィックを分散
・Webサーバーは2つのAZのプライベートサブネットに配置(ALB配下でオートスケーリング対応)
・RDSはマルチAZで配置し、プライマリに障害発生時にスタンバイへ自動フェールオーバー

なお、NATゲートウェイは各AZに1つずつ配置するのがベストプラクティスです。
1つのAZにNATゲートウェイが集中すると、そのAZに障害が起きたときに
他のAZからのインターネットアウトバウンド通信も止まってしまいます。

2. ALBのマルチAZ設定

ALBはインターネット向け(internet-facing)で作成し、2つのパブリックサブネットを指定します。
ALBは指定したサブネットのAZに自動でノードを配置し、マルチAZのリクエスト分散を行います。

# ALBを作成する(2つのパブリックサブネットを指定) $ aws elbv2 create-load-balancer --name web-prod-alb --subnets subnet-0pub1a234567890ab subnet-0pub1c234567890cd --security-groups sg-0alb1234567890ef0 --scheme internet-facing --type application # 実行結果(抜粋) { "LoadBalancers": [{ "DNSName": "web-prod-alb-1234567890.ap-northeast-1.elb.amazonaws.com", "AvailabilityZones": [ {"ZoneName": "ap-northeast-1a", "SubnetId": "subnet-0pub1a234567890ab"}, {"ZoneName": "ap-northeast-1c", "SubnetId": "subnet-0pub1c234567890cd"} ], "State": {"Code": "active"} }] } # ターゲットグループを作成する(EC2のアプリポート8080に転送) $ aws elbv2 create-target-group --name web-prod-tg --protocol HTTP --port 8080 --vpc-id vpc-0a1b2c3d4e5f60001 --health-check-path /health --health-check-interval-seconds 30

ヘルスチェックパス(/health)はEC2アプリが200を返すエンドポイントを指定します。
ここで200が返らないとALBはそのEC2をターゲットから除外するため、アプリ起動後に必ず疎通確認を行ってください。
ALBのDNS名(web-prod-alb-xxx.ap-northeast-1.elb.amazonaws.com)がアプリのエンドポイントになるので、
Route 53でカスタムドメインをAliasレコードでこのDNS名に向けるのが定石です。

3. Auto ScalingグループによるEC2のマルチAZ分散

EC2をマルチAZに分散するには、Auto ScalingグループにVPCゾーンID(プライベートサブネット)を複数指定します。

# Auto Scalingグループを作成する(2つのプライベートサブネットを指定) $ aws autoscaling create-auto-scaling-group --auto-scaling-group-name web-prod-asg --launch-template LaunchTemplateId=lt-0abcdef1234567890,Version='$Latest' --min-size 2 --max-size 6 --desired-capacity 2 --vpc-zone-identifier "subnet-0app1a234567890ef,subnet-0app1c234567890gh" --target-group-arns arn:aws:elasticloadbalancing:ap-northeast-1:123456789012:targetgroup/web-prod-tg/abc --health-check-type ELB --health-check-grace-period 60 # EC2インスタンスが2つのAZに分散されているか確認する $ aws ec2 describe-instances --filters "Name=tag:aws:autoscaling:groupName,Values=web-prod-asg" --query 'Reservations[*].Instances[*].{ID:InstanceId,AZ:Placement.AvailabilityZone,State:State.Name}' --output table # 実行結果 # +-------------------+------------------+------------------+ # | AZ | ID | State | # +-------------------+------------------+------------------+ # | ap-northeast-1a | i-0abc1234ef56 | running | # | ap-northeast-1c | i-0def5678gh90 | running | # +-------------------+------------------+------------------+

--min-size 2 にすることで、ASGは常に2台以上のEC2を維持します。
2台が異なるAZに分散されるため、1AZが落ちても残りのAZのEC2がリクエストを引き続き処理できます。

4. 踏み台サーバー(Bastion Host)の配置

プライベートサブネット内のサーバーにSSHで接続する際は、「踏み台サーバー」を経由する方法が一般的です。
踏み台サーバーはパブリックサブネットに配置し、自分のPCから踏み台にSSH、踏み台からプライベートサブネット内のサーバーにSSHという2段階の接続を行います。

# 踏み台サーバー経由でプライベートEC2に接続するSSH設定例(~/.ssh/config) Host bastion HostName 203.0.113.10 # 踏み台サーバーのElastic IP User ec2-user IdentityFile ~/.ssh/mykey.pem Host private-web HostName 10.0.11.20 # プライベートサブネット内のEC2プライベートIP User ec2-user IdentityFile ~/.ssh/mykey.pem ProxyJump bastion # 使い方(自分のPCから実行) # $ ssh private-web # → 踏み台(203.0.113.10)を経由してプライベートEC2(10.0.11.20)に接続される

踏み台サーバーのセキュリティグループは、
SSH(22番ポート)のインバウンドを「自分のPCのIPアドレス/32」のみに制限してください。
0.0.0.0/0に開放することは絶対に避けてください。
なお近年は踏み台サーバーを使わずに、AWS Systems Manager Session Managerを使ってSSHなしでEC2に接続する方法も増えています。SSHポートの開放が不要なため、セキュリティをさらに高めたい場合は検討してください。

VPCフローログで通信を可視化・監査する

VPCを設計して動かし始めたとき、「セキュリティグループは意図通りに動いているか」「プライベートサブネットから想定外の通信が出ていないか」を確認する手段が必要です。
そのために使うのが「VPCフローログ(VPC Flow Logs)」です。

VPCフローログは、VPC内のネットワークインターフェース(ENI: Elastic Network Interface)を通過するIPトラフィックのACCEPT/REJECTを記録するサービスです。
実際のパケットの中身(ペイロード)は記録されませんが、送信元IP・宛先IP・ポート・プロトコル・通信量・判定結果が記録されます。

フローログは「問題が起きてから有効にしても遅い」サービスです。
本番環境ではVPC設計の段階でフローログの出力先を決めておくことを強くすすめます。

1. フローログが記録する主要フィールド

フローログのレコードには以下のフィールドが含まれます。

フィールド 内容 主な活用場面
srcaddr 送信元IPアドレス 不審なアクセス元の特定
dstaddr 宛先IPアドレス 想定外の通信先の検出
srcport / dstport 送信元・宛先ポート 使用サービス・プロトコルの確認
protocol プロトコル番号(6=TCP、17=UDP、1=ICMP) 通信種別の把握
action ACCEPT または REJECT セキュリティグループ・NACLの動作確認
bytes 転送バイト数 異常な大量転送の検出
log-status OK / NODATA / SKIPDATA ログ欠損の確認

2. 出力先の設計(S3 vs CloudWatch Logs)

フローログの出力先は「S3」か「CloudWatch Logs」のどちらかを選択します。

観点 S3出力 CloudWatch Logs出力
コスト 低い(長期保存向き) 高い(リアルタイム向き)
分析ツール Athena・QuickSight CloudWatch Insights
リアルタイム性 約5分の遅延 約2分の遅延
アラート連携 Lambda経由で可能 Metric Filter + Alarmで直接可能
推奨ユースケース 監査・コスト分析・長期保存 リアルタイム異常検知

長期保存・コスト効率を重視するならS3(Parquet形式)
REJECTスパイクをリアルタイムで検知したいならCloudWatch Logs+Metric Filter+Alarmが定石です。
本番環境では両方を組み合わせるケースもあります。

3. フローログレコードの読み方

実際のフローログのレコードはスペース区切りで記録されます。

# フローログレコードの例(v2フォーマット: version account-id interface-id srcaddr dstaddr srcport dstport protocol packets bytes start end action log-status) # 203.0.113.45からポート443へのHTTPS通信(ACCEPT) 2 123456789012 eni-0a1b2c3d4e5f 203.0.113.45 10.0.1.50 52341 443 6 15 1823 1688000000 1688000059 ACCEPT OK # 198.51.100.22からSSH(22番ポート)への接続試行(REJECT) # → SGでSSHの送信元IPが制限されているため弾かれた 2 123456789012 eni-0a1b2c3d4e5f 198.51.100.22 10.0.1.50 54321 22 6 3 180 1688000000 1688000059 REJECT OK # EC2(10.0.1.50)からRDS(10.0.2.30)の3306番への通信(ACCEPT) # → プライベートサブネット間の正常な通信 2 123456789012 eni-0a1b2c3d4e5f 10.0.1.50 10.0.2.30 49152 3306 6 8 1024 1688000000 1688000059 ACCEPT OK

予期しないREJECT:外部から特定ポートへのREJECTが大量に記録されている場合、攻撃スキャンの可能性あり
予期しないACCEPT:本来ブロックすべき通信がACCEPTされていた場合、SGの設定漏れを疑う
NACLとSGの切り分け:NACLがREJECTした通信はSGに到達する前に遮断される。どちらが遮断したかをフローログのactionフィールドで確認できる

VPC設計でよくあるトラブルと対処法

aws vpc 設計を初めて行う際に、実際によく直面するトラブルと対処法をまとめます。
フローログが有効になっていれば、切り分け作業がより確実に進められます。

1. プライベートサブネットのEC2がインターネットに繋がらない

最も多いトラブルです。次の順番で確認してください。

# チェックリスト(上から順に確認する) # 1. NATゲートウェイが「利用可能」状態か確認 # AWSコンソール → VPC → NATゲートウェイ → ステータスを確認 # 2. プライベートサブネットのルートテーブルを確認 # 「0.0.0.0/0 → nat-xxxxxxxxxx」のルートが存在するか確認 # 3. NATゲートウェイがパブリックサブネットに配置されているか確認 # NATゲートウェイがプライベートサブネットに作られているとアウトバウンドが通らない # 4. EC2からpingで確認 # $ ping 8.8.8.8 # → 到達できれば外部通信OK。できなければルートテーブルかセキュリティグループを再確認 # 5. フローログが有効な場合:プライベートサブネットのENIのフローログを確認 # EC2からNATゲートウェイへの通信がACCEPTされているか確認する

2. セキュリティグループの設定後もSSHで繋がらない

セキュリティグループでSSHを許可したのに接続できない場合、よくある原因は以下です。
自分のPCのグローバルIPアドレスが変わっている:プロバイダの都合でIPが変動することがある。現在のIPを確認して再設定する
踏み台サーバー経由が必要なのに直接接続しようとしている:プライベートサブネットのEC2にはパブリックIPがない
キーペアが間違っている:EC2作成時に指定したキーペア(.pemファイル)と一致しているか確認する

# 自分のPCの現在のグローバルIPアドレスを確認する $ curl -s https://checkip.amazonaws.com 203.0.113.50 # このIPアドレスをセキュリティグループのインバウンドルールに設定する # 送信元: 203.0.113.50/32 ポート: SSH(22) # フローログが有効な場合:踏み台サーバーのENIのフローログで # 自分のIPからSSH(22番)への通信がREJECTされていないか確認する

3. RDSにアプリケーションから繋がらない

アプリケーションサーバーからRDSへの接続が失敗する場合は、次の点を確認してください。
RDSのセキュリティグループ:アプリケーションサーバー用SGを送信元として3306番(MySQL)や5432番(PostgreSQL)が許可されているか
サブネットグループ:RDSのサブネットグループにアプリケーションサーバーと同じVPCのサブネットが含まれているか

「セキュリティグループは設定した」という場合でも、送信元をIPではなくSGで指定しているかを確認してください。
間違えてアプリサーバーのIPを直接指定すると、IPが変動したときに接続が切れます。

フローログを使って切り分ける場合は次の手順が有効です。
・ステップ1:EC2のENIのフローログを確認。RDSの3306番への通信がREJECTされているか確認する
・ステップ2:RDSのENIのフローログを確認。EC2からのパケットがRDS側のENIに到達しているか確認する
・ステップ3:NACLがステートレスのため、アウトバウンドのエフェメラルポート(1024~65535)の許可漏れも疑う

# RDS-SGの設定確認(UserIdGroupPairsにEC2-SGのみが入っているか) $ aws ec2 describe-security-groups --group-ids sg-0rds1234567890gh0 --query 'SecurityGroups[0].IpPermissions[?FromPort==`3306`]' # RDSのエンドポイントDNS名を名前解決して疎通確認する $ nslookup web-prod-mysql.xxxxxxxxxxxx.ap-northeast-1.rds.amazonaws.com # プライベートIP(10.0.21.x)が返れば名前解決は正常 # 失敗する場合はVPCのenableDnsHostnames/enableDnsSupportを確認する $ aws ec2 describe-vpc-attribute --vpc-id vpc-0a1b2c3d4e5f60001 --attribute enableDnsHostnames

4. ALBのヘルスチェックが失敗してEC2がunhealthyになる

ALBのターゲットグループでEC2が「unhealthy」になる場合、次の順で確認してください。

# 確認1: EC2-SGでALB-SGからのアクセスが許可されているか $ aws ec2 describe-security-groups --group-ids sg-0ec21234567890fg0 --query 'SecurityGroups[0].IpPermissions' # ポート8080・送信元sg-0alb1234567890ef0 のルールが存在するか確認する # 確認2: EC2上でアプリが指定ポートでlistenしているか # (AWS Systems Manager Session Managerでポート確認) $ ss -tlnp | grep 8080 # 正常時の出力例: # LISTEN 0 128 0.0.0.0:8080 0.0.0.0:* users:(("java",pid=1234,fd=42)) # 何も返らない場合はアプリが起動していないか別ポートでlistenしている # 確認3: ヘルスチェックパスが200を返すか $ curl -o /dev/null -s -w "%{http_code}" http://localhost:8080/health # 200が返れば正常。200以外ならアプリのヘルスチェック実装を確認する

まとめ:VPC設計のベストプラクティス

この記事で解説したaws vpc 設計のポイントをまとめます。

設計項目 ベストプラクティス
CIDRブロック 余裕を持って 10.0.0.0/16 を選択。後から変更は困難。/24より小さいサブネットは避ける
サブネット分割 パブリック(ALB)・アプリプライベート(EC2)・DBプライベート(RDS)の3層に分ける
ALB パブリックサブネットのマルチAZに配置。ターゲットグループのヘルスチェックを設定する
セキュリティグループ ALB-SG→EC2-SG→RDS-SGのSG参照チェーンで3層防御。SSHは特定IPのみ
ネットワークACL セキュリティグループを主軸に。明示的なブロックが必要な場合のみ活用
NATゲートウェイ パブリックサブネットに配置。本番はAZ数分用意する。不要時は削除
マルチAZ 本番環境は必ず複数AZにサブネットを分散。RDS・NATゲートウェイも各AZに
踏み台サーバー パブリックサブネットに配置。SSHアクセスは自分のPCのIPに限定
VPCフローログ VPC設計の段階で有効化先を決める。S3(長期保存)かCloudWatch Logs(リアルタイム)を選択
VPC DNS設定 enableDnsHostnames/enableDnsSupportを有効にする(RDSエンドポイント名解決に必須)

VPC設計は「最初に正しく作ると後が楽」なインフラの基盤です。
この記事の構成を参考に、まずは検証環境で一度手を動かしてみてください。
一度作ってみると、各コンポーネントの関係がぐっと分かりやすくなります。

より複雑な冗長設計(マルチリージョン・VPC Peering等)は
AWS冗長設計入門で解説しています。あわせてご覧ください。

現場で通用する安全なLinuxサーバー構築の「型」を体系的に身につけたい方へ、20年以上の運用経験を持つ現役エンジニアが基礎から教えます。
Linux無料マニュアルを受け取る >>

無料メルマガで学習を続ける

Linuxの実践スキルをメールで毎週お届け。
登録は30秒、解除もいつでも可。

登録無料・いつでも解除できます

暗記不要・1時間後にはサーバーが動く

3,100名以上が実践した「型」を無料で公開中

プロのエンジニアはコマンドを暗記していません。
「現場で使える型」を効率よく使いこなしているだけです。
その「型」を図解60Pにまとめた入門マニュアルを、完全無料でプレゼントしています。

姓・名・メールの3つだけ/30秒/解除は3秒 / 詳細はこちら

Linux無料マニュアル(図解60P) 名前とメールで30秒登録
宮崎 智広

この記事を書いた人

宮崎 智広(みやざき ともひろ)

株式会社イーネットマーキュリー代表。現役のLinuxサーバー管理者として20年以上の実務経験を持ち、これまでに累計3,100名以上のエンジニアを指導してきたLinux教育のプロフェッショナル。「現場で本当に使える技術」を体系的に伝えることをモットーに、実践型のLinuxセミナーの開催や無料マニュアルの配布を通じてLinux人材の育成に取り組んでいる。

趣味は、キャンプにカメラ、トラウト釣り。好きな食べ物は、ラーメンにお酒。休肝日が作れない、酒量を減らせないのが悩み。最近、ドラマ「フライトエンジェル」を観て涙腺が崩壊しました。