AWS Network Vol4|IPv6デュアルスタック移行×NAT64/DNS64×エッジIPv6化

目次

1. この記事について

デュアルスタックVPC全体構成図 (IPv4 CIDR + IPv6 /56 / dual-stack subnet / IPv6-only subnet / IGW+egress-only IGW)
fig01: デュアルスタックVPC全体構成図 (IPv4 CIDR + IPv6 /56 / dual-stack subnet / IPv6-only subnet / IGW+egress-only IGW)
この記事で扱う IPv6 本番移行の全体像

  • デュアルスタック VPC 設計 (Amazon 提供 IPv6 /56 の関連付け・サブネットへの /64 割当・ルートテーブル設計)
  • IPv6-only サブネット + NAT64/DNS64 で IPv4 専用サービスへ到達する経路構築
  • ELB (ALB/NLB) / CloudFront / API Gateway のエッジ IPv6 化 (dual-stack エンドポイント)
  • IPv4→IPv6 の 5 ステップ段階移行ロードマップと EKS/ECS の IPv6 対応
Network 本番運用シリーズの到達点と本 Vol の位置づけ

  • Vol2 (マルチアカウント網: TGW/PrivateLink/Direct Connect/VPC Peering/VPN) と Vol3 (Cloud WAN/VPC Lattice/IPAM advanced/VPC Endpoint) はいずれも IPv4 アドレッシングを前提とした設計だった
  • 本 Vol4 は「IPv4 → IPv6 デュアルスタックへの移行設計」という直交軸で、既存の Vol2/Vol3 設計を置き換えずに横断補完する
  • Vol3 が本編末尾で予告した「IPv6 完全対応設計 (Dual-stack VPC / IPAM IPv6 Pool / VPC Lattice IPv6)」を本編で実装する
なぜ今 IPv6 移行なのか (時事性)

  • パブリック IPv4 アドレス課金 (2024-02-01 開始・$0.005/時/IP、EC2 は月750時間まで無料枠あり) が全アカウントで継続的な移行圧力になっている
  • Amazon API Gateway の dual-stack (IPv4/IPv6) エンドポイントが 2025-03 に東京含む全商用リージョンで GA し、エッジ IPv6 化の最後のピースが揃った
  • IPv6-only サブネットへ移行できれば、IPv4 アドレスのコストと枯渇リスクを同時に解消できる

1-1. 本記事のゴール

本記事のゴールは、既存の IPv4 本番 VPC に IPv6 をデュアルスタックで安全に追加し、無停止で本番運用へ組み込める状態に到達することです。
Amazon 提供 IPv6 /56 の VPC 関連付けからサブネットへの /64 割当、IPv6-only サブネットと NAT64/DNS64 による IPv4 サービスへの到達経路の構築までを Terraform で実装します。
さらに ALB/NLB/CloudFront/API Gateway をエッジ IPv6 化 (dual-stack エンドポイント) し、EKS の VPC CNI IPv6 モードと ECS の IPv6-only タスクにも対応させます。
読了後には、IPv4 課金対象アドレスを段階的に削減しつつ IPv6 クライアントからの到達性も確保する、5 ステップの段階移行ロードマップを自チームの本番環境に適用できる状態を目指します。

本編で構築するデュアルスタック移行は、以下の 5 フェーズで段階的に進めます。各フェーズは独立して Apply 可能な単位に分割しており、途中のフェーズで一旦止めても既存の IPv4 ワークロードへの影響がない設計です。

  1. Step1: VPC への IPv6 CIDR (/56) 追加 — 既存 IPv4 VPC を止めずに IPv6 アドレス空間だけを先行確保する
  2. Step2: 既存サブネットへの dual-stack 化 (IPv6 /64 割当) — IPv4/IPv6 が併存するサブネットへ移行する
  3. Step3: ALB/NLB/CloudFront/API Gateway のエッジ dual-stack 化 — 外部クライアントからの IPv6 到達性を先に確保する
  4. Step4: IPv6-only サブネット + NAT64/DNS64 の追加 — 新規ワークロードを IPv6-only で構築できる基盤を整える
  5. Step5: IPv4 アドレスの棚卸しと削減 — 不要な Elastic IP / パブリック IPv4 を返却し課金対象を最小化する

この 5 フェーズは §6 で改めてロードマップとして図解し、§7〜§8 の詰まりポイントとアンチパターン変換演習でフェーズごとの落とし穴を補強する構成になっています。

本編の検証環境は Terraform 1.9.x / hashicorp/aws ~> 5.0 を前提とし、東京リージョン (ap-northeast-1) で全機能が GA 済みであることを §2 で個別に確認したうえで構成例を提示します。オンプレミス側のネットワーク (IPv6 未対応の場合を含む) との相互接続は本編のスコープ外とし、AWS 側のデュアルスタック化に焦点を絞ります。

本編を通読することで到達できる状態を、移行前後で整理すると以下のとおりです。

観点移行前 (IPv4-only)移行後 (本編到達点)
VPC アドレッシングIPv4 CIDR のみIPv4 CIDR + Amazon 提供 IPv6 /56 のデュアルスタック
IPv4 専用サービスへの到達制約なし (全て IPv4)IPv6-only サブネットから NAT64/DNS64 経由で到達可能
エッジ (ALB/CloudFront/API Gateway)IPv4 クライアントのみ受付dual-stack 化により IPv6 クライアントも受付可能
コンテナ基盤 (EKS/ECS)IPv4 アドレスでノード/タスク当たりの Pod 密度が制約IPv6 化によりアドレス枯渇を気にせずスケール可能
IPv4 コスト保有アドレス数に比例して課金が増加不要なパブリック IPv4 を棚卸しし段階的に削減

1-2. 読者像

想定読者は、既存の IPv4 本番 VPC を運用しており、パブリック IPv4 アドレス課金・IPv4 アドレスの枯渇・グローバル到達性要件のいずれかに直面しているネットワーク/インフラ担当者です。
IPv6 のアドレス表記やプレフィックス長といった基礎知識は理解しているものの、AWS 上でデュアルスタック VPC をどう設計し、既存の IPv4 ワークロードを止めずに移行するかの具体的な手順に不安がある層を想定しています。
Network 本番運用シリーズ Vol2/Vol3 で TGW・Cloud WAN・VPC Lattice・IPAM といった IPv4 前提の設計を構築済み、または類似のマルチアカウント VPC 網を運用中のチームであれば、本編の内容をそのまま自環境の移行計画に転用しやすくなっています。

特に、以下のいずれかに該当するチームは本編の対象読者として想定密度が高い。

想定シチュエーション直面している課題本編で扱う対応範囲
大規模 EC2/ALB 環境で Elastic IP・パブリック IPv4 を多数保有IPv4 課金 ($0.005/時/IP) が月次コストを圧迫している§6 の段階移行ロードマップと IPv4 削減ステップ
モバイル/IoT クライアントからの接続が多い SaaS 基盤キャリア網の IPv6-only 化でクライアント側 IPv6 対応が先行している§5 のエッジ IPv6 化 (ALB/CloudFront/API Gateway)
コンテナ基盤 (EKS/ECS) を本番運用中Pod/タスクあたりの IPv4 アドレス枯渇でノード当たりの Pod 密度が頭打ち§6 の EKS VPC CNI IPv6・ECS IPv6-only タスク
新規 IPv6-only サービスを社内標準にしたい既存 IPv4 専用サービス (DB・内部 API) への到達性をどう確保するか不明§4 の NAT64/DNS64 による IPv4 到達
グローバル向け Web/API サービスを提供中地域によっては IPv6-only 接続のモバイル網からのアクセス比率が高い§5 のエッジ IPv6 化と §6 の段階移行ロードマップ全体

いずれのシチュエーションでも、既存 IPv4 環境を停止せずに段階移行できることを前提に設計を進めます。移行の緊急度はチームごとに異なりますが、本編の 5 フェーズは独立して適用できるため、コスト最適化を急ぐチームは Step5 (IPv4 削減) から逆算して着手するステップを前倒しできます。

一方で、以下に該当するチームは本編の主対象からは外れます。あくまで参考情報として補足します。

  • 新規構築の VPC のみを対象とし、既存 IPv4 環境との共存を考慮しなくてよいチーム (移行ではなく新規設計のため、本編の段階移行ロードマップの多くのステップが不要になる)
  • オンプレミス側のネットワーク機器・ファイアウォールが IPv6 に未対応で、当面 AWS 側だけを IPv6 化しても事業上のメリットが得られないチーム
  • Well-Architected レベルでの IPv6 セキュリティ設計 (IPv6 特有の脅威モデリングや監査要件) を主目的とするチーム (本編は移行設計が主眼であり、セキュリティ観点は AWS Security 本番運用シリーズを参照されたい)

1-3. なぜ今これを書くか

パブリック IPv4 アドレス課金 (2024-02-01 開始・$0.005/時/IP) が恒常的なコスト圧力として定着し、多くのチームが IPv6 移行を検討課題から実行課題へ引き上げています。
これに加えて Amazon API Gateway の dual-stack エンドポイントが 2025-03 に東京含む全商用リージョンで GA したことで、VPC からエッジまでの全レイヤで IPv6 対応が出揃いました。
一方で既存の Network 本番運用シリーズ (Vol2: マルチアカウント網、Vol3: Cloud WAN/VPC Lattice/IPAM advanced) はいずれも IPv4 アドレッシングを前提としており、IPv6 移行そのものを横断的に扱うガイドが空白のままでした。
Vol3 の末尾でも本 Vol4 の内容を「IPv6 完全対応設計」として次回予告しており、本編はその正当な継続 Vol として、移行圧力が高まった今このタイミングで執筆する意義が大きいといえます。

本編執筆時点で確認できる主要な IPv6 関連マイルストーンは以下のとおりです。いずれも東京 (ap-northeast-1) を含む全商用リージョンで提供済みであることを公式情報で確認しています。

時期マイルストーン本編への影響
2024-02-01パブリック IPv4 アドレス課金開始 ($0.005/時/IP)IPv6 移行の経済的動機付けとなる直接的なコスト圧力
2025-03Amazon API Gateway dual-stack エンドポイント GAREST/HTTP/WebSocket 全タイプ + カスタムドメインで IPv6 対応が可能に
継続提供ALB/NLB/CloudFront の dual-stack 対応エッジ層の IPv6 化に技術的な障壁がない状態が既に整っている
継続提供EKS VPC CNI IPv6 モード・ECS IPv6-only タスクコンテナ基盤の IPv4 アドレス枯渇問題への直接的な解決策

これらの要素が出揃ったことで、IPv6 移行は「一部レイヤだけ対応して終わる」状態から「VPC からエッジ、コンテナ基盤まで一気通貫で移行できる」段階に達しました。本編はこの一気通貫の移行設計を、既存 IPv4 本番環境を止めずに実現する手順として提供します。

移行を先送りするコストも無視できません。パブリック IPv4 アドレス課金は保有アドレス数に比例して増加し続けるため、移行を遅らせるほど累積コストが積み上がります。
また EKS/ECS のような Pod/タスク単位で IPv4 アドレスを消費するコンテナ基盤では、ノードあたりの Pod 密度が IPv4 アドレス数によって頭打ちになりやすく、IPv6 化はコスト最適化だけでなくスケーラビリティ確保の観点でも先送りしにくい課題になっています。

本編の構成は、上記の 5 フェーズに沿って以下のように進行します。

  • §2 前提・環境・準備: IAM 権限 / Terraform 1.9.x・hashicorp/aws ~> 5.0 の初期化 / ゴール状態の定義
  • §3 デュアルスタック VPC 設計 (Step1-2): IPv6 /56 割当・サブネット設計
  • §4 IPv6-only サブネット + NAT64/DNS64 (Step4・★山場1): IPv4 サービスへの到達経路構築
  • §5 エッジの IPv6 化 (Step3・★山場2): ALB/NLB/CloudFront/API Gateway の dual-stack 化
  • §6 EKS/ECS の IPv6 と段階移行ロードマップ (Step4-5): コンテナ基盤対応と 5 ステップの全体像
  • §7 詰まりポイント7選: 移行中に遭遇しやすい落とし穴の図解
  • §8 アンチパターン変換演習 (5問) + シリーズ繋ぎ: 設計判断の根拠とVol5予告

前作: AWS Network本番運用 Vol3 (IPAM advanced / VPC Endpoint deep dive) を読む


2. 前提・環境・準備

移行前 (IPv4-only) → 移行後 (dual-stack) 環境差分図
fig02: 移行前 (IPv4-only) → 移行後 (dual-stack) 環境差分図

本章では、本 Vol4 のハンズオン全体を通して前提とする IPv4 本番 VPC の状態、必要な IAM 権限、Terraform ワークスペースの初期構成を整えます。§3 以降で実装するデュアルスタック VPC 設計・NAT64/DNS64・エッジ IPv6 化・EKS/ECS IPv6 対応は、すべて本章で整備した環境の上に積み上げます。

2-1. 前提環境

本記事は Network 本番運用シリーズ Vol2 (マルチアカウント網) / Vol3 (Cloud WAN・VPC Lattice・IPAM advanced) で構築済みの本番 VPC を前提とし、その VPC を段階的に IPv6 デュアルスタック化する手順を扱います。まだ Vol2/Vol3 相当の構成を導入していない場合は、少なくとも以下と同等の IPv4 本番 VPC を用意してください。

項目前提条件
AWS アカウント既存本番 IPv4 VPC を保有し、ec2:* / elasticloadbalancing:* 系権限を持つアカウント
VPC CIDRIPv4 /16 (例: 10.0.0.0/16)。Amazon 提供 IPv6 /56 を追加関連付けする前提
サブネット構成最低 2 AZ にパブリック/プライベートサブネットが分散配置済み
既存 NAT Gateway各 AZ のパブリックサブネットに配置済み (NAT64 は既存 NAT Gateway をそのまま流用可能)
ルーティングVol2 で構築した TGW 経由のマルチアカウント網、または単体 VPC でも可

AWS CLI / Terraform のバージョンは以下を必須とします。

ツールバージョン用途
AWS CLIv2.15 以上modify-subnet-attribute --enable-dns64 を含む IPv6 関連コマンド実行
Terraform1.9.x 以上aws_vpc_ipv6_cidr_block_association 等の IPv6 リソース管理
hashicorp/aws provider~> 5.0dual-stack / IPv6-only サブネット・egress-only IGW 対応

環境確認は以下のコマンドで行います。

# 実行アカウント・権限の確認
aws sts get-caller-identity

# 既存 IPv4 VPC の CIDR とサブネット構成を確認
aws ec2 describe-vpcs \
  --query 'Vpcs[*].[VpcId,CidrBlock,Ipv6CidrBlockAssociationSet]' \
  --output table

# AWS CLI バージョン確認 (2.15 以上を推奨)
aws --version

# Terraform / provider バージョン確認
terraform version

IAM 権限の詳細は 2-4 で提示しますが、最低限必要なアクションは ec2:AssociateVpcCidrBlock / ec2:AssociateSubnetCidrBlock / ec2:ModifySubnetAttribute など、CIDR 関連付けとサブネット属性変更を担う ec2:*Ipv6* 系アクション群です。

2-2. 使用技術スタック

本 Vol4 で扱う IPv6 移行スタックを、IaC・VPC・変換・出口・エッジの 5 レイヤに整理します。

レイヤサービス / 機能IPv6 対応方式東京提供備考
IaCTerraform 1.9.x / hashicorp/aws ~> 5.0ipv6_cidr_block / assign_ipv6_address_on_creationE基準: §2で版明記必須
VPCdual-stack VPC / IPv6-only subnetAmazon 提供 /56 (VPC) → /64 (サブネット)aws_vpc_ipv6_cidr_block_association + aws_subnet.ipv6_cidr_block
変換NAT64 (既存 NAT Gateway) + DNS64 (サブネット属性 enable_dns64)64:ff9b::/96 合成 (RFC6052)2022-11-03 GA・追加課金なし・全リージョン提供
出口egress-only Internet GatewayIPv6 outbound 専用 (SNAT 不要)aws_egress_only_internet_gateway
エッジALB / NLB / CloudFront / API Gatewayip_address_type = dualstackAPI Gateway dual-stack は 2025-03 GA

DNS64 は Route 53 Resolver 側に新しいルールを追加する機能ではなく、サブネットの属性として有効化する点に注意してください。有効化すると、そのサブネット内のリソースが利用する VPC 内蔵 Resolver (.2 アドレス) が、IPv4 のみの DNS レコードに対して 64:ff9b::/96 プレフィックスを付与した合成 AAAA レコードを返すようになります。

2-3. Terraform 初期化 — 3点セット

terraform.tfrequired_versionrequired_providers を明示し、S3 バックエンドで state を管理します。

# terraform.tf
terraform {
  required_version = "~> 1.9.0"

  required_providers {
 aws = {
source  = "hashicorp/aws"
version = "~> 5.0"
 }
  }

  backend "s3" {
 bucket= "your-org-terraform-state"
 key= "network/vol4-ipv6-migration/terraform.tfstate"
 region= "ap-northeast-1"
 dynamodb_table = "terraform-lock"
 encrypt  = true
  }
}

provider "aws" {
  region = var.aws_region

  default_tags {
 tags = {
Project= "network-production-vol4"
ManagedBy = "terraform"
 }
  }
}

variables.tf では、既存 VPC の ID とサブネット割当ビット数を変数化し、Vol2/Vol3 の Terraform ワークスペースから値を受け渡せるようにします。

# variables.tf
variable "aws_region" {
  description = "デプロイ先リージョン"
  type  = string
  default  = "ap-northeast-1"
}

variable "existing_vpc_id" {
  description = "IPv6 デュアルスタック化する既存 IPv4 VPC の ID"
  type  = string
}

variable "ipv6_subnet_newbits" {
  description = "VPC の /56 IPv6 CIDR からサブネットへ割り当てる追加ビット数 (/56 + 8 = /64)"
  type  = number
  default  = 8
}

variable "enable_ipv6_only_subnets" {
  description = "IPv6-only サブネット (§4 で NAT64/DNS64 対象) を作成するかどうか"
  type  = bool
  default  = true
}

初期化とバージョン確認は以下の通りです。

terraform init
terraform validate

terraform version
# Terraform v1.9.x
# + provider registry.terraform.io/hashicorp/aws v5.x.x

2-4. IAM 構成

IPv6 移行作業を行う運用者ロールには、CIDR 関連付け・サブネット属性変更・egress-only IGW 管理・エッジサービスの IPv6 化に必要な権限を最小権限原則で付与します。

{
  "Version": "2012-10-17",
  "Statement": [
 {
"Sid": "VpcIpv6CidrManagement",
"Effect": "Allow",
"Action": [
  "ec2:AssociateVpcCidrBlock",
  "ec2:DisassociateVpcCidrBlock",
  "ec2:AssociateSubnetCidrBlock",
  "ec2:DisassociateSubnetCidrBlock",
  "ec2:ModifySubnetAttribute",
  "ec2:ModifyVpcAttribute"
],
"Resource": "*"
 },
 {
"Sid": "EgressOnlyIgwAndRouting",
"Effect": "Allow",
"Action": [
  "ec2:CreateEgressOnlyInternetGateway",
  "ec2:DeleteEgressOnlyInternetGateway",
  "ec2:CreateRoute",
  "ec2:ReplaceRoute",
  "ec2:DeleteRoute"
],
"Resource": "*"
 },
 {
"Sid": "DescribeForValidation",
"Effect": "Allow",
"Action": [
  "ec2:DescribeVpcs",
  "ec2:DescribeSubnets",
  "ec2:DescribeEgressOnlyInternetGateways",
  "ec2:DescribeRouteTables"
],
"Resource": "*"
 },
 {
"Sid": "EdgeIpv6Enablement",
"Effect": "Allow",
"Action": [
  "elasticloadbalancing:SetIpAddressType",
  "elasticloadbalancing:ModifyLoadBalancerAttributes",
  "cloudfront:UpdateDistribution",
  "cloudfront:GetDistributionConfig",
  "apigateway:PATCH",
  "apigateway:GET"
],
"Resource": "*"
 }
  ]
}

ec2:ModifySubnetAttribute は、IPv6-only サブネットでの自動 IPv6 アドレス割当 (assign_ipv6_address_on_creation) と DNS64 有効化 (enable_dns64) の両方に使う共通アクションです。AWS CLI から個別に確認する場合は以下のように実行します。

# DNS64 有効化 (IPv6-only サブネット向け)
aws ec2 modify-subnet-attribute \
  --subnet-id subnet-xxxxxxxx \
  --enable-dns64

# サブネット作成時の IPv6 自動割当を有効化
aws ec2 modify-subnet-attribute \
  --subnet-id subnet-xxxxxxxx \
  --assign-ipv6-address-on-creation

2-5. ゴール状態の定義

本記事を読み終えた時点で、以下の状態に到達します。

対象到達状態
VPC既存 IPv4 VPC に Amazon 提供 IPv6 /56 が関連付けられ、dual-stack サブネットと IPv6-only サブネットが両立している
NAT64/DNS64IPv6-only サブネット上のワークロードが 64:ff9b::/96 経由で IPv4 専用サービスへ到達できる
エッジALB / NLB / CloudFront / API Gateway が dualstack 化され、IPv6 クライアントから直接到達できる
EKS/ECSVPC CNI の prefix delegation と ECS IPv6-only タスクにより、コンテナワークロードが IPv6 で稼働している
移行ロードマップ無停止でのデュアルスタック追加から IPv4 課金削減までの 5 ステップが自組織の標準手順として整備されている

各状態の具体的な検証コマンドは §3 以降の各セクション末尾に記載します。


3. デュアルスタック VPC 設計 — IPv6 CIDR 割当とサブネット設計

デュアルスタックVPC構成 (fig01 再掲)
fig01: デュアルスタックVPC構成 (fig01 再掲)

既存の IPv4 本番 VPC へ IPv6 を追加する最初のステップは、VPC に IPv6 CIDR ブロックを関連付けることです。本節では、Amazon 提供の IPv6 /56 を使った最小構成での dual-stack 化を軸に、移行運用の視点で設計を進めます。IPAM Pool 階層による IPv6 CIDR の一元管理や BYOIP との組み合わせは Network本番運用 Vol3 の IPAM advanced 節で扱っているため、本節ではその詳細に立ち入らず、Vol3 で確立した Pool 構成を土台に「既存 VPC へどう追加し、どうサブネットへ配分するか」に絞って解説します。

3-1. Amazon 提供 IPv6 /56 の VPC 関連付け

既存 VPC に IPv6 CIDR を追加する最も手早い方法は、aws_vpc リソースの assign_generated_ipv6_cidr_blocktrue にすることです。この設定により、AWS 側が管理する IPv6 アドレス空間から /56 の CIDR ブロックが自動的に割り当てられ、VPC に関連付けられます。

resource "aws_vpc" "prod" {
  cidr_block = "10.0.0.0/16"
  assign_generated_ipv6_cidr_block = true
  enable_dns_support= true
  enable_dns_hostnames = true

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

IPAM Pool や BYOIP で確保した IPv6 アドレス空間を使う場合は、assign_generated_ipv6_cidr_block の代わりに aws_vpc_ipv6_cidr_block_association リソースで ipv6_ipam_pool_id を指定します。既存の IPv4 dual-stack 化を最短で始めたい場合は Amazon 提供 /56 で十分ですが、複数 VPC・複数アカウントで IPv6 アドレス空間を一元管理したい場合は Vol3 の Pool 階層設計に沿って IPAM 経由で払い出す構成に切り替えることを推奨します。

resource "aws_vpc_ipv6_cidr_block_association" "from_ipam" {
  vpc_id= aws_vpc.prod.id
  ipv6_ipam_pool_id = "ipam-pool-XXXXXXXXXXXXXXXXX"
  ipv6_netmask_length  = 56
}

いずれの方式でも VPC には /56 (実効的に 65536 個の /72、あるいは 256 個の /64 を切り出せる空間) が割り当てられ、以降のサブネット設計はこの /56cidrsubnet() 関数で分割していく形になります。

3-2. dual-stack サブネット設計 — IPv4 + IPv6 の共存

移行期はまず既存 IPv4 サブネットへ IPv6 CIDR を追加する dual-stack 化から始めます。aws_subnetipv6_cidr_blockassign_ipv6_address_on_creation を追加するだけで、既存のインスタンス起動フローに手を入れずに IPv6 アドレスが自動付与されるようになります。

resource "aws_subnet" "public_a" {
  vpc_id  = aws_vpc.prod.id
  cidr_block = cidrsubnet(aws_vpc.prod.cidr_block, 8, 1)
  ipv6_cidr_block  = cidrsubnet(aws_vpc.prod.ipv6_cidr_block, 8, 1)
  assign_ipv6_address_on_creation = true
  map_public_ip_on_launch= true
  availability_zone = "ap-northeast-1a"

  tags = {
 Name = "public-dualstack-1a"
  }
}

resource "aws_subnet" "private_a" {
  vpc_id  = aws_vpc.prod.id
  cidr_block = cidrsubnet(aws_vpc.prod.cidr_block, 8, 11)
  ipv6_cidr_block  = cidrsubnet(aws_vpc.prod.ipv6_cidr_block, 8, 11)
  assign_ipv6_address_on_creation = true
  availability_zone = "ap-northeast-1a"

  tags = {
 Name = "private-dualstack-1a"
  }
}

cidrsubnet(aws_vpc.prod.ipv6_cidr_block, 8, N)/56 を 8 ビット分割し /64 を切り出す指定です。IPv6 のサブネットは仕様上 /64 固定を推奨するため (それ以外の長さでは EC2/ENI 側で IPv6 アドレスの自動割当が正しく機能しないこともあります)、AZ ごと・用途ごとに /64 を 1 つずつ割り当てる設計を基本とします。/56 からは最大 256 個の /64 を切り出せるため、AZ 数 × (public/private/ipv6-only の3種) 程度の規模であれば十分な余裕があります。

3-3. ルートテーブル設計 — IGW と egress-only IGW の役割分担

dual-stack サブネットのルートテーブルには、IPv4 用の経路に加えて IPv6 用の経路を追加します。パブリックサブネットは IPv4・IPv6 とも Internet Gateway (IGW) 経由で双方向到達可能にし、プライベートサブネットは IPv6 側を egress-only IGW 経由のアウトバウンド専用にします。

resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.prod.id
}

resource "aws_egress_only_internet_gateway" "main" {
  vpc_id = aws_vpc.prod.id
}

resource "aws_route_table" "public" {
  vpc_id = aws_vpc.prod.id
}

resource "aws_route" "public_ipv4_default" {
  route_table_id= aws_route_table.public.id
  destination_cidr_block = "0.0.0.0/0"
  gateway_id  = aws_internet_gateway.main.id
}

resource "aws_route" "public_ipv6_default" {
  route_table_id  = aws_route_table.public.id
  destination_ipv6_cidr_block = "::/0"
  gateway_id = aws_internet_gateway.main.id
}

resource "aws_route_table" "private" {
  vpc_id = aws_vpc.prod.id
}

resource "aws_route" "private_ipv4_default" {
  route_table_id= aws_route_table.private.id
  destination_cidr_block = "0.0.0.0/0"
  nat_gateway_id = aws_nat_gateway.public_a.id
}

resource "aws_route" "private_ipv6_egress_only" {
  route_table_id = aws_route_table.private.id
  destination_ipv6_cidr_block= "::/0"
  egress_only_gateway_id  = aws_egress_only_internet_gateway.main.id
}

egress-only IGW は NAT Gateway の IPv6 版に相当する存在ですが、アドレスを変換しない点が NAT Gateway と根本的に異なります。IPv6 はグローバルユニークアドレスが前提のため変換の必要がなく、egress-only IGW は「インバウンド接続の起点を外側から作らせない (アウトバウンドのみ許可する)」というステートフルフィルタの役割に専念します。既存 IPv4 の NAT Gateway 課金 (処理データ量課金) は IPv6 側の egress-only IGW には発生しないため、dual-stack 化によって IPv4 依存のワークロードを IPv6 側へ寄せるほど NAT Gateway の転送量コストを削減できる点も移行のインセンティブになります。

§3 のポイント

  • Amazon提供 /56 は assign_generated_ipv6_cidr_block で即座に付与、IPAM Pool 経由の払い出しは Vol3 の Pool 階層設計に委譲
  • IPv6 サブネットは /64 固定が基本設計単位 (cidrsubnet() で /56 を8ビット分割)
  • パブリックは IGW で双方向、プライベートは egress-only IGW でアウトバウンド専用にし、IPv4 の NAT Gateway 課金を回避

3-4. dual-stack サブネットと IPv6-only サブネットの選択基準

ここまでの dual-stack 設計は、既存 IPv4 ワークロードを止めずに IPv6 を追加する「移行の第一歩」として最も安全な選択です。一方で、新規に構築するワークロードや、IPv4 アドレス課金・IPv4 枯渇の影響を強く受けているレイヤでは、最初から IPv6-only サブネットを選ぶ方が運用コスト・アドレス管理コストの両面で有利になる場面があります。両者の判断ツリーを以下に示します。

graph TD
 A[新規/既存サブネットの設計] --> B{既存IPv4ワークロードが稼働中か}
 B -->|Yes 無停止移行が必須| C[dual-stackサブネット]
 B -->|No 新規構築| D{IPv4アドレスを持つ必要があるか}
 D -->|Yes IPv4専用SaaS/レガシー依存あり| C
 D -->|No IPv4依存なし| E{IPv4サービスへの到達が必要か}
 E -->|Yes 一部IPv4専用APIに到達| F[IPv6-onlyサブネット + NAT64/DNS64]
 E -->|No 完全にIPv6で完結| G[IPv6-onlyサブネット + egress-only IGWのみ]
 C --> H[IPv4課金は継続だが移行リスク最小]
 F --> I[IPv4課金ゼロ + IPv4到達性を維持]
 G --> J[IPv4課金ゼロ + 構成最もシンプル]

既存の本番 VPC に対する移行では、まず dual-stack 化で IPv6 経路を確立し、稼働実績を見ながら新規サブネットから IPv6-only への切り替えを進める段階移行が現実的です。次節では、この IPv6-only サブネットを支える NAT64/DNS64 の仕組みを詳しく見ていきます。


4. IPv6-only サブネット + NAT64 / DNS64 — IPv6 から IPv4 サービスへの到達 (★山場1)

NAT64/DNS64 変換フロー (DNS64 で 64:ff9b::/96 合成 → NAT Gateway で IPv4 到達)
fig03: NAT64/DNS64 変換フロー (DNS64 で 64:ff9b::/96 合成 → NAT Gateway で IPv4 到達)

IPv6-only サブネットは、IPv4 アドレスを一切持たないサブネットです。IPv4 アドレス課金や IPv4 CIDR 枯渇の問題を根本的に解消できる一方、単体では IPv4 専用サービス (社内の未移行システムや外部 SaaS の IPv4 専用エンドポイントなど) に到達できません。この課題を解決するのが NAT64 と DNS64 の組み合わせです。本節では、この2つの仕組みを個別に理解したうえで、IPv6-only ワークロードから IPv4 専用サービスへ実際に到達する経路を構築します。

4-1. IPv6-only サブネットの作成

IPv6-only サブネットは aws_subnet に IPv4 の cidr_block を指定せず、ipv6_native = true を設定することで作成します。NAT64/DNS64 を利用するには、あわせて enable_dns64 を有効化する必要があります。

resource "aws_subnet" "ipv6_only_a" {
  vpc_id  = aws_vpc.prod.id
  ipv6_cidr_block= cidrsubnet(aws_vpc.prod.ipv6_cidr_block, 8, 21)
  ipv6_native = true
  assign_ipv6_address_on_creation  = true
  enable_dns64= true
  enable_resource_name_dns_aaaa_record_on_launch = true
  availability_zone  = "ap-northeast-1a"

  tags = {
 Name = "ipv6only-1a"
  }
}

ipv6_native = true のサブネットで起動したインスタンスは IPv4 アドレス (プライベート IPv4 すら) を持ちません。ENI・セキュリティグループ・NACL の設計も IPv6 アドレス表記のみで完結させる必要があるため、既存の IPv4 前提で書かれた SG ルールをそのまま流用できない点に注意してください。

4-2. DNS64 — Route 53 Resolver による AAAA レコード合成

DNS64 は、問い合わせ先のホスト名が AAAA レコード (IPv6) を持たない場合に、VPC 内の Amazon 提供 DNS リゾルバが A レコード (IPv4) を基に「合成 AAAA レコード」を動的に生成する仕組みです。前節の enable_dns64 = true によってサブネット単位で有効化され、追加の Route 53 Resolver ルールを作成する必要はありません。

合成される IPv6 アドレスは、RFC 6052 で定義された Well-Known Prefix 64:ff9b::/96 に、変換対象の IPv4 アドレスを埋め込んだ形式です。たとえば IPv4 アドレス 203.0.113.10 に対する合成アドレスは 64:ff9b::203.0.113.10 (64:ff9b::cb00:710a の混在表記) になります。IPv6-only サブネット上のクライアントが AAAA レコードを問い合わせた際、実際には AAAA レコードが存在しない IPv4 専用ホストであっても、この合成プレフィックスを含む応答が返るため、クライアント側は通常の IPv6 通信と同じ流儀で接続を試みることができます。

# IPv6-only サブネット上のインスタンスから確認
dig AAAA ipv4-only-service.example.internal +short
# => 64:ff9b::cb00:710a  (IPv4アドレス 203.0.113.10 が埋め込まれた合成AAAA)

DNS64 はあくまで「名前解決の応答をすり替える」役割に限定され、実際のパケット変換は行いません。この合成アドレス宛てのパケットを実際に IPv4 パケットへ変換して転送するのが、次項の NAT64 の役割です。

4-3. NAT64 — NAT Gateway によるプロトコル変換

NAT64 は、宛先が 64:ff9b::/96 の合成 IPv6 パケットを実際の IPv4 パケットへ変換して転送する仕組みです。AWS では NAT Gateway がこの変換機能を兼ねます。既存の IPv4 NAT Gateway (dual-stack サブネットに配置済み、§3 参照) に対して、IPv6-only サブネットのルートテーブルから 64:ff9b::/96 宛の経路を追加するだけで NAT64 が有効になります。

resource "aws_route_table" "ipv6_only_a" {
  vpc_id = aws_vpc.prod.id
}

resource "aws_route" "ipv6_only_nat64" {
  route_table_id= aws_route_table.ipv6_only_a.id
  destination_ipv6_cidr_block  = "64:ff9b::/96"
  nat_gateway_id = aws_nat_gateway.public_a.id
}

resource "aws_route" "ipv6_only_egress" {
  route_table_id= aws_route_table.ipv6_only_a.id
  destination_ipv6_cidr_block = "::/0"
  egress_only_gateway_id= aws_egress_only_internet_gateway.main.id
}

resource "aws_route_table_association" "ipv6_only_a" {
  subnet_id= aws_subnet.ipv6_only_a.id
  route_table_id = aws_route_table.ipv6_only_a.id
}

ここで重要なのは、ルートテーブルに 64:ff9b::/96 (NAT64 経路) と ::/0 (egress-only IGW 経路) を 両方とも登録する 点です。宛先アドレスが 64:ff9b::/96 の範囲に一致するパケットだけが NAT Gateway 経由の NAT64 変換対象になり、それ以外の IPv6 宛先 (ネイティブ IPv6 対応サービス) は従来どおり egress-only IGW 経由でそのまま IPv6 のまま転送されます。ルーティングテーブルの最長プレフィックスマッチにより、より限定的な 64:ff9b::/96::/0 より優先されるため、両方の経路が共存しても矛盾は生じません。

NAT Gateway 自体は dual-stack (または IPv4) サブネットに配置されている既存のものをそのまま再利用できます。NAT64 専用の NAT Gateway を新設する必要はなく、IPv4 アドレス変換用の NAT Gateway が IPv6 側からの NAT64 リクエストも合わせて処理します。

4-4. egress-only IGW と NAT Gateway の役割分担

IPv6-only サブネットのアウトバウンド経路は、宛先によって次のように使い分けられます。

宛先経由するゲートウェイ変換の有無
IPv6 ネイティブ対応サービス (::/0 の通常経路)egress-only IGW変換なし (IPv6 のまま到達)
IPv4 専用サービス (DNS64 合成 64:ff9b::/96)NAT Gateway (NAT64)IPv6→IPv4 変換

egress-only IGW は「アウトバウンドのみ許可するステートフルなゲートウェイ」であり、パケットの中身を書き換えません。一方 NAT Gateway は 64:ff9b::/96 宛のパケットを受け取ると、埋め込まれた IPv4 アドレスを取り出して実際の IPv4 パケットとして送信し、戻りの応答パケットを再び合成 IPv6 アドレスへ変換してクライアントへ返します。この2系統を1つのルートテーブル内で共存させることが、IPv6-only サブネットを実用的にする設計の要点です。

4-5. 実証 — IPv6-only ワークロードから IPv4 専用サービスへの到達

DNS64・NAT64・ルーティングが揃った状態で、IPv6-only サブネット上のインスタンスから IPv4 専用サービスへ実際に到達できることを確認します。

# IPv6-only サブネット上の EC2 インスタンスにて実行
curl -6 -v https://ipv4-only-api.example.com/health
# DNS64合成 → NAT64変換 を経由してIPv4サービスへ到達し、200 OKが返る

疎通に失敗する場合の典型的な確認ポイントは次の3つです。①サブネットの enable_dns64 が有効になっているか、②ルートテーブルに 64:ff9b::/96 → NAT Gateway の経路が存在するか、③セキュリティグループ・NACL の Outbound ルールが ::/0 (または少なくとも 64:ff9b::/96) の IPv6 宛通信を許可しているかです。SG は合成前の元の宛先ではなく実際に送出される IPv6 パケットの宛先 (64:ff9b::/96 配下のアドレス) に対して評価されるため、IPv4 時代の SG ルールをそのまま持ち込んでも機能しません。

NAT64/DNS64 本番運用 3鉄則

  1. DNS64 とルーティングは必ずセットで有効化せよ
    enable_dns64 = true だけでは合成 AAAA レコードが払い出されるのみで、疎通は成立しない。64:ff9b::/96 → NAT Gateway のルートが同時に存在して初めて経路が完成する。
  2. NAT64 用の NAT Gateway は IPv4 到達性を持つサブネットに置け
    NAT Gateway が変換後の IPv4 パケットを実際に送出できるよう、dual-stack (または IPv4) サブネットに配置し、IGW への経路を確保する。IPv6-only サブネットに NAT Gateway 自体を置くことはできない。
  3. セキュリティグループは合成後の IPv6 宛先で評価される点を前提に設計せよ
    IPv4 時代の Outbound ルールをそのまま移植せず、64:ff9b::/96 を含む IPv6 宛先への Outbound を明示的に許可する。

IPv6-only サブネットと NAT64/DNS64 の組み合わせにより、IPv4 アドレス課金・枯渇を回避しながら、既存の IPv4 専用サービスへの依存を残したまま段階的に移行を進められます。次節では、この足回りの上でクライアントとの接点となるエッジ層 (ELB / CloudFront / API Gateway) の IPv6 化を扱います。


5. エッジの IPv6 化 — ELB / CloudFront / API Gateway (★山場2)

エッジIPv6化構成 (CloudFront/ALB/NLB/API Gateway dualstack)
fig04: エッジIPv6化構成 (CloudFront/ALB/NLB/API Gateway dualstack)

§4 では IPv6-only サブネットから IPv4 サービスへの到達経路を確立しました。本節では視点を反転し、
インターネット側の IPv6 クライアントが AWS 上のワークロードへ到達するための「エッジの IPv6 化」を扱います。
ALB / NLB / CloudFront / API Gateway という4つのエッジコンポーネントは、それぞれ実装は異なるものの
「IPv4 と IPv6 の両方を同時に受け付ける」という dual-stack の考え方は共通しています。
2025-03 に API Gateway が dual-stack エンドポイントへ対応したことで、この4コンポーネント全てが
dual-stack 化可能になり、本番環境のエッジを IPv4 一本足から解放できる状態が揃いました。

5-1. エッジ dual-stack 化の全体像 — 4コンポーネント共通の設計原則

エッジの IPv6 化を進めるうえで押さえておきたい原則は次の3点です。

1つ目は、dual-stack 化はエンドポイントの属性変更であり、バックエンドの構成には影響しないという点です。
ALB のターゲットグループ、CloudFront のオリジン、API Gateway の統合 (Lambda / HTTP プロキシ) は
IPv4 のままで構いません。IPv6 対応が必要なのは「クライアントとエッジの間」の区間だけであり、
エッジからバックエンドへの通信は引き続き VPC 内部の IPv4 ネットワークを利用します。
これにより、バックエンドの改修なしにエッジだけを段階的に IPv6 化できます。

2つ目は、IPv6 CIDR がサブネットに割り当たっていることが ALB/NLB dual-stack 化の前提条件という点です。
§3 で設計したデュアルスタックサブネットに ALB/NLB を配置していない場合、
ip_address_typedualstack に変更しようとしてもエラーになります。
CloudFront と API Gateway はリージョンサービスの外側にあるグローバルエッジのため、この制約は受けません。

3つ目は、dual-stack 化と同時に AAAA レコードを Route 53 に追加しないと、IPv6 クライアントは
そもそもエッジへ到達できない
という点です。エッジ側を dual-stack にしても、DNS が A レコード
(IPv4) しか返さなければクライアントは IPv6 での接続を試みません。エイリアスレコードで
A と AAAA の両方を作成することが dual-stack 化の実質的な完了条件になります。

5-2. ALB / NLB の dual-stack 化 — ip_address_type = dualstack

ALB / NLB はいずれも aws_lb リソースの ip_address_type 属性で dual-stack を制御します。
値は ipv4 (デフォルト) と dualstack の2種類で、internet-facing / internal のどちらの
ロードバランサーでも設定可能です。internal な dual-stack ALB を作る場合は、配置先の全サブネットに
IPv6 CIDR が割り当たっている必要があります。

# ALB dual-stack化 — Terraform (hashicorp/aws ~> 5.0)
resource "aws_lb" "app" {
  name= "${var.env}-app-alb"
  internal  = false
  load_balancer_type = "application"
  ip_address_type = "dualstack"

  subnets = [
 aws_subnet.dualstack_public_a.id,
 aws_subnet.dualstack_public_c.id,
 aws_subnet.dualstack_public_d.id,
  ]

  security_groups = [aws_security_group.alb.id]

  tags = { Name = "${var.env}-app-alb" }
}

# NLB dual-stack化 — Terraform
resource "aws_lb" "nlb" {
  name= "${var.env}-app-nlb"
  internal  = false
  load_balancer_type = "network"
  ip_address_type = "dualstack"

  subnets = [
 aws_subnet.dualstack_public_a.id,
 aws_subnet.dualstack_public_c.id,
 aws_subnet.dualstack_public_d.id,
  ]

  tags = { Name = "${var.env}-app-nlb" }
}

# Route 53 — A/AAAA 両方のエイリアスレコードを作成
resource "aws_route53_record" "alb_ipv4" {
  zone_id = var.route53_zone_id
  name = "app.example.com"
  type = "A"

  alias {
 name = aws_lb.app.dns_name
 zone_id = aws_lb.app.zone_id
 evaluate_target_health = true
  }
}

resource "aws_route53_record" "alb_ipv6" {
  zone_id = var.route53_zone_id
  name = "app.example.com"
  type = "AAAA"

  alias {
 name = aws_lb.app.dns_name
 zone_id = aws_lb.app.zone_id
 evaluate_target_health = true
  }
}

ターゲットグループとヘルスチェックは ip_address_type の変更による影響を受けません。
ALB/NLB は IPv6 クライアントからのリクエストを受け付けた後、内部的には IPv4 のターゲットグループへ
転送するため、バックエンドの EC2 / ECS / Lambda は IPv4 のままで問題ありません。
既存の IPv4-only ALB を dual-stack に変更する場合も、ip_address_type の更新は無停止で反映されます。

5-3. CloudFront の IPv6 有効化 — is_ipv6_enabled

CloudFront は aws_cloudfront_distribution リソースの is_ipv6_enabled 属性で IPv6 対応を切り替えます。
ALB/NLB とは異なり CloudFront はグローバルサービスのため、オリジンに紐づく VPC の IPv6 CIDR 有無に
関係なく有効化できます。

# CloudFront dual-stack化 — Terraform
resource "aws_cloudfront_distribution" "cdn" {
  is_ipv6_enabled = true
  enabled= true
  comment= "${var.env} edge distribution (dual-stack)"

  origin {
 domain_name = aws_lb.app.dns_name
 origin_id= "alb-origin"

 custom_origin_config {
http_port  = 80
https_port  = 443
origin_protocol_policy  = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
 }
  }

  default_cache_behavior {
 target_origin_id = "alb-origin"
 viewer_protocol_policy = "redirect-to-https"
 allowed_methods= ["GET", "HEAD", "OPTIONS", "PUT", "POST", "PATCH", "DELETE"]
 cached_methods  = ["GET", "HEAD"]

 forwarded_values {
query_string = true
cookies {
  forward = "all"
}
 }
  }

  restrictions {
 geo_restriction {
restriction_type = "none"
 }
  }

  viewer_certificate {
 acm_certificate_arn= var.acm_certificate_arn
 ssl_support_method = "sni-only"
 minimum_protocol_version = "TLSv1.2_2021"
  }
}

is_ipv6_enabled が有効な CloudFront ディストリビューションは、AWS が自動的に AAAA レコード相当の
IPv6 エンドポイントを払い出します。Route 53 でカスタムドメインをエイリアス設定する場合は、
ALB と同様に A・AAAA 両方のエイリアスレコードを作成することで、IPv6 クライアントが
CloudFront のドメイン名を直接引かなくても到達できるようになります。

5-4. API Gateway dual-stack エンドポイント (2025-03 GA) — REST / HTTP / WebSocket

Amazon API Gateway の dual-stack エンドポイントは 2025-03 に GA した機能で、REST API
(EDGE / REGIONAL / PRIVATE の全エンドポイントタイプ)・HTTP API・WebSocket API、およびカスタムドメインが
対象です。AWS 公式の発表では、追加料金なしで全ての商用リージョンと GovCloud (US) リージョンに提供され、
既存 API の設定変更として即時反映される (再デプロイ不要) 点が明記されています。東京リージョン
(ap-northeast-1) を含む商用リージョン全域が対象のため、Vol4 執筆時点で本番導入に支障はありません。

REST API (API Gateway v1) では、ip_address_typeendpoint_configuration ブロックの中に
ネストする属性として提供されます。

# REST API (v1) — REGIONAL dual-stack化
resource "aws_api_gateway_rest_api" "rest" {
  name = "${var.env}-rest-api"

  endpoint_configuration {
 types= ["REGIONAL"]
 ip_address_type  = "dualstack"
  }
}

# REST API カスタムドメイン — dual-stack化
resource "aws_api_gateway_domain_name" "rest" {
  domain_name  = "api.example.com"
  regional_certificate_arn = var.acm_certificate_arn

  endpoint_configuration {
 types  = ["REGIONAL"]
  }

  domain_name_configuration {
 ip_address_type = "dualstack"
  }
}

HTTP API / WebSocket API (API Gateway v2 = apigatewayv2) では、ip_address_type
endpoint_configuration を介さずリソース直下のトップレベル属性として指定します。デフォルト値は ipv4 です。

# HTTP API (v2) — dual-stack化
resource "aws_apigatewayv2_api" "http" {
  name = "${var.env}-http-api"
  protocol_type = "HTTP"
  ip_address_type = "dualstack"
}

# WebSocket API (v2) — dual-stack化
resource "aws_apigatewayv2_api" "ws" {
  name  = "${var.env}-ws-api"
  protocol_type  = "WEBSOCKET"
  route_selection_expression = "$request.body.action"
  ip_address_type= "dualstack"
}

エンドポイントタイプごとの制約として、次の3点を必ず確認してください。

  • REGIONAL / EDGE の REST API はデフォルトが ipv4 のため、既存 API を IPv6 対応させるには
    明示的に dualstack へ変更する操作が必要です。
  • PRIVATE エンドポイント (VPC Endpoint 経由でのみアクセス可能な REST API) は dualstack 以外の
    値を選べません。ipv4 を指定するとエラーになります。
  • エンドポイントタイプを PRIVATE から REGIONAL へ移行する場合、移行完了直後は ip_address_type
    自動的に dualstack へ引き継がれます。ipv4 に戻すのは移行完了後の別ステップとして扱う必要があり、
    移行と同時に ipv4 を指定する構成は成立しません。

5-5. IPv6 クライアントからの到達実証 — dig AAAA / curl -6

エッジの dual-stack 化が正しく反映されているかは、DNS 解決とアプリケーション層の疎通の両方で確認します。

# 1. AAAA レコードが払い出されているか確認
dig AAAA app.example.com +short
dig AAAA api.example.com +short

# 2. IPv6経由でALB (CloudFront/API Gateway) へ到達できるか確認
curl -6 -v https://app.example.com/healthz
curl -6 -v https://api.example.com/v1/ping

# 3. API GatewayのIP Address Typeを直接確認
aws apigateway get-rest-api --rest-api-id "$REST_API_ID" \
  --query 'endpointConfiguration'
aws apigatewayv2 get-api --api-id "$HTTP_API_ID" \
  --query 'ipAddressType'

dig AAAA が空を返す場合、原因の大半はエッジ側の dual-stack 化ではなく Route 53 の
AAAA レコード未作成です。curl -6 がタイムアウトする場合は、AAAA レコードは存在していても
WAF や セキュリティグループが IPv6 CIDR からの通信を許可していない可能性を疑ってください
(詳細は次項)。IPv6 未対応のネットワーク環境から検証する場合は、curl -6 がそもそも
IPv6 アドレスに解決できず失敗するため、検証環境自体が IPv6 到達性を持っているかを事前に確認してください。

5-6. ポリシーの落とし穴 — WAF / SG の IPv6 CIDR 対応

エッジを dual-stack 化しても、その手前にある WAF や セキュリティグループが IPv4 CIDR しか
許可していなければ、IPv6 クライアントからの通信は到達前に破棄されます。dual-stack 化の作業で
最も見落とされやすいのがこのポリシー層です。

AWS WAF の IPSet はバージョンごとに別リソースとして作成する必要があります。1つの aws_wafv2_ip_set
に IPv4 と IPv6 の CIDR を混在させることはできず、ip_address_versionIPV4 / IPV6
明示的に分ける必要があります。

# WAF IPSet — IPv4/IPv6を別リソースとして作成
resource "aws_wafv2_ip_set" "allow_ipv4" {
  name= "${var.env}-allow-ipv4"
  scope  = "REGIONAL"
  ip_address_version = "IPV4"
  addresses = var.allowed_ipv4_cidrs
}

resource "aws_wafv2_ip_set" "allow_ipv6" {
  name= "${var.env}-allow-ipv6"
  scope  = "REGIONAL"
  ip_address_version = "IPV6"
  addresses = var.allowed_ipv6_cidrs
}

# WebACL — 両IPSetをOR条件で結合しないと片方の疎通が失われる
resource "aws_wafv2_web_acl" "edge" {
  name  = "${var.env}-edge-acl"
  scope = "REGIONAL"

  default_action {
 block {}
  }

  rule {
 name  = "allow-known-cidrs"
 priority = 1

 action {
allow {}
 }

 statement {
or_statement {
  statement {
 ip_set_reference_statement {
arn = aws_wafv2_ip_set.allow_ipv4.arn
 }
  }
  statement {
 ip_set_reference_statement {
arn = aws_wafv2_ip_set.allow_ipv6.arn
 }
  }
}
 }

 visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "allow-known-cidrs"
sampled_requests_enabled= true
 }
  }

  visibility_config {
 cloudwatch_metrics_enabled = true
 metric_name = "${var.env}-edge-acl"
 sampled_requests_enabled= true
  }
}

セキュリティグループ側は ipv6_cidr_blocks 属性を cidr_blocks と並列で指定することで、
同一ルール内に IPv4 と IPv6 の許可範囲を両方持たせられます。

# ALB用セキュリティグループ — IPv4/IPv6両CIDRを許可
resource "aws_security_group" "alb" {
  name= "${var.env}-alb-sg"
  vpc_id = aws_vpc.main.id

  ingress {
 from_port  = 443
 to_port = 443
 protocol= "tcp"
 cidr_blocks= ["0.0.0.0/0"]
 ipv6_cidr_blocks = ["::/0"]
  }

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

API Gateway のリソースポリシーで送信元 IP を制限している場合も同様の注意が必要です。
aws:SourceIp 条件で IPv4 CIDR のみを列挙したリソースポリシーは、ip_address_type
dualstack に変更しても IPv6 クライアントを暗黙に拒否し続けます。IPv6 のみを許可する、
あるいは IPv4/IPv6 を両方許可するリソースポリシーへ明示的に更新することが、
dual-stack 化を完了させるための必須作業になります。

エッジ IPv6 化 3鉄則

鉄則1 — dual-stack化とAAAAレコード追加は必ずセットで実施せよ

エッジの ip_address_type / is_ipv6_enabled を変更しただけでは、DNS が A レコードしか
返さないため IPv6 クライアントは接続を試みません。Route 53 で AAAA エイリアスレコードを
同時に作成して初めて dual-stack 化が完了します。

鉄則2 — WAF IPSetはIPv4/IPv6を別リソースで作成しOR条件で結合せよ

1つの IPSet に IPv4/IPv6 を混在させることはできません。片方のバージョンの IPSet だけを
WebACL に紐付けると、もう片方のバージョンのクライアントが暗黙にブロックされます。

鉄則3 — PRIVATE エンドポイントはdualstack固定という制約を移行計画に織り込め

PRIVATE な REST API は ip_address_typeipv4 に設定できません。
REGIONAL からの移行、あるいは新規構築時点でこの制約を前提に設計すること。

エッジdual-stack化の落とし穴 — ポリシー層の見落とし

ALB/NLB/CloudFront/API Gateway を dual-stack にしても、手前の WAF や セキュリティグループ、
API Gateway リソースポリシーが IPv4 CIDR のみを許可していると、IPv6 クライアントの通信は
エッジ到達前に破棄されます。dig AAAA で DNS 解決ができているのに curl -6 がタイムアウトする場合、
まずこのポリシー層の IPv6 CIDR 対応漏れを疑ってください。

もう一つの落とし穴は、既存の IPv4-only ALB を dual-stack に変更する際、配置先サブネットに
IPv6 CIDR が割り当たっていないとエラーになる点です。§3 のデュアルスタックサブネット設計が
未完了のまま §5 の設定変更を先行させると、この依存関係でエラーが発生します。

AWS公式ドキュメント — API Gateway IP Address Type (dual-stack) リファレンス


6. EKS / ECS の IPv6 と段階移行ロードマップ

EKS/ECS IPv6 — VPC CNI prefix delegation とタスクENI IPv6割当
fig05: EKS/ECS IPv6 — VPC CNI prefix delegation とタスクENI IPv6割当
IPv4→IPv6 段階移行ロードマップ (5ステップ)
fig06: IPv4→IPv6 段階移行ロードマップ (5ステップ)

コンテナワークロードの IPv6 対応は VM ベースの移行と勘案すべき点が異なります。EKS はクラスター全体で IPv4 か IPv6 のどちらか一方を選択する仕様であり、ECS Fargate はタスク単位で IPv6-only ネットワーキングを選択できます。本節では EKS VPC CNI と ECS Fargate それぞれの IPv6 設定を提示したうえで、SG/NACL の IPv6 ルール設計、IPv4→IPv6 の段階移行ロードマップ、IPv6 BYOIP の運用深掘りをまとめます。

6-1. EKS VPC CNI の IPv6 対応 — クラスター作成時の ip_family 指定と Prefix Delegation

Amazon EKS は Kubernetes とは異なり、Pod・Service へのデュアルスタックアドレス割当をサポートしていません。クラスター作成時に IPv4IPv6 のいずれかの IP ファミリーを選択する必要があり、作成後の変更はできません。既存の IPv4 クラスターを IPv6 に切り替えたい場合は新規クラスターの作成が必須です。

IPv6 クラスターでは Pod に IPv6 アドレスが割当され、Pod 外部 (別 VPC やインターネット) の IPv4 リソースと通信する際はノード自身が host-local CNI プラグインを介して SNAT を行います。この仕組みにより、Pod の IPv4 通信については §4 で扱った DNS64/NAT64 を個別に用意する必要がありません。IPv6-only サブネット全体を対象とする DNS64/NAT64 と、EKS ノードが Pod 単位で行う SNAT は目的も適用層も異なる点に注意してください。

IPv6 クラスターの前提条件は以下のとおりです。

  • ワーカーノードは Nitro ベースの EC2 インスタンスまたは Fargate に限定される (Prefix Delegation が Nitro 世代でのみ動作するため)
  • VPC・サブネットの両方に IPv4 CIDR と IPv6 CIDR が割当済みであること (IPv6-only クラスターでも VPC には IPv4 CIDR が必須)
  • ノード用サブネットで IPv6 アドレスの自動割当が有効化されていること
  • Amazon VPC CNI アドオンは v1.10.1 以降を使用すること (IPv6 クラスター作成時は Prefix Delegation の設定を含めて既定でデプロイされる)
resource "aws_eks_cluster" "main" {
  name  = "prod-ipv6-cluster"
  role_arn = aws_iam_role.eks_cluster.arn
  version  = "1.31"

  vpc_config {
 subnet_ids  = concat(var.dualstack_subnet_ids, var.ipv6only_subnet_ids)
 endpoint_private_access  = true
 endpoint_public_access= false
  }

  kubernetes_network_config {
 ip_family = "ipv6"
  }
}

resource "aws_eks_addon" "vpc_cni" {
  cluster_name  = aws_eks_cluster.main.name
  addon_name = "vpc-cni"
  addon_version = "v1.19.0-eksbuild.1"
  # IPv6 クラスターでは EKS が Prefix Delegation を既定で有効化するが、
  # 明示的に configuration_values で固定しておくと addon 更新時の設定ドリフトを防げる
  configuration_values = jsonencode({
 env = {
ENABLE_PREFIX_DELEGATION = "true"
ENABLE_IPv6  = "true"
 }
  })
  before_compute = true
}

Prefix Delegation が有効な IPv6 クラスターでは、ENI のスロットごとに /80 の IPv6 プレフィックスが割当されます。1 プレフィックスあたりのアドレス数は事実上無制限に近いため、IPv4 プレフィックスモードで問題になりがちな「ウォームプレフィックスの枯渇」自体は起きません。ただし ENI 数・スロット数の上限はインスタンスタイプ依存のまま残る点は 7-7 で扱います。

CNI IAM ロールには IPv4 用ポリシーとは別に IPv6 専用ポリシーをアタッチする必要があります。IPv4 用ポリシーには存在しない ec2:AssignIpv6Addresses 等のアクションが含まれるため、既存の IPv4 クラスター運用からの流用ではなく IPv6 専用ポリシーを新規に作成してください。

6-2. ECS Fargate IPv6-only タスク — dualStackIPv6 アカウント設定と awsvpc ネットワーキング

ECS Fargate タスクに IPv6 アドレスを割当てるには、以下 4 条件をすべて満たす必要があります。

  1. タスクのネットワークモードが awsvpc であること
  2. VPC・サブネットが IPv6 対応済みで、サブネットの IPv6 自動割当が有効であること
  3. Fargate プラットフォームバージョンが Linux で 1.4.0 以降であること
  4. IAM プリンシパル (タスク実行に使う Role/User) に対して ECS アカウント設定 dualStackIPv6enabled になっていること

4 番目の dualStackIPv6 アカウント設定は見落としやすいポイントです。他の ECS アカウント設定と異なり、Terraform の hashicorp/aws プロバイダーでは本設定への対応が長らく issue 化されたままで、バージョンによってはリソース経由で確実に設定できません。CI/CD パイプラインでは AWS CLI を直接呼び出すか、null_resource + local-exec で idempotent に適用する方法が確実です。

# IAM プリンシパル単位で dualStackIPv6 を有効化 (Terraform リソース非対応環境向け)
aws ecs put-account-setting-default \
  --name dualStackIPv6 \
  --value enabled
resource "aws_ecs_task_definition" "ipv6_only" {
  family = "ipv6-only-service"
  requires_compatibilities = ["FARGATE"]
  network_mode  = "awsvpc"
  cpu= 512
  memory= 1024
  execution_role_arn= aws_iam_role.ecs_execution.arn
  container_definitions= jsonencode([
 {
name  = "app"
image = "${var.ecr_repository_url}:latest"
portMappings = [{ containerPort = 8080, protocol = "tcp" }]
 }
  ])
}

resource "aws_ecs_service" "ipv6_only" {
  name= "ipv6-only-service"
  cluster= aws_ecs_cluster.main.id
  task_definition = aws_ecs_task_definition.ipv6_only.arn
  desired_count= 2
  launch_type  = "FARGATE"

  network_configuration {
 subnets = var.ipv6only_subnet_ids
 security_groups  = [aws_security_group.ecs_tasks.id]
 assign_public_ip = false
  }
}

IPv6-only サブネットに配置したタスクが Public IPv4 経由のサービス (レガシー SaaS API 等) へ到達する場合は、EKS のような Pod 単位 SNAT は行われないため、§4 で構築した DNS64/NAT64 経路 (64:ff9b::/96 → NAT Gateway) をそのまま利用します。

6-3. SG / NACL の IPv6 ルール設計

セキュリティグループのデフォルトルール (同一 SG からの ingress 許可・全 egress 許可) は IPv4/IPv6 の双方に暗黙適用されるわけではありません。カスタムで追加するルールは IPv4 用 (cidr_blocks / ipv4_cidr_blocks) と IPv6 用 (ipv6_cidr_blocks / cidr_ipv6) を明示的に分けて定義する必要があります。

resource "aws_vpc_security_group_ingress_rule" "https_ipv6" {
  security_group_id = aws_security_group.alb.id
  cidr_ipv6 = "::/0"
  from_port = 443
  to_port = 443
  ip_protocol= "tcp"
}

resource "aws_vpc_security_group_egress_rule" "app_egress_ipv6" {
  security_group_id = aws_security_group.ecs_tasks.id
  cidr_ipv6 = "::/0"
  ip_protocol= "-1"
}

ネットワーク ACL はステートレスであるため、SG 以上に IPv6 の考慮漏れが起きやすい箇所です。IPv4 用ルールをコピーして IPv6 用ルールを追加する際は、番号体系を IPv4/IPv6 で分離したうえで ingress・egress を対称に定義してください。デフォルト NACL は IPv4/IPv6 の両方を全許可しますが、カスタム NACL を新規作成した場合は IPv6 の ingress/egress ルール追加を忘れると通信がサイレントに破棄されます。

resource "aws_network_acl_rule" "allow_ipv6_https_in" {
  network_acl_id  = aws_network_acl.public.id
  rule_number= 210
  egress  = false
  protocol= "tcp"
  rule_action= "allow"
  ipv6_cidr_block  = "::/0"
  from_port  = 443
  to_port  = 443
}

resource "aws_network_acl_rule" "allow_ipv6_https_out" {
  network_acl_id  = aws_network_acl.public.id
  rule_number= 210
  egress  = true
  protocol= "tcp"
  rule_action= "allow"
  ipv6_cidr_block  = "::/0"
  from_port  = 1024
  to_port  = 65535
}

6-4. IPv4 → IPv6 段階移行ロードマップ — 無停止 5 ステップ

本番 VPC を無停止のまま IPv6 化するには、既存 IPv4 経路を維持したまま IPv6 経路を積み増し、最終段階で初めて IPv4 依存を削減する順序が安全です。

Step内容既存 IPv4 通信への影響
Step1VPC に Amazon 提供 IPv6 /56 を関連付け、既存サブネットに /64 を割当なし (追加のみ)
Step2既存サブネットを dual-stack 化 (assign_ipv6_address_on_creation 有効化・ルートテーブルへ IGW/egress-only IGW 追加)なし (IPv4 ルートは維持)
Step3ALB/NLB/CloudFront/API Gateway を ip_address_type = dualstack に変更し、AAAA レコードを追加なし (IPv4 リスナーは並存)
Step4新規ワークロード向けに IPv6-only サブネット + DNS64/NAT64 を追加し、EKS/ECS の IPv6 対応を投入なし (既存 IPv4-only ワークロードは無変更)
Step5IPv4 依存の低いワークロードから Elastic IP 解放・NAT Gateway の IPv4 トラフィック削減を実施し、Public IPv4 課金 ($0.005/時/IP) を削減対象ワークロードのみ影響 (事前検証必須)

Step1〜Step4 は既存 IPv4 経路を変更しない「追加のみ」の変更であるため、通常のメンテナンスウィンドウを確保せずに適用できます。Step5 のみ既存リソース (Elastic IP・NAT Gateway) の削減を伴うため、対象ワークロードの IPv6 到達性をモニタリングにより確認したうえで段階的に実施してください。

sequenceDiagram
 participant NW as ネットワーク管理者
 participant VPC as VPC/サブネット
 participant Edge as ALB/CloudFront/API GW
 participant Mon as CloudWatch/VPC Flow Logs
 participant IPv4 as 既存IPv4リソース

 NW->>VPC: Step1 IPv6 /56 CIDR関連付け (IPv4通信は無停止)
 VPC->>VPC: Step2 サブネットdual-stack化 + ルート追加
 NW->>Edge: Step3 ip_address_type=dualstack + AAAA追加
 Edge-->>IPv4: IPv4リスナーは並存継続
 NW->>Mon: IPv6経路のトラフィック検証 (切替前)
 Mon-->>NW: IPv6到達性OK確認
 NW->>VPC: Step4 IPv6-onlyサブネット追加 + DNS64/NAT64
 VPC->>VPC: EKS/ECS新規ワークロードをIPv6-onlyへ配置
 NW->>IPv4: Step5 Elastic IP解放 + NAT Gateway IPv4削減
 IPv4-->>NW: IPv4課金削減を確認
段階移行ロードマップのポイント

  • Step1〜4 は既存 IPv4 経路への追加のみであり、通常のデプロイフローで適用できる
  • Step5 (IPv4 削減) だけは影響範囲を伴うため、対象ワークロードの IPv6 到達率を Flow Logs で確認してから実施する
  • EKS は Step4 でクラスター単位の判断が必要 (IPv4/IPv6 混在不可のため新規クラスターとして追加し、既存クラスターと並存させる設計が現実的)

6-5. IPv6 BYOIP — Vol3 IPAM 節を運用視点で深掘り

BYOIP のプロビジョニング手順そのもの (ROA 作成・X.509 証明書提出・provision-byoip-cidr) は Vol3 の IPAM advanced 節 で扱っています。本節では IPv6 BYOIP を移行運用の観点から深掘りします。

IPv6 BYOIP は /48 以上のアドレスブロックを対象とし、VPC へ直接割当ててオンプレミス由来の IPv6 GUA (Global Unicast Address) を AWS 環境でそのまま継続利用する用途に適しています。オンプレミスのファイアウォール許可リストや取引先とのピアリング設定に既存 IPv6 レンジが組み込まれている場合、BYOIP を使えば移行後もアドレス変更なしで既存の許可リストを維持できます。

段階移行ロードマップ (6-4) の Step5 でオンプレミス側の IPv4 サービスを廃止する際、対向のファイアウォールルールが IPv4 CIDR ベースで組まれていると通信断が起きます。IPv6 BYOIP でオンプレミスの IPv6 レンジを AWS 側に引き継いでおけば、Step5 実施前に対向側のルールを IPv6 BYOIP レンジへ更新でき、断絶なく移行を完了できます。ROA の有効期限管理 (Vol3 鉄則3) は移行完了後も継続的に必要になるため、移行完了をもって管理対象から外さないよう運用ルールに組み込んでください。


7. 詰まりポイント7選 図解

詰まりポイント判断ツリー
fig07: 詰まりポイント判断ツリー

IPv6 デュアルスタック移行で本番運用チームがつまずきやすいポイントを 7 パターンに体系化しました。いずれも「IPv4 では意識しなくてよかった設定がIPv6では明示的に必要になる」という共通構造を持ちます。

Pattern 1: SG/NACL の IPv6 ルール漏れ

症状: サブネットに IPv6 CIDR を割当て、インスタンスに IPv6 アドレスも付与したにもかかわらず、IPv6 経由の通信がタイムアウトする。IPv4 経由では正常に到達できる。

原因: セキュリティグループのカスタムルールを IPv4 用のみで作成し、cidr_ipv6 / ipv6_cidr_blocks の追加を忘れているケースが大半です。またステートレスな NACL では ingress/egress を対称に定義する必要があり、IPv6 の egress ルール追加を忘れると戻りパケットが破棄されます。

対処法: SG は aws_vpc_security_group_ingress_rule / egress_rule で IPv4・IPv6 のルールを両方明示します。NACL は IPv4/IPv6 で番号帯を分離し (例: IPv4 は 100 番台、IPv6 は 200 番台)、追加漏れをレビューで検出しやすくします。

Pattern 1 対処チェックリスト

1. SG のカスタムルールを棚卸しし、IPv4 用ルールに対応する IPv6 用ルールが存在するか確認する

2. カスタム NACL は ingress/egress を必ず対で追加する (デフォルト NACL は両方全許可のため対象外)

3. aws ec2 describe-security-groupsIpv6Ranges が空のルールを抽出し、意図的に IPv4 限定か棚卸しする

Pattern 2: DNS64 未有効化で IPv6-only 到達不可

症状: NAT Gateway・ルートテーブルまで正しく設定したはずなのに、IPv6-only サブネットのワークロードが IPv4-only サービスへ到達できない。

原因: NAT64 は NAT Gateway で自動的に有効ですが、DNS64 はサブネット単位で明示的に有効化が必要です。DNS64 を有効化していないと、IPv4-only サービスの名前解決結果がそのまま IPv4 アドレスとして返り、IPv6-only ワークロードはその宛先に到達できません。

対処法: サブネットの enable_dns64true に設定し、64:ff9b::/96 へのルートを NAT Gateway 向けに追加します。

resource "aws_subnet" "ipv6only" {
  vpc_id = aws_vpc.main.id
  ipv6_cidr_block = cidrsubnet(aws_vpc.main.ipv6_cidr_block, 8, 10)
  ipv6_native  = true
  enable_dns64 = true
}

resource "aws_route" "dns64_nat64" {
  route_table_id  = aws_route_table.ipv6only.id
  destination_ipv6_cidr_block = "64:ff9b::/96"
  nat_gateway_id= aws_nat_gateway.main.id
}

Pattern 2 対処チェックリスト

1. aws ec2 describe-subnets で対象サブネットの EnableDns64 が true か確認する

2. ルートテーブルに 64:ff9b::/96 → NAT Gateway のルートが存在するか確認する

3. dig で IPv4-only ホスト名を IPv6-only インスタンスから引き、64:ff9b:: プレフィックスの合成アドレスが返るか確認する

Pattern 3: egress-only IGW と NAT64 の混同

症状: dual-stack サブネットで IPv6 インターネット宛通信は正常だが、IPv4-only サービス宛の通信 (DNS64 合成アドレス経由) だけ失敗する。

原因: egress-only IGW は IPv6 同士の直接通信 (純粋な IPv6 インターネット宛) のみを扱う経路であり、NAT64 による IPv6→IPv4 変換は担当しません。::/0 を egress-only IGW にだけ向けたルートテーブルでは、64:ff9b::/96 宛の合成アドレストラフィックが NAT Gateway に届かず失敗します。

対処法: ルートテーブルに ::/0 → egress-only IGW (通常の IPv6 宛) と 64:ff9b::/96 → NAT Gateway (IPv4 変換宛) の 2 経路を両方定義し、より詳細な 64:ff9b::/96 ルートが優先されることを確認します。

Pattern 3 対処チェックリスト

1. ルートテーブルに ::/064:ff9b::/96 の 2 経路が両方定義されているか確認する

2. 64:ff9b::/96 の宛先が NAT Gateway (egress-only IGW ではない) を指しているか確認する

3. egress-only IGW は「IPv6 専用の outbound-only 経路」、NAT64 は「IPv6→IPv4 変換」という役割の違いをチーム内で明文化する

Pattern 4: dual-stack ELB のヘルスチェック

症状: ALB のターゲットグループに IPv6-only Fargate タスクを登録したところ、全ターゲットが Unhealthy と判定される。

原因: ターゲットグループの ip_address_typeipv4 のままだと、IPv4 アドレスを持たない IPv6-only タスクは登録用のアドレスが存在せず、ヘルスチェック自体が実行されません。ターゲットグループの ip_address_type はターゲットのアドレスファミリーと一致させる必要があります。

対処法: IPv6-only タスクを登録するターゲットグループは ip_address_type = "ipv6" で作成し、ALB 自体も ip_address_type = "dualstack" に設定します。

resource "aws_lb_target_group" "ipv6_only" {
  name  = "ipv6-only-tg"
  port  = 8080
  protocol = "HTTP"
  vpc_id= aws_vpc.main.id
  target_type = "ip"
  ip_address_type = "ipv6"

  health_check {
 path = "/health"
 healthy_threshold= 2
 unhealthy_threshold = 3
  }
}

resource "aws_lb" "app" {
  name= "app-alb"
  load_balancer_type = "application"
  ip_address_type  = "dualstack"
  subnets = var.dualstack_subnet_ids
}

Pattern 4 対処チェックリスト

1. ターゲットグループの ip_address_type とタスク/インスタンスのアドレスファミリーが一致しているか確認する

2. IPv4/IPv6 混在ワークロードでは dual-stack サブネットにタスクを配置し、両アドレスを持たせる設計を優先する

3. ALB の ip_address_type = dualstack 適用後は AAAA レコード追加を忘れずに行う

Pattern 5: API Gateway ポリシーの IPv4 前提

症状: API Gateway を dual-stack エンドポイント化した直後から、一部クライアントで 403 Forbidden が発生し始める。

原因: リソースポリシーで aws:SourceIp 条件を IPv4 CIDR のみで記述していると、IPv6 経由でアクセスしてきたクライアントの送信元 IP がどの許可 CIDR にもマッチせず拒否されます。dual-stack 化によって一部クライアントが IPv6 経由でアクセスするようになった結果、既存の IPv4 前提ポリシーが顕在化する典型例です。

対処法: aws:SourceIp 条件に IPv4 CIDR と IPv6 CIDR の両方を列挙します。

{
  "Effect": "Allow",
  "Principal": "*",
  "Action": "execute-api:Invoke",
  "Resource": "arn:aws:execute-api:ap-northeast-1:123456789012:abcdef1234/*",
  "Condition": {
 "IpAddress": {
"aws:SourceIp": [
  "203.0.113.0/24",
  "2001:db8:1234::/48"
]
 }
  }
}

Pattern 5 対処チェックリスト

1. dual-stack 化前にリソースポリシーの aws:SourceIp 条件を棚卸しし、IPv6 CIDR の追加漏れがないか確認する

2. WAF の IP セット (WebACL に紐付く許可/拒否リスト) も同様に IPv6 CIDR の追加が必要な点を確認する

3. dual-stack 切替直後はアクセスログで 403 の急増を監視し、IPv6 由来かを送信元アドレスで切り分ける

Pattern 6: IPv6 CIDR とオンプレ重複

症状: オンプレミスと AWS 間で Direct Connect / VPN 経由の IPv6 疎通を設定したところ、特定の宛先だけ意図しない経路に到達する。

原因: AWS が払い出す IPv6 /56 は GUA (Global Unicast Address) でありグローバルに一意ですが、オンプレミス側で ULA (fc00::/7 帯、実運用上は fd00::/8) を独自採番している場合、ULA はグローバルな一意性が保証されないため、M&A による組織統合や複数拠点間の VPN 相互接続時に他組織・他拠点と同一の ULA レンジを使用していて重複するケースがあります。

対処法: AWS 側は Amazon 提供の GUA を使用し、オンプレミス側の ULA 採番状況を IPAM (Vol3 §4) のスコープに含めて棚卸しします。オンプレミス側が ULA を使い続ける場合は、Direct Connect / VPN で接続する前に双方の CIDR 重複を必ず確認してください。

Pattern 6 対処チェックリスト

1. オンプレミス側で使用中の IPv6 レンジ (ULA/GUA いずれか) を棚卸しし IPAM に登録する

2. Direct Connect / VPN 接続前に AWS 側 GUA とオンプレミス側レンジの重複有無を確認する

3. 恒久的な相互接続が前提の拠点は ULA ではなく IPv6 BYOIP (6-5) による GUA 化を検討する

Pattern 7: EKS prefix delegation の ENI 上限

症状: /80 という巨大な IPv6 プレフィックスを割当てているにもかかわらず、ノードの Pod 数が想定より少ない段階で Pending のまま増えなくなる。

原因: IPv6 Prefix Delegation は ENI のスロットごとに 1 プレフィックスを割当てる方式であり、IPv4 プレフィックスモードのようなアドレス数の枯渇は起きません。しかし 1 ノードあたりに接続できる ENI 数とスロット数はインスタンスタイプ依存の上限が変わらず残っており、実際の最大 Pod 数はこの ENI/スロット上限で頭打ちになります。

対処法: インスタンスタイプごとの ENI/IP スロット上限表 (eni-max-pods.txt 相当) を参照し、想定 Pod 密度に対して余裕のあるインスタンスタイプを選定します。CNI メトリクス (ipamd のプレフィックス割当状況) を CloudWatch で監視し、上限接近時にノードグループのインスタンスタイプ変更やノード追加を検討します。

Pattern 7 対処チェックリスト

1. 対象インスタンスタイプの ENI 数上限・ENI あたりスロット数を事前に確認する

2. Prefix Delegation はアドレス枯渇を解消するが ENI/スロット上限そのものは解消しない前提でノード設計する

3. VPC CNI の CloudWatch メトリクスでプレフィックス割当状況を継続監視し、上限接近をアラート化する


8. アンチパターン → 正解パターン変換演習 + シリーズ繋ぎ

IPv6 移行の本番設計でよく見られるアンチパターンを 5 問の演習形式で整理する。
❌ アンチパターン (設計の問題点) → ✅ 正解パターン (本番設計) の変換を通じて、§7 で図解した詰まりポイントの具体的な解消手順を身につける。

Q1: NAT64/DNS64 未設定の IPv6-only サブネット → DNS64 + NAT Gateway の組み合わせ

アンチパターン: IPv6-only サブネットを作成しただけで、IPv4 専用サービスへの到達経路を用意していない

# ❌ IPv6-only サブネットのみ作成し DNS64/NAT64 が未設定
resource "aws_subnet" "ipv6_only" {
  vpc_id  = aws_vpc.main.id
  ipv6_cidr_block  = cidrsubnet(aws_vpc_ipv6_cidr_block_association.main.ipv6_cidr_block, 8, 10)
  ipv6_native= true
  enable_resource_name_dns_aaaa_record_on_launch = true
}
# DNS64 (Route 53 Resolver) も NAT64 (NAT Gateway) も未構成 → IPv4 専用の RDS 等に到達不可

正解パターン: Route 53 Resolver の DNS64 + NAT Gateway (NAT64) をセットで構成

# ✅ DNS64 を有効化した Resolver ルールを IPv6-only サブネットに関連付け
resource "aws_route53_resolver_rule" "dns64" {
  domain_name = "."
  rule_type= "RECURSIVE"
  resolver_endpoint_id = aws_route53_resolver_endpoint.dns64.id
}
# NAT64 対応の NAT Gateway をルートテーブルの ::/0 (合成プレフィックス経由) に紐付け
resource "aws_route" "ipv6_only_to_nat64" {
  route_table_id  = aws_route_table.ipv6_only.id
  destination_ipv6_cidr_block = "64:ff9b::/96"
  nat_gateway_id= aws_nat_gateway.natgw.id
}

解説: DNS64 は IPv4-only ホスト名を 64:ff9b::/96 (RFC6052) の合成 IPv6 アドレスへ変換し、NAT64 対応の NAT Gateway がその宛先を実際の IPv4 アドレスへ変換する。どちらか片方だけでは到達できないため、IPv6-only サブネットを構築する際は必ずセットで設計する。

Q2: egress-only IGW と NAT64 の混同 → ルート宛先で役割を分離

アンチパターン: IPv6-only ワークロードから IPv4 サービスへの到達を egress-only IGW だけで賄おうとする

# ❌ egress-only IGW への ::/0 ルートのみ設定 (IPv4 サービスには到達不可)
resource "aws_route" "ipv6_only_default" {
  route_table_id  = aws_route_table.ipv6_only.id
  destination_ipv6_cidr_block = "::/0"
  egress_only_gateway_id= aws_egress_only_internet_gateway.main.id
}
# egress-only IGW は IPv6 to IPv6 の outbound 専用。IPv4 専用サービスへは到達できない

正解パターン: 宛先ごとにルートを分離し、IPv6 outbound は egress-only IGW、IPv4 到達は NAT64 に振り分け

# ✅ 純粋な IPv6 outbound は egress-only IGW
resource "aws_route" "ipv6_native_egress" {
  route_table_id  = aws_route_table.ipv6_only.id
  destination_ipv6_cidr_block = "::/0"
  egress_only_gateway_id= aws_egress_only_internet_gateway.main.id
}
# IPv4 専用サービスへの到達は 64:ff9b::/96 経由で NAT64 (NAT Gateway) に振り分け
resource "aws_route" "ipv6_only_nat64" {
  route_table_id  = aws_route_table.ipv6_only.id
  destination_ipv6_cidr_block = "64:ff9b::/96"
  nat_gateway_id= aws_nat_gateway.natgw.id
}

解説: egress-only IGW は SNAT を行わない IPv6 to IPv6 の outbound 専用ゲートウェイであり、NAT64 の代替にはならない。ルート宛先を ::/0 (純粋な IPv6 outbound) と 64:ff9b::/96 (NAT64 経由の IPv4 到達) で明確に分離することが、IPv6-only サブネット設計の基本になる。

Q3: SG/NACL の IPv4 CIDR 前提放置 → IPv6 CIDR (::/0) の追加

アンチパターン: デュアルスタック化後も SG/NACL が 0.0.0.0/0 のみ許可し、IPv6 クライアントが拒否される

# ❌ IPv4 CIDR のみのインバウンドルール (dual-stack 化後も IPv6 クライアントを拒否)
resource "aws_security_group_rule" "https_in" {
  type  = "ingress"
  from_port= 443
  to_port  = 443
  protocol = "tcp"
  cidr_blocks = ["0.0.0.0/0"]
  security_group_id = aws_security_group.alb.id
}

正解パターン: ipv6_cidr_blocks を明示的に追加し、NACL も同様に対応

# ✅ IPv6 CIDR を追加したインバウンドルール
resource "aws_security_group_rule" "https_in" {
  type  = "ingress"
  from_port= 443
  to_port  = 443
  protocol = "tcp"
  cidr_blocks = ["0.0.0.0/0"]
  ipv6_cidr_blocks  = ["::/0"]
  security_group_id = aws_security_group.alb.id
}
# NACL も IPv6 ルールを個別エントリとして追加する必要がある (SG と異なり暗黙の許可がない)
resource "aws_network_acl_rule" "https_in_ipv6" {
  network_acl_id  = aws_network_acl.public.id
  rule_number  = 110
  protocol  = "tcp"
  rule_action  = "allow"
  ipv6_cidr_block = "::/0"
  from_port = 443
  to_port= 443
}

解説: SG/NACL は IPv4 と IPv6 のルールが完全に独立しており、片方を許可しても他方は自動的には許可されない。デュアルスタック化のタイミングで SG/NACL を棚卸しし、IPv6 CIDR のルール追加漏れがないか確認する。

Q4: ALB dual-stack 化のみでヘルスチェック/DNS が IPv4 前提のまま → ターゲットグループと AAAA レコードを揃える

アンチパターン: ALB を ip_address_type = "dualstack" にしただけで、ターゲットグループと DNS レコードが IPv4 のまま

# ❌ ALB のみ dualstack 化、ターゲットグループと DNS は IPv4 のまま
resource "aws_lb" "main" {
  ip_address_type = "dualstack"
  # ...
}
# ターゲットグループの ip_address_type が未指定 (デフォルト ipv4) のまま
# Route 53 に A レコードのみ存在し AAAA レコードが無い → IPv6 クライアントが名前解決できない

正解パターン: ターゲットグループの IP アドレスタイプと Route 53 の AAAA レコードを合わせて設定

# ✅ ALB / ターゲットグループ / Route 53 を dualstack で揃える
resource "aws_lb" "main" {
  ip_address_type = "dualstack"
  # ...
}
resource "aws_lb_target_group" "app" {
  ip_address_type = "ipv4"
  # dualstack ALB でもターゲットグループは EC2/ECS の IP バージョンに合わせて選択する
}
resource "aws_route53_record" "aaaa" {
  zone_id = aws_route53_zone.main.zone_id
  name = "app.example.com"
  type = "AAAA"
  alias {
 name = aws_lb.main.dns_name
 zone_id = aws_lb.main.zone_id
 evaluate_target_health = true
  }
}

解説: ALB の ip_address_type = "dualstack" はクライアント側 (フロントエンド) の IP バージョンを制御するだけであり、ターゲットグループ (バックエンド) の IP バージョンやヘルスチェック疎通は別設定になる。AAAA レコードを追加しなければ IPv6 クライアントはそもそも ALB の IPv6 アドレスを名前解決できない点も見落としやすい。

Q5: API Gateway REST のみ dual-stack 化 → 全エンドポイントタイプとカスタムドメインを揃える

アンチパターン: REST API のみ dual-stack 化し、HTTP/WebSocket API やカスタムドメインが IPv4 のまま放置される

# ❌ REST API のみ dualstack、カスタムドメインは IPv4 のまま
resource "aws_api_gateway_rest_api" "main" {
  endpoint_configuration {
 types  = ["REGIONAL"]
 ip_address_type = "dualstack"
  }
}
# aws_apigatewayv2_domain_name (HTTP/WebSocket 用カスタムドメイン) の
# ip_address_type が指定されておらず IPv4 のままになっている

正解パターン: REST/HTTP/WebSocket・カスタムドメインを一貫して dual-stack 化

# ✅ REST API を dualstack 化
resource "aws_api_gateway_rest_api" "main" {
  endpoint_configuration {
 types  = ["REGIONAL"]
 ip_address_type = "dualstack"
  }
}
# HTTP/WebSocket 用カスタムドメインも dualstack を明示
resource "aws_apigatewayv2_domain_name" "main" {
  domain_name = "api.example.com"
  domain_name_configuration {
 endpoint_type= "REGIONAL"
 ip_address_type = "DUALSTACK"
 security_policy = "TLS_1_2"
  }
}

解説: API Gateway の dual-stack 対応は REST/HTTP/WebSocket の各エンドポイントタイプとカスタムドメインでそれぞれ個別に設定が必要であり、REST API だけ dual-stack 化してもカスタムドメイン経由のアクセスは IPv4 のまま残ることがある。移行時は API の種類とカスタムドメインの両方を棚卸しし、抜け漏れなく dual-stack 化する。

Network 本番運用シリーズは Vol1 (VPC 基礎) から Vol2 (マルチアカウント網) / Vol3 (Cloud WAN・VPC Lattice・IPAM advanced・VPC Endpoint) と IPv4 前提の設計を積み上げてきました。
本 Vol4 は「IPv4 → IPv6 デュアルスタックへの移行」という直交軸でシリーズ全体を横断補完し、Vol3 が予告した IPv6 完全対応設計を実装として完結させるものです。
IPv6 移行を終えた本番環境は、次にネットワーク帯域・接続方式のさらなる最適化 (Direct Connect の高度化・サービス間通信の高度化) というテーマへ進む土台が整います。

Network 本番運用シリーズ ナビ

シリーズ前作 Vol3 (Cloud WAN / VPC Lattice / IPAM advanced) を読む