マルチVPCにおけるVPCエンドポイントの集約構成をTerraformで構築してみた

2023.04.20

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

AWS事業本部のイシザワです。

今回はマルチVPC環境におけるVPCエンドポイントの集約構成をTerraformで構築してみます。

以下の記事の続き物です。この記事単体でも読めるようにしていますが、名前解決の集約は既に行っていることを前提とします。

構成要素

VPCエンドポイント

AWS SDKやAWS CLIを使ってAWSサービスの操作を行うにはAWSサービスエンドポイントにアクセスする必要があります。リージョナルサービスでは各リージョンにエンドポイントがあり、例えば東京リージョンのAWS Systems Managerであれば以下のURLがサービスエンドポイントとなります。

https://ssm.ap-northeast-1.amazonaws.com

このサービスエンドポイントはパブリックインターフェースなので、EC2からサービスエンドポイントにアクセスするためにはインターネットゲートウェイを経由する必要があります。

試しにEC2インスタンスからssm.ap-northeast-1.amazonaws.comを名前解決するとパブリックIPが返ってくることが確認できます。

$ resolvectl query ssm.ap-northeast-1.amazonaws.com
ssm.ap-northeast-1.amazonaws.com: 52.119.221.73 -- link: ens5

-- Information acquired via protocol DNS in 2.1ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network

VPCエンドポイントはAWSのサービスにプライベートにアクセスするための仕組みです。インターフェース型とゲートウェイ型のVPCエンドポイントがありますが、ゲートウェイ型だと他VPCから直接アクセスすることができないので集約構成においてはインターフェース型を使用します。

インターフェース型のエンドポイントでは、ネットワークインターフェースを作成するサブネットを指定します。このエンドポイントにはDNS名でアクセスします。

Route 53 Private Hosted Zone

Route 53 Private Hosted Zoneは関連付けたVPC内で利用できるDNSコンテンツサーバです。

AWS SDKやAWS CLIを使うとデフォルトではDNS名が[service_code].[region_code].amazonaws.comで表されるAWSサービスエンドポイントにアクセスします。 Route 53 Private Hosted Zoneにエイリアスレコードを追加することで、このDNS名に対するアクセスをVPCエンドポイントに向かわせることができます。

Transit Gateway

名前解決の集約構成と同様に、VPCどうしを接続するために使用します。

VPC IPAM (IP Address Manager)

名前解決の集約構成と同様に、CIDRの重複を避けるために使用します。

全体構成

以下の構成によりVPCエンドポイントをVPCエンドポイント集約VPCに集約します。

  • VPCエンドポイント集約VPCにVPCエンドポイントを作成する。
  • AWSサービスエンドポイントへのアクセスを↑で作成したVPCエンドポイントに向かわせるためのRoute 53 Private Hosted Zoneを名前解決VPCに作成する。

全体の構成は以下の図の通りです。今回はSSMセッションマネージャーをインターネットゲートウェイの無いワークロードVPCから利用できるようにします。

Terraformコード

ソースコードはGitHubに載せています。いくつかピックアップして解説をしていきます。

ソースコード

VPCエンドポイント

VPCエンドポイントにアタッチするセキュリティグループを作成します。VPCエンドポイントにはHTTPSでアクセスするのでインバウンドルールで443/tcpを許可します。

modules/vpce_aggregation/vpc_endpoint.tf

resource "aws_security_group" "allow_https" {
  name        = "allow_https"
  description = "Allow HTTPS inbound traffic"
  vpc_id      = aws_vpc.main.id

  ingress {
    from_port   = 443
    to_port     = 443
    protocol    = "tcp"
    cidr_blocks = [var.toplevel_pool_cidr]
  }

  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
  }

  tags = {
    Name = "allow_https"
  }
}

VPCエンドポイントのプライベートDNSは集約構成の場合は不要のため無効にしています。 これを有効にすると作成したVPCにAWS管理のPrivate Hosted Zoneが作成されて、デフォルトのサービスエンドポイントのDNS名とVPCエンドポイントのDNS名を関連付けるエイリアスレコードが作成されます。 集約構成で無い場合はRoute 53の設定が不要となるので設定することをお勧めします。

modules/vpce_aggregation/modules/vpc_endpoint/vpc_endpoint.tf

locals {
  service_name = "com.amazonaws.${var.region_name}.${var.service_code}"
  domain_name  = "${var.service_code}.${var.region_name}.amazonaws.com"
}

resource "aws_vpc_endpoint" "main" {
  vpc_id              = var.vpc_id
  subnet_ids          = [var.subnet_id]
  vpc_endpoint_type   = "Interface"
  service_name        = local.service_name
  private_dns_enabled = false

  security_group_ids = [var.security_group_id]

  tags = {
    Name = "${var.system_id}-vpce-${var.service_code}"
  }
}

Route 53 Private Hosted Zone

デフォルトのサービスエンドポイントのDNS名とVPCエンドポイントのDNS名を関連付けるエイリアスレコードをPrivate Hosted Zoneに登録します。 インターフェースのDNS名はdns_entry[0]["dns_name"]で、DNS名が登録されているホストゾーンIDはdns_entry[0]["hosted_zone_id"]で参照できます。

Private Hosted ZoneはVPCエンドポイントごとに作成します。

modules/vpce_aggregation/modules/vpc_endpoint/vpc_endpoint.tf

locals {
  service_name = "com.amazonaws.${var.region_name}.${var.service_code}"
  domain_name  = "${var.service_code}.${var.region_name}.amazonaws.com"
}

resource "aws_vpc_endpoint" "main" {
  vpc_id              = var.vpc_id
  subnet_ids          = [var.subnet_id]
  vpc_endpoint_type   = "Interface"
  service_name        = local.service_name
  private_dns_enabled = false

  security_group_ids = [var.security_group_id]

  tags = {
    Name = "${var.system_id}-vpce-${var.service_code}"
  }
}

resource "aws_route53_zone" "main" {
  name = local.domain_name

  vpc {
    vpc_id = var.dns_vpc_id
  }
}

resource "aws_route53_record" "main" {
  zone_id = aws_route53_zone.main.id
  name    = local.domain_name
  type    = "A"

  alias {
    name                   = aws_vpc_endpoint.main.dns_entry[0]["dns_name"]
    zone_id                = aws_vpc_endpoint.main.dns_entry[0]["hosted_zone_id"]
    evaluate_target_health = false
  }
}

動作確認

作成したsample-workload0-test-instanceにSSMセッションマネージャーでログインできることを確認します。

念のためログイン後にssm.ap-northeast-1.amazonaws.comを名前解決して、プライベートIPが返ってくることを確認します。

$ resolvectl query ssm.ap-northeast-1.amazonaws.com
ssm.ap-northeast-1.amazonaws.com: 10.0.0.154   -- link: ens5

-- Information acquired via protocol DNS in 13.6ms.
-- Data is authenticated: no; Data was acquired via local or encrypted transport: no
-- Data from: network

まとめ

VPCエンドポイントの集約構成をTerraformで作成してみました。名前解決の集約構成と合わせることで、VPCエンドポイント用のPrivate Hosted Zoneの設定を1箇所で済ませることができます。 VPCエンドポイントの集約を行う際は名前解決の集約もぜひ検討してみてください。

次回はインターネットへの通信の集約をしてみようと思います。