外部ネットワークとAWSのVPCをSite-to-Site VPNとトランジェントゲートウェイで疎通する

# 初めに
「[CYBIRD Advent Calendar 2023](https://qiita.com/advent-calendar/2023/cybird)」 の19日目の記事を担当する[@gongon282828](https://qiita.com/gongon282828)です。

18日目の記事は、[@cy-yuka-WP](https://qiita.com/cy-yuka-WP)さんの「[Google Apps Scriptを利用したスケジュール管理](https://qiita.com/cy-yuka-WP/private/a445e7df6301ed80e286)」でした。

この記事では、外部ネットワークと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等)へのセキュリティグループの設定の許可については割愛し、各ネットワークのルートテーブルまでの構築方法について記載します。

# ネットワーク構成
以下、ネットワーク構成を示します。

![ネットワーク構成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3626152/659cab8e-9503-b842-f2f1-18730075072d.png)

Site-to-Site VPNで外部ネットワークと疎通させ、AWS側はトランジェントゲートウェイで疎通をとっています。

# TerraformでのAWSリソースの構築
ここではTerraformで構築する方法を示します。

## トランジェントゲートウェイとカスタムゲートウェイの生成

ここでは、トランジェントゲートウェイとカスタムゲートウェイを生成します。

```terraform: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アドレスを指定します。

## トランジェントゲートウェイとカスタムゲートウェイの疎通

```terraform:terraform
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で生成し参照させる方法が一般的かと思います。

```terraform:terraform
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イメージについても別途作成する必要があります。

https://github.com/vyos/

# 最後に
トランジェントゲートウェイを利用することで、例えば環境の部分移管やAWS側の一部VPCを廃止させたい時など、稼働しているネットワークに影響を与えずに、ネットワーク構成をフレキシブルに変更することができ、便利に活用しています。

また、実はいうとこの記事は本当は書く予定はなかったのですが、アドベントカレンダーの記事はQiita内で重複参照できないことを前日朝に気づき(絶望...)、急遽、本記事を用意しました。

「[AWS for Games Advent Calendar 2023](https://qiita.com/advent-calendar/2023/aws-for-games)」 の19日目の記事「[ECS Fargateでのx86_64とarmのマルチアーキテクチャ実装概略とTerraformでの構築](https://qiita.com/gongon282828/private/e1e852c77e242c22299b)」も担当しているので、ぜひそちらもチェックいただければ嬉しいです！

「[CYBIRD Advent Calendar 2023](https://qiita.com/advent-calendar/2023/cybird)」 、20日目は[@ice_matcha3](https://qiita.com/ice_matcha3)さんの「[ロードバランサの流量調整を動的にやってみた！](https://qiita.com/ice_matcha3/items/b4e05b66a422a75aad57)」です。

サイバードのアドベントカレンダーは明日も引き続きインフラトピックになります！
乞うご期待！！