【AWS Transit Gateway】複数VPCのアウトバウンド通信を集約する環境を作る

2022.04.06

アウトバウンド通信を集約するモチベーション

多くのVPCを稼働させているAWS環境では 「ネットワークセキュリティの統制」が煩雑になりがちです。 特に インターネットへ出ていく通信(アウトバウンド通信) に焦点を当てると、 以下のような要件がよく出てきます。

  • アウトバウンド通信のログを集中管理したい
  • アウトバウンド通信を一元的に検査および保護したい

各VPC上で独立して Internetゲートウェイ(IGW) および NATゲートウェイ(NATGW) を作成するのが最も直感的です。しかし VPCが増えるにつれて統制や管理コストが肥大してきます。

img

▲ 図: 各VPC独立でアウトバウンド通信を行う構成。次第に統制・管理コストが大きくなる

そういった背景があり、 アウトバウンド通信をどうにかして集約したい ニーズが出てきます。

img

▲ 図: アウトバウンド通信をどうにかして集約したい NATGW

Transit Gateway で実現

AWS Transit Gateway(TGW) を使ってアウトバウンド通信の集約を実現します。

TGW はネットワーク間の ハブ接続 を実現するサービスです。 VPCやオンプレ環境のネットワーク(スポーク) を TGW(ハブ) に接続できます。 そして TGW(ハブ) 上でネットワーク(スポーク)間の通信を集中管理します。 ネットワークの接続、ルーティングの運用負荷を軽減できることがメリットです。

どのようにアウトバウンド通信を集約するか 、以下がざっくりとした構成図です。

img

アウトバウンド通信集約用のVPC を作成して、そこに NATGW環境を作成します。 そして全ての VPCを TGWに接続(Attach)します。 TGWのルーティングで 「アウトバウンド通信は全て NATGW環境へ渡す」 ような設定をいれます。

以上が概要です。 もう少し細かい設計をこれからしていきます。

設計の前提

img

  • アウトバウンド通信を行いたいワークロード環境: APP-VPC#1 〜 APP-VPC#n
  • アウトバウンド通信を行うための NATGW環境: NAT-VPC

上記 2種のVPC群をベースにした設計をしていきます。 東京リージョンの 2 Availability Zone(AZ) 構成を前提として進めます。 また、 APP-VPC 同士の通信は無い ように作ります。

少し細かい設計(サブネット, TGWアタッチメント編)

サブネット設計

img

  • APP-VPC#x … 2AZ配置、それぞれワークロード用サブネットと TGW用サブネット
  • NAT-VPC … 2AZ配置、それぞれパブリックサブネットと TGW用サブネット 。 NATGWを配置

上述のようなサブネット構成とします。

TGW用サブネット を用意しているところがポイントです。 これは TGWアタッチメント(後述) 専用の小さなCIDR( /28 など) のサブネットです。 Transit Gateway 設計のベストプラクティス を踏襲しています。

TGWアタッチメント設計

img

とにもかくにも TGWリソース、およびTGWアタッチメントリソースを作成 するところから TGW活用は始まります。

TGWリソースは以下のような設定で作成しました。

  • Default route table association: 無効(disable)
  • Default route table propagation: 有効(enable)

つまり、デフォルトルートテーブルを作成しておきます。 そして、TGWアタッチメントからのプロパゲーション(伝播)ルートを このルートテーブルに自動で反映させます。 (このルートテーブルの活用は 次章 もう少し細かい設計(ルーティング編) で詳しく説明します)

TGWアタッチメントは VPC単位で作成します 。 作成の際に、サブネットを複数指定できます。 TGW用サブネット 2つ (AZ-A, AZ-C) を指定しましょう。 (TGWは内部的な動作として、指定したサブネットに ネットワークインターフェース: ENI を配置します)。

これで土台は作れました。次章でルート設計を行います。

少し細かい設計(ルーティング編)

TGWルートテーブル設計

img

TGWアタッチメント(APP-VPC) 用に TGWルートテーブルを 1つ作成しておきます。 このルートテーブルを 各TGWアタッチメント(APP-VPC)に 関連付け(Association) させます。

また、前章でデフォルトルートテーブルを別途作っていました。 これは TGWアタッチメント(NAT-VPC)に 関連付け(Association) させます。 ※ このデフォルトルートテーブルは TGWアタッチメントからの伝播が有効になっていました。 そのため、デフォルトで図のような伝播ルートが生成されています。

ルート設計 (行きの通信)

img

Workload subnet内 のリソースがインターネットへ出るための 行きの通信 を考えながらルートを設計します。

①: VPCルート(Workload subnet in APP-VPC)

とりあえずデフォルトルートをTGWに向けましょう 。 その後の ルート制御は TGWがやってくれます。

②: TGWルート(APP TGW Route Table)

インターネットに出るためには NAT-VPC へ行くしかありません。 デフォルトルートを TGWアタッチメント(NAT-VPC) に向けましょう。

③: VPCルート(TGW subnet in NAT-VPC)

ここからは、よくある NATGW VPCのルーティングです。 デフォルトルートを NATGWに向けます。 ※ 2AZ構成としているので ルートテーブルは 2つ作ります。

④: VPCルート(Public subnet in NAT-VPC)

通常のパブリックサブネット設定です。 デフォルトルートを Internet Gateway(IGW) に向けます。

ルート設計 (戻りの通信)

img

インターネットからの 戻りの通信 を考えながらルートを設計します。

①: VPCルート(Public subnet in NAT-VPC)

  • 帰り道のゴールは 各APP-VPC の IPアドレス であること
  • 帰るためには TGWを経由 する必要があること

上記 2つを意識します。

宛先の CIDRは プレフィクスリスト を活用すると良いでしょう。 複数 CIDRをまとめて管理できます。

宛先(プレフィクスリスト)のターゲットは TGWです。 帰るためには、必ず TGWを経由しないといけません。

②: TGWルート(Default Propagation TGW RouteTable)

ここは デフォルト伝播のルートテーブルを使っているおかげで、 特に追加のルート(staticルート)は必要無い です。 帰りたい APP-VPC へのTGWアタッチメントへ向かってくれます。

③: VPCルート(TGW subnet in APP-VPC)

ここまできたら Workload subnet のリソースへ帰ることができます。 そのため特にルートの追加はありません (デフォルトである localルート)。

追加ルート (APP-VPC間通信の防止)

APP-VPC 同士の通信は無い 要件のために追加設定が必要です。

このままでは、例えば APP-VPC#1 から APP-VPC#2 への通信を試みたときに NAT-VPCのNATGW経由で接続できてしまいます 。 (参考: この事象の詳細 → Transit Gatewayでインターネットの出口を集約した際の思わぬ弊害とその対応(Blackholeルート) | DevelopersIO )

対策として、 APP TGW RouteTableAPP-VPC 宛先の通信をドロップするルート を追加します。

img

ターゲットを Blackhole とすることで実現できます。 宛先CIDRは PrefixList を使うと便利です。 2020/08 のアップデート で TGWルートテーブルに対しても プレフィクスリストのルートを追加できるようになっています。

ルート設計まとめ

ごちゃってますが、これまでのルート設計を下図にまとめます。

img

構築してみる

img

上図のような APP-VPC#1, APP-VPC#2, NAT-VPC からなる アウトバウンド集約環境を作ります。

Terraform を使って構築しました。コードは以下に置いています。

(参考) 作成されるリソース

$ terraform plan | grep "will be created"
  # aws_ec2_managed_prefix_list.app will be created
  # aws_ec2_managed_prefix_list_entry.app1 will be created
  # aws_ec2_managed_prefix_list_entry.app2 will be created
  # aws_ec2_transit_gateway.main will be created
  # aws_ec2_transit_gateway_prefix_list_reference.app_blackhole will be created
  # aws_ec2_transit_gateway_route.app_default will be created
  # aws_ec2_transit_gateway_route_table.app will be created
  # aws_ec2_transit_gateway_route_table_association.center will be created
  # aws_ec2_transit_gateway_vpc_attachment.center will be created
  # aws_eip.center_a will be created
  # aws_eip.center_c will be created
  # aws_internet_gateway.center will be created
  # aws_nat_gateway.center_a will be created
  # aws_nat_gateway.center_c will be created
  # aws_route.center_public_to_igw will be created
  # aws_route.center_tgw will be created
  # aws_route.center_tgw_to_natgw_a will be created
  # aws_route.center_tgw_to_natgw_c will be created
  # aws_route_table.center_public will be created
  # aws_route_table.center_tgw_a will be created
  # aws_route_table.center_tgw_c will be created
  # aws_route_table_association.center_public_a will be created
  # aws_route_table_association.center_public_c will be created
  # aws_route_table_association.center_tgw_a will be created
  # aws_route_table_association.center_tgw_c will be created
  # aws_subnet.center_public_a will be created
  # aws_subnet.center_public_c will be created
  # aws_subnet.center_tgw_a will be created
  # aws_subnet.center_tgw_c will be created
  # aws_vpc.center will be created
  # module.app1_tgwatt.aws_ec2_transit_gateway_route_table_association.app will be created
  # module.app1_tgwatt.aws_ec2_transit_gateway_vpc_attachment.app will be created
  # module.app1_tgwatt.aws_route.app_to_tgw will be created
  # module.app1_vpc.aws_route_table.private will be created
  # module.app1_vpc.aws_route_table.tgw will be created
  # module.app1_vpc.aws_route_table_association.private_a will be created
  # module.app1_vpc.aws_route_table_association.private_c will be created
  # module.app1_vpc.aws_route_table_association.tgw_a will be created
  # module.app1_vpc.aws_route_table_association.tgw_c will be created
  # module.app1_vpc.aws_security_group.test will be created
  # module.app1_vpc.aws_subnet.private_a will be created
  # module.app1_vpc.aws_subnet.private_c will be created
  # module.app1_vpc.aws_subnet.tgw_a will be created
  # module.app1_vpc.aws_subnet.tgw_c will be created
  # module.app1_vpc.aws_vpc.main will be created
  # module.app2_tgwatt.aws_ec2_transit_gateway_route_table_association.app will be created
  # module.app2_tgwatt.aws_ec2_transit_gateway_vpc_attachment.app will be created
  # module.app2_tgwatt.aws_route.app_to_tgw will be created
  # module.app2_vpc.aws_route_table.private will be created
  # module.app2_vpc.aws_route_table.tgw will be created
  # module.app2_vpc.aws_route_table_association.private_a will be created
  # module.app2_vpc.aws_route_table_association.private_c will be created
  # module.app2_vpc.aws_route_table_association.tgw_a will be created
  # module.app2_vpc.aws_route_table_association.tgw_c will be created
  # module.app2_vpc.aws_security_group.test will be created
  # module.app2_vpc.aws_subnet.private_a will be created
  # module.app2_vpc.aws_subnet.private_c will be created
  # module.app2_vpc.aws_subnet.tgw_a will be created
  # module.app2_vpc.aws_subnet.tgw_c will be created
  # module.app2_vpc.aws_vpc.main will be created

確認

「アウトバウンド通信」を確認

EC2インスタンスを APP-VPC#1 および APP-VPC#2 上に配置します。

img

APP-VPC#1 上のインスタンスへ Session Manager を使って接続します。 curl -I https://dev.classmethod.jp/ を実行してインターネットへのアウトバウンド通信が できるかどうか確かめてみます。

img

ちゃんと情報取得できていますね。アウトバウンド通信ができていることを確認できました。

「VPC間通信の防止」を確認

APP-VPC#2 上のインスタンスへ ping を飛ばしてみます。

img

APP-VPC#1 と APP-VPC#2 との間の通信ができないことを確認しました。 TGWルートテーブルの Blackholeルートが機能していますね。

VPC Reachability Analyzer で色々確認

これは VPC内の接続性テストを行えるサービスです。 ※このサービスの詳細は以下参照ください

早速 このサービスを使って以下に示す接続性テストを行ってみました。

パス 内容 想定する回答
APP-VPC1_to_APP-VPC2 APP-VPC#1 instance から APP-VPC#2 instance への接続テスト 到達不可能
APP-VPC1_to_Internet APP-VPC#1 instance から Internet(IGW) への接続テスト 到達可能
NATGW_to_APP-VPC1 NATGW からなる APP-VPC#1 instance への接続テスト 到達可能

結果はこちら。想定通りになりました。

img

APP-VPC1_to_APP-VPC2 詳細

以下のように「到達不可能である理由」を表示してくれます。

img

Transit Gateway ルートテーブル tgw-rtb-019d812f20af61b09 には tgw-attach-037e474c52768c3de への適切なルートがありません。

tgw-rtb-019d812f20af61b09 は TGWアタッチメント(for APP-VPC) に関連付けていたルートテーブルでした。 以下のように Blackholeルートを入れていることが到達不可能の原因となります。

img

APP-VPC1_to_Internet 詳細

到達可能である場合は、以下のようにパスが表示されます。TGW経由で IGWへ接続できることを確認できます。

img

NATGW_to_APP-VPC1 詳細

NATGWからの 戻りの通信も到達可能です。以下のようなパスとなります。

img

VPC Network Access Analyzer で色々確認

これは ネットワークインターフェース間のパスを分析してくれるサービスです。 ※このサービスの詳細は以下参照ください

早速使用してみます。使用開始時にデフォルトで作成されている 4つのスコープの内 AWS-IGW-Egress(IGWへのアウトバウンドパスの特定) を選択して、結果を見てみます。

img

結果はこちら。 「 instance-APP-VPC#1 から nwinfra-dev-center-igw へのパス」、および 「 instance-APP-VPC#2 から nwinfra-dev-center-igw へのパス」が表示されていることが分かります。 (同じ内容のパス?が 重複して表示されている原因は分かりませんでした)

img

結果を選択すると VPC Reachability Analyzer と同じようにパスが表示されます。

img

この AWS-IGW-Egress の結果からも APP-VPC から NAT-VPC 経由で アウトバウンド通信を行えることを確認できました。

おわりに

Transit Gateway を活用して、アウトバウンド通信を集約する環境を作成してみました。

このあとの拡張として Network Firewall によるアウトバウンド通信の検査や、 VPC Flow Logs によるアウトバウンド通信ログの集中管理が考えられます。

img

▲ 図: Network Firewall および VPC Flow Logs の導入イメージ

これら集中管理の恩恵を受けるためにも、Transit Gateway の各要素 やネットワークルーティングの理解は大事です。 最初はルート設計が少し煩雑に感じるかも知れません。 行きの通信だけではなく、戻りの通信も考慮することが大事です。

参考