【実務・中級編】 Transit Gateway(TGW)ルートテーブルアソシエーションと伝播(Propagation)の制御 – クラウドインフラと仮想化ネットワーク実践ガイド

こんにちは。現場で数々の「ネットワークが繋がらない」「開発環境から本番データベースにパケットが素通りしている」といった冷や汗もののトラブルを解決してきたシニアネットワークエンジニアです。

AWSで複数のVPCやオンプレミス拠点を抱えるマルチVPCアーキテクチャを設計する際、避けて通れないのがTransit Gateway(以下、TGW)です。多くのエンジニアが「とりあえずTGWを作って、すべてのVPCをアタッチすれば繋がるだろう」と安易に考えて構築を始めます。しかし、デフォルト設定のまま運用を始めると、不要なVPC間通信が素通りし、セキュリティの境界線(セグメンテーション)が崩壊してしまいます。

TGWを真にコントロールし、本番環境、開発環境、共有サービス(Shared Services)、そしてオンプレミス間のトラフィックを安全かつ意図通りに制御するために不可欠なのが、「アソシエーション(Association)」と「伝播(Propagation)」の正確な理解です。

今回は、教科書的な説明を越えて、パケットがどのようにTGW内をルーティングされるのか、その裏側の挙動やTerraformによるベストプラクティスコード、そして現場で役立つデバッグ手順まで徹底的に解説します。

—

1. アソシエーションと伝播(Propagation)の決定的な違い

TGWのルーティングを理解する最大の鍵は、「アソシエーション」と「伝播」を全く別の独立した概念として脳内で分離することです。まずはこの2つの定義を頭に叩き込んでください。

アソシエーション(Association)=「パケットの入り口」

アソシエーションは、「あるVPC(アタッチメント)からTGWに入ってきたパケットが、どのTGWルートテーブルを参照すべきか」を決定する紐付けです。

  • 1つのアタッチメント(VPCやVPNなど)は、必ず1つのTGWルートテーブルにのみアソシエーションできます。
  • パケットがアタッチメントからTGWに到達した瞬間、TGWはアソシエーションされたルートテーブルを見て「次にどこへパケットを送るべきか」を判断します。

伝播(Propagation)=「パケットの出口情報の学習」

伝播は、「あるアタッチメントが持つネットワーク帯(CIDR)を、どのTGWルートテーブルに自動登録(学習)させるか」を決定する制御です。

  • アソシエーションとは異なり、1つのアタッチメントは複数のTGWルートテーブルに対して伝播を設定できます。
  • 例えば、本番VPCのアタッチメントを「本番用TGWルートテーブル」と「共有サービス用TGWルートテーブル」の両方に伝播させると、それぞれのルートテーブルに本番VPCのCIDR(例: 10.1.0.0/16)が自動的に登録されます。

通信フローの可視化(シーケンス)

VPC-A(送信元)からVPC-B(送信先)へ通信が行われる際の、TGW内部の動きを見てみましょう。

[ VPC-A ] 
   │ 
   │ 1. パケット送信 (宛先: VPC-BのIP `10.2.0.10`)
   ▼
[ TGW Attachment-A ]
   │
   │ 2. ルートテーブルの決定 (Associationに基づく)
   ▼
[ TGW Route Table (Associated with Attachment-A) ]
   │
   │ 3. ルートの検索 (宛先 `10.2.0.0/16` への経路があるか?)
   │    ※ この経路情報は、Attachment-Bの「Propagation」によって自動登録されている
   ▼
[ TGW Attachment-B ]
   │ 
   │ 4. パケット転送
   ▼
[ VPC-B ]

このシーケンスから分かるように、双方向通信を成立させるためには、「VPC-AのTGWルートテーブルにVPC-Bへのルートがあり(VPC-Bからの伝播)」、かつ「VPC-BのTGWルートテーブルにVPC-Aへのルートがある(VPC-Aからの伝播)」という、双方の伝播が成立していなければなりません。

—

2. 実務で最も使われる3大デザインパターン

TGWを構築する際、デフォルト設定の「Default Route Table Association/Propagation」を有効にしたままにするのは、検証環境だけにしてください。本番システムでは、以下のいずれかのパターンを用いて、明示的にトラフィックを分離(セグメンテーション)します。

パターンA:フルメッシュ(検証環境・小規模向け)

  • 挙動:すべてのVPCが相互に通信可能。
  • 設定:単一のTGWルートテーブルを用意し、すべてのアタッチメントをそこにアソシエーションし、かつすべての物理アタッチメントからそのルートテーブルに伝播させます。

パターンB:ハブ&スポーク(共有サービス接続)

  • 挙動:各VPCは「共有サービスVPC(AD、DNS、監視サーバー等)」や「オンプレミス」とは通信できるが、VPC同士(例えばVPC-AとVPC-B)は一切通信させない。
  • 設定:
  • 「スポーク用TGWルートテーブル」と「共有用TGWルートテーブル」を分離。
  • スポークVPCは、共有VPCへの伝播のみを許可。

パターンC:本番/開発の完全隔離(エンタープライズの標準)

  • 挙動:本番環境(Prod)グループと開発環境(Dev)グループを完全に隔離し、本番と開発が直接通信できないようにする。
  • 設定:
  • TGWルートテーブルを「Prod用」と「Dev用」に2分割。
  • Prod VPCのアタッチメントは「Prod用TGWルートテーブル」にアソシエーション&伝播。
  • Dev VPCのアタッチメントは「Dev用TGWルートテーブル」にアソシエーション&伝播。

—

3. Terraformで実装するセグメンテーション(本番/開発の分離)

GUIでのポチポチ設定は、設定漏れやセキュリティホールの原因になります。ここでは、実務でそのまま使えるTerraform (v1.0+)を用いた「本番環境(Prod)と開発環境(Dev)の完全隔離パターン」のHCL(HashiCorp Configuration Language)コード例を示します。

デフォルトの自動アソシエーションと自動伝播を明示的に false にし、手動で厳密に紐付けるのがプロの設計です。

# ==========================================
# Transit Gateway 本体の定義
# ==========================================
resource "aws_ec2_transit_gateway" "main" {
  description                     = "Main TGW for Enterprise Segmentation"
  default_route_table_association = "disable" # デフォルトアソシエーションは必ず無効化
  default_route_table_propagation = "disable" # デフォルト伝播も必ず無効化

  tags = {
    Name = "main-tgw"
  }
}

# ==========================================
# TGW ルートテーブルの定義(本番用と開発用を分離)
# ==========================================
resource "aws_ec2_transit_gateway_route_table" "prod" {
  transit_gateway_id = aws_ec2_transit_gateway.main.id

  tags = {
    Name = "tgw-rt-prod"
  }
}

resource "aws_ec2_transit_gateway_route_table" "dev" {
  transit_gateway_id = aws_ec2_transit_gateway.main.id

  tags = {
    Name = "tgw-rt-dev"
  }
}

# ==========================================
# TGW アタッチメントの定義(VPC A = Prod, VPC B = Dev)
# ==========================================
resource "aws_ec2_transit_gateway_vpc_attachment" "prod_vpc" {
  transit_gateway_id = aws_ec2_transit_gateway.main.id
  vpc_id             = "vpc-0123456789abcdef0" # 本番VPCのID(実際の環境に合わせて変更)
  subnet_ids         = ["subnet-001", "subnet-002"] # TGW用に作成したサブネット

  transit_gateway_default_route_table_association = false
  transit_gateway_default_route_table_propagation = false

  tags = {
    Name = "tgw-att-prod-vpc"
  }
}

resource "aws_ec2_transit_gateway_vpc_attachment" "dev_vpc" {
  transit_gateway_id = aws_ec2_transit_gateway.main.id
  vpc_id             = "vpc-0987654321fedcba0" # 開発VPCのID(実際の環境に合わせて変更)
  subnet_ids         = ["subnet-003", "subnet-004"]

  transit_gateway_default_route_table_association = false
  transit_gateway_default_route_table_propagation = false

  tags = {
    Name = "tgw-att-dev-vpc"
  }
}

# ==========================================
# アソシエーション(パケットの入り口)の制御
# ==========================================
# Prod VPCから入るパケットは、Prod用ルートテーブルを参照
resource "aws_ec2_transit_gateway_route_table_association" "prod_association" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.prod_vpc.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.prod.id
}

# Dev VPCから入るパケットは、Dev用ルートテーブルを参照
resource "aws_ec2_transit_gateway_route_table_association" "dev_association" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.dev_vpc.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.dev.id
}

# ==========================================
# 伝播(出口情報の学習)の制御
# ==========================================
# Prod VPCのCIDR情報を、Prod用ルートテーブルに伝播(学習)させる
resource "aws_ec2_transit_gateway_route_table_propagation" "prod_propagation" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.prod_vpc.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.prod.id
}

# Dev VPCのCIDR情報を、Dev用ルートテーブルに伝播(学習)させる
resource "aws_ec2_transit_gateway_route_table_propagation" "dev_propagation" {
  transit_gateway_attachment_id  = aws_ec2_transit_gateway_vpc_attachment.dev_vpc.id
  transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.dev.id
}

この構成により、prod-vpc と dev-vpc のルーティング情報はそれぞれのテーブルにしか存在しないため、物理的にパケットが混ざり合うことは100%ありません。

—

4. 泥臭い現場で役立つデバッグ手順:パケットが届かない時の3つの壁

「設定は完璧なはずなのに、VPC間でピン(ping)すら通らない!」
ネットワーク構築の現場では日常茶飯事です。焦ってTGWを再作成する前に、パケットが通る経路を「3つの壁」に分解してデバッグしましょう。

[ 送信元インスタンス ]
       │
  【第1の壁】VPCルートテーブル(ローカルVPC内)
       ▼
[ TGW アタッチメント ]
       │
  【第2の壁】TGWルートテーブル(アソシエーション & 伝播 / 静的ルート)
       ▼
  【第3の壁】セキュリティグループ / ネットワークACL(宛先VPC内)
       ▼
[ 送信先インスタンス ]

【第1の壁】VPCルートテーブルの確認

TGWにパケットが到達する前に、送信元VPCのルートテーブルで「宛先IP宛てのパケットをTGWアタッチメントに投げる」設定(例: 10.2.0.0/16 -> tgw-xxxxxx)が漏れていないか確認します。

  • Tips: TGWアタッチメントを作成しただけでは、VPCのルートテーブルは自動更新されません。必ず各サブネットのルートテーブルに、手動またはTerraformでTGW宛てのルートを追加してください。

【第2の壁】TGWルートテーブルの確認(AWS CLIを駆使する)

TGW内でルーティングが迷子になっていないか確認します。マネジメントコンソールで探すのも良いですが、CLIで一発でルート情報を引き出すのがプロです。

特定のTGWルートテーブルに、宛先(例: 10.2.0.0/16)へのルートが存在するか検索するコマンド:

aws ec2 search-transit-gateway-routes \
    --transit-gateway-route-table-id tgw-rtb-0123456789abcdef0 \
    --filters "Name=route-search.exact-match,Values=10.2.0.0/16" \
    --region ap-northeast-1
  • 出力結果のチェックポイント:
  • state が active になっているか? (blackhole になっていないか)
  • type が propagated(伝播によって動的学習されたルート)または static(静的ルート)になっているか?
  • 期待するアタッチメントID(transitGatewayAttachmentId)に向いているか?

【第3の壁】セキュリティグループと「戻り」のルート

行き(AからB)のルートが完璧でも、「帰り(BからA)のルート」がなければTCPのスリーウェイ・ハンドシェイクは完了せず、通信はタイムアウトします。

  • 宛先VPCのセキュリティグループで、送信元VPCのCIDR(例: 10.1.0.0/16)からのインバウンド通信が許可されているか?
  • 宛先VPCのルートテーブルに、送信元VPC宛てのパケットをTGWに返すルートが書かれているか?

—

5. まとめ:ネットワークの制御権をその手に

Transit Gatewayは、大規模なAWSマルチアカウント・マルチVPC環境における「交通整理の司令塔」です。
その心臓部であるアソシエーションと伝播を理解し、デフォルト設定に頼らず明示的に分離設計を行うことで、強固なセキュリティセグメンテーションが実現できます。

  • アソシエーションは、どのテーブルで「行き先を探すか」を決める。
  • 伝播は、そのテーブルに「誰がどこにいるか」を教える。

この基本原則と、今回紹介したTerraformによる実装、そしてAWS CLIを用いたトラブルシューティング手法を武器に、ぜひ安全でスケーラブルなクラウドインフラを構築してください。ネットワークを制する者が、システム全体の信頼性を制するのです。

コメント

タイトルとURLをコピーしました