ping6.net

AWS 上的 IPv6:VPC、EC2、ELB 及更多

在 Amazon Web Services 上部署 IPv6 的完整指南。配置 VPC、子网、安全组、负载均衡器和仅出口网关。

ping6.net2024年12月14日8 min read
IPv6AWScloudVPCEC2networking

AWS 在大多数服务中都有全面的 IPv6 支持,但默认情况下不启用。你必须选择加入,配置分布在多个层次。

TL;DR - 快速摘要

要点:

  • AWS 提供双栈 IPv6(大多数服务不支持仅 IPv6)
  • 你从 2600::/12 范围获得 Amazon 分配的 /56
  • 所有 IPv6 地址都是全局唯一且可路由的(无 NAT)
  • 仅出口网关提供有状态过滤而不需要地址转换

跳转至: VPC 配置 | EC2 实例 | 安全组 | 负载均衡器 | Terraform 示例

AWS IPv6 架构概述#

AWS 以双栈配置提供 IPv6。你的资源获得 IPv4 和 IPv6 地址。在大多数服务中,你不能仅运行 IPv6(除了一些容器和无服务器工作负载)。

Amazon 从其公共池分配 IPv6 CIDR 块。你无法选择前缀——AWS 从 2600::/12 范围为你的 VPC 分配 /56 块。

与 IPv4 使用私有 RFC1918 地址(10.0.0.0/8、172.16.0.0/12、192.168.0.0/16)不同,所有 AWS IPv6 地址都是全局唯一且可路由的(GUA)。IPv6 没有传统意义上的 NAT——如果你想要仅出站连接,请使用仅出口互联网网关。

VPC IPv6 配置#

从 VPC 开始。添加 IPv6 不会中断现有的 IPv4 资源。

关联 IPv6 CIDR 块#

将 Amazon 提供的 IPv6 CIDR 添加到 VPC:

aws ec2 associate-vpc-cidr-block \
  --vpc-id vpc-0abc123def456 \
  --amazon-provided-ipv6-cidr-block

AWS 分配 /56 块。响应显示分配的范围:

{
  "Ipv6CidrBlockAssociation": {
    "Ipv6CidrBlock": "2600:1f13:1234:5600::/56",
    "Ipv6CidrBlockState": {
      "State": "associating"
    }
  }
}

等待状态变为 associated

aws ec2 describe-vpcs --vpc-ids vpc-0abc123def456 \
  --query 'Vpcs[0].Ipv6CidrBlockAssociationSet[0].Ipv6CidrBlockState.State'

如果你有提供商独立的 IPv6 空间并希望跨 AWS 账户或迁移维护地址,还可以使用"自带 IP"(BYOIP)。大多数用户使用 Amazon 提供的地址。

子网 IPv6 配置#

子网需要从 VPC 的 /56 块中获取自己的 /64 切片。

列出 VPC 的 IPv6 CIDR:

aws ec2 describe-vpcs --vpc-ids vpc-0abc123def456 \
  --query 'Vpcs[0].Ipv6CidrBlockAssociationSet[0].Ipv6CidrBlock'

输出:2600:1f13:1234:5600::/56

/64 块分配给子网。对子网部分使用十六进制值 00-FF:

# 公有子网 A - 2600:1f13:1234:5600::/64
aws ec2 associate-subnet-cidr-block \
  --subnet-id subnet-0abc111 \
  --ipv6-cidr-block 2600:1f13:1234:5600::/64
 
# 公有子网 B - 2600:1f13:1234:5601::/64
aws ec2 associate-subnet-cidr-block \
  --subnet-id subnet-0abc222 \
  --ipv6-cidr-block 2600:1f13:1234:5601::/64
 
# 私有子网 A - 2600:1f13:1234:5610::/64
aws ec2 associate-subnet-cidr-block \
  --subnet-id subnet-0abc333 \
  --ipv6-cidr-block 2600:1f13:1234:5610::/64

为在公有子网中启动的实例启用自动分配 IPv6 地址:

aws ec2 modify-subnet-attribute \
  --subnet-id subnet-0abc111 \
  --assign-ipv6-address-on-creation

路由表#

IPv6 路由与 IPv4 分离。为 IPv6 CIDR 块创建路由。

对于公有子网,将所有 IPv6 流量(::/0)路由到互联网网关:

aws ec2 create-route \
  --route-table-id rtb-0abc123 \
  --destination-ipv6-cidr-block ::/0 \
  --gateway-id igw-0def456

互联网网关无需修改即可处理 IPv4 和 IPv6。

对于私有子网,你将使用仅出口互联网网关(稍后解释):

aws ec2 create-route \
  --route-table-id rtb-0abc789 \
  --destination-ipv6-cidr-block ::/0 \
  --egress-only-internet-gateway-id eigw-0ghi012

验证路由:

aws ec2 describe-route-tables --route-table-ids rtb-0abc123 \
  --query 'RouteTables[0].Routes'

EC2 实例 IPv6 配置#

EC2 实例需要显式分配 IPv6 地址。

使用 IPv6 启动新实例#

启动时指定 --ipv6-address-count

aws ec2 run-instances \
  --image-id ami-0abc123 \
  --instance-type t3.medium \
  --subnet-id subnet-0abc111 \
  --ipv6-address-count 1 \
  --key-name mykey \
  --security-group-ids sg-0abc456

或从子网范围分配特定的 IPv6 地址:

aws ec2 run-instances \
  --image-id ami-0abc123 \
  --instance-type t3.medium \
  --subnet-id subnet-0abc111 \
  --ipv6-addresses Ipv6Address=2600:1f13:1234:5600::a \
  --key-name mykey \
  --security-group-ids sg-0abc456

将 IPv6 添加到现有实例#

获取网络接口 ID(ENI):

aws ec2 describe-instances --instance-ids i-0abc123 \
  --query 'Reservations[0].Instances[0].NetworkInterfaces[0].NetworkInterfaceId'

分配 IPv6 地址:

aws ec2 assign-ipv6-addresses \
  --network-interface-id eni-0def456 \
  --ipv6-address-count 1

或指定确切地址:

aws ec2 assign-ipv6-addresses \
  --network-interface-id eni-0def456 \
  --ipv6-addresses 2600:1f13:1234:5600::b

实例操作系统配置#

大多数现代 AMI(Amazon Linux 2023、Amazon Linux 2、Ubuntu 20.04+)通过 cloud-init 和 DHCPv6 自动配置 IPv6。

SSH 到实例并验证:

ip -6 addr show

你应该在 eth0 接口上看到分配的 IPv6 地址:

2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 9001
    inet6 2600:1f13:1234:5600::a/128 scope global
       valid_lft forever preferred_lft forever
    inet6 fe80::4a2:c9ff:fe12:3456/64 scope link
       valid_lft forever preferred_lft forever

测试连接:

ping6 google.com
curl -6 https://ifconfig.co

如果 IPv6 未配置,检查 cloud-init 日志:

sudo cat /var/log/cloud-init.log | grep -i ipv6

一些较旧的 AMI 可能需要手动配置。添加到 /etc/network/interfaces(Debian/Ubuntu)或 /etc/sysconfig/network-scripts/ifcfg-eth0(RHEL/CentOS)。

安全组#

安全组需要显式的 IPv6 规则。IPv4 规则不适用于 IPv6 流量。

创建双栈安全组规则#

允许来自任何地方的 HTTP/HTTPS(IPv4 和 IPv6):

# IPv4
aws ec2 authorize-security-group-ingress \
  --group-id sg-0abc123 \
  --ip-permissions IpProtocol=tcp,FromPort=443,ToPort=443,IpRanges='[{CidrIp=0.0.0.0/0}]'
 
# IPv6
aws ec2 authorize-security-group-ingress \
  --group-id sg-0abc123 \
  --ip-permissions IpProtocol=tcp,FromPort=443,ToPort=443,Ipv6Ranges='[{CidrIpv6=::/0}]'

允许来自特定 IPv6 前缀的 SSH:

aws ec2 authorize-security-group-ingress \
  --group-id sg-0abc123 \
  --ip-permissions IpProtocol=tcp,FromPort=22,ToPort=22,Ipv6Ranges='[{CidrIpv6=2001:db8::/32,Description="Office IPv6"}]'

允许所有 ICMPv6(IPv6 操作所需):

aws ec2 authorize-security-group-ingress \
  --group-id sg-0abc123 \
  --ip-permissions IpProtocol=ipv6-icmp,FromPort=-1,ToPort=-1,Ipv6Ranges='[{CidrIpv6=::/0}]'

ICMPv6 对 IPv6 至关重要。不要阻止它。这不是安全风险——这是协议要求。

安全组最佳实践#

  1. 将 IPv4 规则镜像到 IPv6,除非有特定原因不这样做
  2. 始终允许 ICMPv6,来源与应用程序流量相同
  3. 使用前缀列表来管理跨两个系列的复杂规则集
  4. 标记安全组以识别双栈与仅 IPv4

示例:为组织的 IPv6 范围创建托管前缀列表:

aws ec2 create-managed-prefix-list \
  --prefix-list-name org-ipv6-ranges \
  --address-family IPv6 \
  --max-entries 10 \
  --entries Cidr=2001:db8:100::/40,Description=HQ \
           Cidr=2001:db8:200::/40,Description=DataCenter

在安全组规则中引用它:

aws ec2 authorize-security-group-ingress \
  --group-id sg-0abc123 \
  --ip-permissions IpProtocol=tcp,FromPort=443,ToPort=443,PrefixListIds='[{PrefixListId=pl-0abc123}]'

弹性负载均衡#

应用程序负载均衡器(ALB)和网络负载均衡器(NLB)支持双栈。经典负载均衡器不支持。

应用程序负载均衡器双栈#

创建或修改 ALB 以使用双栈:

aws elbv2 create-load-balancer \
  --name my-alb \
  --subnets subnet-0abc111 subnet-0abc222 \
  --security-groups sg-0abc123 \
  --ip-address-type dualstack

对于现有负载均衡器:

aws elbv2 set-ip-address-type \
  --load-balancer-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/my-alb/1234567890abcdef \
  --ip-address-type dualstack

负载均衡器自动获取 A 和 AAAA DNS 记录:

dig my-alb-1234567890.us-east-1.elb.amazonaws.com A
dig my-alb-1234567890.us-east-1.elb.amazonaws.com AAAA

客户端可以通过 IPv4 或 IPv6 连接。负载均衡器使用其配置的地址系列(后端实例通常为 IPv4)将流量转发到目标。

网络负载均衡器双栈#

NLB 类似地支持双栈:

aws elbv2 create-load-balancer \
  --name my-nlb \
  --type network \
  --subnets subnet-0abc111 subnet-0abc222 \
  --ip-address-type dualstack

NLB 保留客户端 IP 地址,因此目标看到实际的 IPv6 源地址。确保应用程序日志和安全组处理两个地址系列。

目标组#

目标组通过其 IPv4 地址(最常见)或 IPv6 注册实例。你不能在单个目标组中混合地址系列。

创建 IPv4 目标组:

aws elbv2 create-target-group \
  --name my-targets-ipv4 \
  --protocol HTTP \
  --port 80 \
  --vpc-id vpc-0abc123 \
  --ip-address-type ipv4

创建 IPv6 目标组:

aws elbv2 create-target-group \
  --name my-targets-ipv6 \
  --protocol HTTP \
  --port 80 \
  --vpc-id vpc-0abc123 \
  --ip-address-type ipv6

即使使用双栈负载均衡器,大多数部署也使用 IPv4 目标组。负载均衡器透明地处理协议转换。

仅出口互联网网关#

仅出口互联网网关允许来自私有子网的出站 IPv6 连接,同时阻止入站连接。这类似于 IPv4 的 NAT,但没有地址转换——IPv6 地址保持全局唯一。

创建仅出口网关#

aws ec2 create-egress-only-internet-gateway \
  --vpc-id vpc-0abc123

注意网关 ID:

{
  "EgressOnlyInternetGateway": {
    "EgressOnlyInternetGatewayId": "eigw-0abc123"
  }
}

路由私有子网流量#

在私有子网路由表中添加路由:

aws ec2 create-route \
  --route-table-id rtb-0abc789 \
  --destination-ipv6-cidr-block ::/0 \
  --egress-only-internet-gateway-id eigw-0abc123

私有子网中的实例现在可以发起出站 IPv6 连接,但不会接受来自互联网的入站流量。

使用场景:

  • 需要软件更新的数据库服务器
  • 访问外部 API 的应用服务器
  • 通过 IPv6 调用 AWS API(S3、DynamoDB 等)的内部服务

安全注意事项#

仅出口网关不提供匿名性。你的 IPv6 地址仍然对外部服务可见。这是有状态防火墙,而不是 NAT。

如果你需要带地址掩码的真正出站,则需要 NAT64 网关(设置更复杂,很少需要)。

Route 53 DNS#

Route 53 完全支持 IPv6 和 AAAA 记录。

创建 AAAA 记录#

为实例或负载均衡器添加 AAAA 记录:

aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --change-batch '{
    "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "app.example.com",
        "Type": "AAAA",
        "TTL": 300,
        "ResourceRecords": [{"Value": "2600:1f13:1234:5600::a"}]
      }
    }]
  }'

负载均衡器的别名记录#

对 ALB/NLB 使用别名记录(比 AAAA 记录更好——如果 LB IP 更改则自动更新):

aws route53 change-resource-record-sets \
  --hosted-zone-id Z1234567890ABC \
  --change-batch '{
    "Changes": [{
      "Action": "CREATE",
      "ResourceRecordSet": {
        "Name": "app.example.com",
        "Type": "AAAA",
        "AliasTarget": {
          "HostedZoneId": "Z35SXDOTRQ7X7K",
          "DNSName": "my-alb-1234567890.us-east-1.elb.amazonaws.com",
          "EvaluateTargetHealth": true
        }
      }
    }]
  }'

AliasTarget 中的 HostedZoneId 是该区域中 ELB 的规范托管区域(请参阅 AWS 文档 获取正确值)。

健康检查#

Route 53 健康检查支持 IPv6:

aws route53 create-health-check \
  --health-check-config \
    IPAddress=2600:1f13:1234:5600::a,Port=443,Type=HTTPS,ResourcePath=/health,RequestInterval=30,FailureThreshold=3 \
  --caller-reference $(uuidgen)

将健康检查与故障转移或加权路由策略一起使用以实现高可用性。

CloudFront IPv6#

CloudFront 分发默认支持 IPv6(在大多数情况下无法禁用)。

CloudFront 的全球边缘网络使用双栈。通过 IPv6 连接的客户端访问与 IPv4 客户端相同的边缘位置。

验证 IPv6 支持:

dig d111111abcdef8.cloudfront.net AAAA

CloudFront 处理协议转换。如果源仅支持 IPv4,CloudFront 仍然向 IPv6 客户端提供内容。

源配置不需要更改:

aws cloudfront create-distribution \
  --distribution-config file://distribution-config.json

示例 distribution-config.json(缩写):

{
  "Origins": {
    "Items": [{
      "Id": "my-origin",
      "DomainName": "example.com",
      "CustomOriginConfig": {
        "OriginProtocolPolicy": "https-only"
      }
    }]
  },
  "Enabled": true,
  "Comment": "My distribution"
}

CloudFront 自动为分发配置 IPv6 地址。无需额外配置。

Terraform 示例#

双栈 AWS 设置的基础设施即代码:

# 带 IPv6 的 VPC
resource "aws_vpc" "main" {
  cidr_block = "10.0.0.0/16"
  assign_generated_ipv6_cidr_block = true
  enable_dns_support   = true
  enable_dns_hostnames = true
 
  tags = {
    Name = "main-vpc"
  }
}
 
# 互联网网关
resource "aws_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
}
 
# 仅出口互联网网关
resource "aws_egress_only_internet_gateway" "main" {
  vpc_id = aws_vpc.main.id
}
 
# 公有子网
resource "aws_subnet" "public" {
  vpc_id                  = aws_vpc.main.id
  cidr_block              = "10.0.1.0/24"
  ipv6_cidr_block         = cidrsubnet(aws_vpc.main.ipv6_cidr_block, 8, 1)
  assign_ipv6_address_on_creation = true
  availability_zone       = "us-east-1a"
 
  tags = {
    Name = "public-subnet"
  }
}
 
# 公有子网的路由表
resource "aws_route_table" "public" {
  vpc_id = aws_vpc.main.id
 
  route {
    cidr_block = "0.0.0.0/0"
    gateway_id = aws_internet_gateway.main.id
  }
 
  route {
    ipv6_cidr_block = "::/0"
    gateway_id      = aws_internet_gateway.main.id
  }
 
  tags = {
    Name = "public-rt"
  }
}
 
resource "aws_route_table_association" "public" {
  subnet_id      = aws_subnet.public.id
  route_table_id = aws_route_table.public.id
}
 
# 安全组
resource "aws_security_group" "web" {
  name        = "web-sg"
  description = "Allow HTTP/HTTPS"
  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"]
  }
 
  ingress {
    from_port   = -1
    to_port     = -1
    protocol    = "ipv6-icmp"
    ipv6_cidr_blocks = ["::/0"]
  }
 
  egress {
    from_port   = 0
    to_port     = 0
    protocol    = "-1"
    cidr_blocks = ["0.0.0.0/0"]
    ipv6_cidr_blocks = ["::/0"]
  }
}
 
# 带 IPv6 的 EC2 实例
resource "aws_instance" "web" {
  ami           = "ami-0c55b159cbfafe1f0"  # Amazon Linux 2023
  instance_type = "t3.medium"
  subnet_id     = aws_subnet.public.id
  ipv6_address_count = 1
 
  vpc_security_group_ids = [aws_security_group.web.id]
 
  tags = {
    Name = "web-server"
  }
}
 
# 应用程序负载均衡器
resource "aws_lb" "main" {
  name               = "main-alb"
  internal           = false
  load_balancer_type = "application"
  security_groups    = [aws_security_group.web.id]
  subnets            = [aws_subnet.public.id]
  ip_address_type    = "dualstack"
 
  tags = {
    Name = "main-alb"
  }
}
 
# 输出 IPv6 CIDR
output "vpc_ipv6_cidr" {
  value = aws_vpc.main.ipv6_cidr_block
}
 
output "instance_ipv6" {
  value = aws_instance.web.ipv6_addresses
}

应用:

terraform init
terraform plan
terraform apply

测试 AWS IPv6 部署#

验证端到端连接:

# 测试 DNS 解析
dig app.example.com AAAA
 
# 测试 HTTP/HTTPS 访问
curl -6 https://app.example.com
 
# 从实例测试
ssh ec2-user@<instance-ipv6>
ping6 google.com
curl -6 https://ifconfig.co

使用 AWS 可达性分析器验证网络路径:

aws ec2 create-network-insights-path \
  --source <instance-eni> \
  --destination <alb-eni> \
  --protocol tcp \
  --destination-port 443
 
aws ec2 start-network-insights-analysis \
  --network-insights-path-id <path-id>

检查可能阻止 IPv6 的错误配置。

常见问题和解决方案#

问题:实例有 IPv6 地址但无法访问互联网 解决方案:检查路由表是否有到 IGW 的 ::/0 路由,验证安全组允许出站 IPv6

问题:负载均衡器没有 AAAA 记录 解决方案:确保 ip-address-type 设置为 dualstack,验证子网有 IPv6 CIDR 块

问题:无法通过 IPv6 SSH 到实例 解决方案:从 ::/0 或特定 IPv6 CIDR 添加允许 TCP 端口 22 的安全组规则

问题:仅出口网关不工作 解决方案:验证路由表关联,检查实例是否分配了 IPv6 地址

问题:CloudFront 不通过 IPv6 提供服务 解决方案:CloudFront IPv6 是自动的,无法禁用——检查 DNS 解析和客户端连接

成本注意事项#

AWS 上的 IPv6 本身是免费的。你不需要为 IPv6 CIDR 块或地址付费。

IPv4 和 IPv6 的数据传输成本相同。IPv6 没有定价优势——好处是架构简单性和避免 IPv4 耗尽。

仅出口互联网网关是免费的(与 NAT 网关不同,NAT 网关每小时成本约为 $0.045,外加数据处理费用)。

相关文章#

验证 AWS IPv6 连接

使用我们的Ping 工具测试 AWS 资源,使用DNS 工具验证 Route 53 AAAA 记录。

其他资源#