初めに
「CYBIRD Advent Calendar 2023」 の19日目の記事を担当する@gongon282828です。
18日目の記事は、@cy-yuka-WPさんの「Google Apps Scriptを利用したスケジュール管理」でした。
この記事では、外部ネットワークとAWSをSite-to-Site VPNで疎通し、AWS側では複数のVPNに対して疎通をフレキシブルにとれるように、トランジェントゲートウェイを利用した構築方法を説明します。
前提
サイバードでは、AWSの開発にはIaC(Infrastructure as Code)の実用として、Terraformを利用して構築しているため、その方法について記載します。
その他、諸条件については以下になります。
- Site-to-Site VPNと疎通をとる対象となる外部ネットワーク(オンプレミス環境や他パブリッククラウド等)のIPアドレスやネットワークは事前に用意してあるものとし、割愛します。
- AWS側のVPCは記事の冗長化を避ける目的で、2つのVPCのみ構築する内容で記載し、クロスアカウントでのVPCでのネットワーク疎通については記載しません。
- VPC内のサブネットは、プライベートサブネットの指定のみとします。
- 実際に疎通するAWSリソース(EC2やRDS等)へのセキュリティグループの設定の許可については割愛し、各ネットワークのルートテーブルまでの構築方法について記載します。
ネットワーク構成
以下、ネットワーク構成を示します。
Site-to-Site VPNで外部ネットワークと疎通させ、AWS側はトランジェントゲートウェイで疎通をとっています。
TerraformでのAWSリソースの構築
ここではTerraformで構築する方法を示します。
トランジェントゲートウェイとカスタムゲートウェイの生成
ここでは、トランジェントゲートウェイとカスタムゲートウェイを生成します。
locals{
external_network_public_ip = "x.x.x.x/32"
}
resource "aws_ec2_transit_gateway" "main" {
vpn_ecmp_support = "enable"
default_route_table_association = "disable"
default_route_table_propagation = "disable"
auto_accept_shared_attachments = "disable"
tags = { Name = "tgw" }
}
resource "aws_customer_gateway" "main" {
ip_address = local.external_network_public_ip
bgp_asn = "65000"
type = "ipsec.1"
tags = { "cgw" }
}
今回は、VPNとトランジェントゲートウェイの疎通をとるので、aws_ec2_transit_gateway
のvpn_ecmp_support
はenable
にし、それ以外はdisable
にします。
aws_customer_gateway
(実質的には、これがSite-to-Site VPNのリソース)では、外部ネットワーク内のIPアドレスを指定します。
トランジェントゲートウェイとカスタムゲートウェイの疎通
resource "aws_vpn_connection" "main" {
transit_gateway_id = aws_ec2_transit_gateway.main.id
customer_gateway_id = aws_customer_gateway.main.id
type = "ipsec.1"
static_routes_only = true
tags = { Name = "tgw-cgw-connection" }
}
resource "aws_ec2_transit_gateway_route_table" "main" {
transit_gateway_id = aws_ec2_transit_gateway.main.id
tags = { Name = "tgw-route-table" }
}
data "aws_ec2_transit_gateway_vpn_attachment" "main" {
transit_gateway_id = aws_ec2_transit_gateway.main.id
vpn_connection_id = aws_vpn_connection.main.id
tags = { Name = "twg-vpn-attachment" }
}
resource "aws_ec2_transit_gateway_route_table_association" "vpn" {
transit_gateway_attachment_id = data.aws_ec2_transit_gateway_vpn_attachment.main.id
transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.main.id
}
resource "aws_ec2_transit_gateway_route_table_propagation" "vpn" {
transit_gateway_attachment_id = data.aws_ec2_transit_gateway_vpn_attachment.main.id
transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.main.id
}
Site-to-Site VPN側では、aws_vpn_connection
でトランジェントゲートウェイと紐づけるのですが、外部IPは事前に作成している前提になるので、static_routes_only
はtrue
にします。
トランジェントゲートウェイ側では、トランジェントゲートウェイ用のルートテーブルを作成した上で、Site-to-Site VPNをアタッチさせます。その上で、トランジェントゲートウェイのルートテーブルの関連付け(association)と伝番(propagation)の設定を入れています。
トランジェントゲートウェイとVPCの疎通
トランジェントゲートウェイと各VPCを疎通させます。
ここでは、AWSのVPCの各リソースについては、local
で変数化したものを参照させています。実際に構築する際には、他moduleで生成し参照させる方法が一般的かと思います。
locals{
external_network = "x.x.x.x/16"
aws_network = {
env_1 = {
env = "env_1"
vpc_id = "x.x.x.x/16"
private_subnet_ids = ["x.x.0.x/24","x.x.1.x/24"]
private_route_table_ids = "rtb-xxxxxxxxxx"
}
env_2 = {
env = "env_2"
vpc_id = "x.x.x.x/16"
private_subnet_ids = ["x.x.0.x/24","x.x.1.x/24"]
private_route_table_ids = "rtb-xxxxxxxxxx"
}
}
}
resource "aws_ec2_transit_gateway_vpc_attachment" "main" {
count = length(local.aws_network[*])
transit_gateway_id = aws_ec2_transit_gateway.main.id
vpc_id = local.aws_network[count.index].vpc_id
subnet_ids = local.aws_network[count.index].private_subnet_ids
transit_gateway_default_route_table_association = false
transit_gateway_default_route_table_propagation = false
tags = {
Name = "${local.aws_network[count.index].env}-twg-vpc-attachment"
}
}
resource "aws_ec2_transit_gateway_route_table_association" "aws" {
count = length(aws_ec2_transit_gateway_vpc_attachment.main[*])
transit_gateway_attachment_id = element(aws_ec2_transit_gateway_vpc_attachment.main, count.index)
transit_gateway_route_table_id = aws_ec2_transit_gateway_route_table.main.id
}
resource "aws_route" "main" {
count = length(local.aws_network[*])
route_table_id = local.aws_network[count.index].private_route_table_ids
transit_gateway_id = aws_ec2_transit_gateway.main.id
destination_cidr_block = locals.external_network
}
まず、aws_ec2_transit_gateway_vpc_attachment
でトランジェントゲートウェイと各VPCの紐づけを、サブネット単位で行います。
次に、トランジェントゲートウェイ側のルートテーブルに関連付けと伝番を設定します。
最後に、各VPCのルートテーブル側で、外部ネットワークのciderを登録します。
以上が、Terraformでの構築方法になります。
補足
実践の場では、外部ネットワーク側のルーティングについては仮想ルーターでOSSとなるVyOSを活用して外部ネットワークの疎通リソースを構築しました。
また、VyOS側の作業として、AWSコンソール上で出力される設定情報を流し込む必要があります。
なお、VyOSはLTSバージョンは有償にはなるのですが、isoイメージをビルドするソースは公開されているので、isoイメージについても別途作成する必要があります。
最後に
トランジェントゲートウェイを利用することで、例えば環境の部分移管やAWS側の一部VPCを廃止させたい時など、稼働しているネットワークに影響を与えずに、ネットワーク構成をフレキシブルに変更することができ、便利に活用しています。
また、実はいうとこの記事は本当は書く予定はなかったのですが、アドベントカレンダーの記事はQiita内で重複参照できないことを前日朝に気づき(絶望...)、急遽、本記事を用意しました。
「AWS for Games Advent Calendar 2023」 の19日目の記事「ECS Fargateでのx86_64とarmのマルチアーキテクチャ実装概略とTerraformでの構築」も担当しているので、ぜひそちらもチェックいただければ嬉しいです!
「CYBIRD Advent Calendar 2023」 、20日目は@ice_matcha3さんの「ロードバランサの流量調整を動的にやってみた!」です。
サイバードのアドベントカレンダーは明日も引き続きインフラトピックになります!
乞うご期待!!