Getting IPv6 ready on AWS

Getting IPv6 ready on AWS

As of February 1, 2024, AWS has introduced a new charge for public IPv4 addresses [1]. This decision reflects the increasing scarcity and rising acquisition costs of IPv4 addresses, encouraging users to adopt IPv6 for modernization and conservation. The charges for public IPv4 addresses apply across all AWS services that can have a public IPv4 address. Each public IPv4 address will cost $0.005/hour or around $43.80/year.

Across tens or hundreds of instances, this might be a substantial bill for many customers, and it's reported that AWS could potentially make between $400 million to $1 billion in additional revenue from this change [2].

It's no secret that, as an industry, we tend to shy away from IPv6 due to the perceived complexity of migration, lack of familiarity or expertise with IPv6 configurations, concerns over compatibility with existing infrastructure, no benefits, or perceived security implications. This article will discuss the basics of IPv6 and getting IPv6 ready on AWS.

Basics of IPv6

IPv6 was formally introduced into the Internet standards with the publication of RFC 2460 in December 1998 [3].

Subnetting in IPv6 is vastly simplified due to its ample address space. In IPv6, subnetting is based on dividing the address into two parts: the network prefix and the interface identifier. The network prefix is typically 64 bits long, while the interface identifier is also 64 bits. This fixed-length subnetting scheme simplifies network organization and management, avoiding the complex subnet calculations required in IPv4.

You will typically hear of "prefix-delegations" in IPv6; for example, your ISP will allocate you a /56 prefix, and you will be responsible for creating /64 subnets. A host inside the subnet can then use Stateless Address AutoConfiguration and/or other mechanisms, such as DHCPv6 [4] , to allocate itself an address, and there is no need to work out what physical size of subnets to create. It's that simple.

  • 2001:db8:1234:5600::/56 - ISP Delegated Prefix
  • 2001:db8:1234:5600::/64 - Subnet 0
  • 2001:db8:1234:56ff::/64 - Subnet 255
Example IPv6 Subnetting

There are some IPv6 Address Space Ranges [5] worth remembering:

  • 2000::/3 refers to a global unicast address space in IPv6, designed for general public internet use. This range covers IPv6 addresses starting from 2000:: up to 3FFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF:FFFF. This is currently the world's routable allocated IPv6 address space. This is typically what you will be using.
  • fe80::/10 is designated for link-local addresses in IPv6. These addresses are used within a single network segment to which a host is connected and are not routable across the internet. This allows devices on the same physical local network to communicate with each other without requiring a globally unique IP address.
  • fc00::/7 is the range designated for private addressing, similar to IPv4's private address ranges, called the Unique Local Address (ULA) range. AWS expose fd00:ec2::/32 for local services such as TimeSync and DNS. However, AWS do not support ULA CIDRS for VPCs [6]. On the other hand, Google Cloud does provide ULA support for private connectivity [7].
  • 64:ff9b:1::/48 is reserved for Local-Use IPv4/IPv6 Translation
  • ff00::/8 in IPv6 is reserved for multicast addresses. These addresses send a single packet to multiple destinations, enabling efficient data distribution to multiple recipients.

Typical an IPv6 interface will have multiple addresses, including:

  • A link-local address fe80::/10 used for Neighbor Discovery Protocol (NDP), including Router Solicitation and Router Advertisement [4]
  • One or multiple SLAAC or DHCPv6 assigned Unicast addresses for communication with external (internet) devices.

AWS VPC Setup

When allocating IPv6 ranges to a VPC, AWS provides several options tailored to organizational needs. These include using AWS-provided IPv6 ranges, where AWS automatically allocates an IPv6 CIDR block; BYOIP (Bring Your Own IP), which allows organizations to bring their IPv6 blocks into AWS; and IPAM (IP Address Manager).

The simplest option is that AWS will provide a publically routable /56 IPv6 prefix for each VPC. This allows for 256 subnets to be created, each of the standard /64 size. Let's say, for example, we get allocated 2001:db8:1234:5600::/56. We might then allocate the following IPv6 CIDR blocks:

Subnet Name IPv6 Allocation
eu-west-1a Private 2001:db8:1234:5600::/64
eu-west-1b Private 2001:db8:1234:5601::/64
eu-west-1a Public 2001:db8:1234:5602::/64
eu-west-1b Public 2001:db8:1234:5603::/64

When an Elastic Network Interface (ENI) is created within an IPv6-enabled subnet, it is assigned an IPv6 address upon creation. Once the instance launches, the operating system must be configured with DHCPv6 to request an address, which EC2 Nitro Virtualisation platform intercepts and returns the address assigned to the ENI. While it's possible to manually set the operating system IP, VPC anti-spoofing mechanisms enforce the IP address configured on the network interface to match the one configured on the underlying ENI [8].

As all allocated IPv6 addresses are publically routable, AWS has introduced a new network component that can make instances private, synonymous with the traditional IPv4 "private subnets" approach. This component is called the egress-only internet gateway (EIGW). The following diagram shows a network configuration with a public and private subnet. Using the egress-only IGW ensures the internet cannot initiate connections inbound to the private instances.

Public and Private Subnets with IPv6

DNS64 + NAT64

As of February 2024, it's not realistically possible to have a full IPv6-only deployment in AWS. AWS still very much favours IPv4 and has limited dual-stack in their services; in fact, the list of services that support IPv6 is embarrassingly small; Corey Quinn at The Duckbill Group has an excellent summary [9].

While, in theory, you can create IPv6-only subnets, ensure you know all the caveats of AWS patchy support before committing to it. There are, however, some networking topologies to support IPv6-only using NAT64 (6-to-4) routing.

DNS64 and NAT64 are essential components for facilitating communication between IPv6-only and IPv4-only networks, particularly in environments where resources may reside in IPv6-only subnets but still require access to IPv4 services.

DNS64 is a service provided at the IPv6-only subnet level during DNS resolution and can be enabled using the modify-subnet-attribute API [10]. When DNS64 is enabled on an IPv6-only subnet, the Route53 Resolver looks up DNS and does one of the following:

  • If an IPv6 address is available, it returns the original record.
  • If the DNS record does not have an associated IPv6 address, Route53 synthesizes an IPv6 address by appending the IPv4 address to a well-known /96 prefix (64:ff9b::/96) as defined in RFC 6052.

The host then attempts to connect to the returned address. To process the 6-4 conversion, the 64:ff9b::/96prefix must be configured in the subnet route tables to direct traffic towards a NAT Gateway. The NAT Gateway then processes the packet, parsing the destination IPv4 address by truncating the 64:ff9b::/96 prefix and initiates a connection to the IPv4 resource, using the NATs IPv4 assigned address. The response packets are then de-NAT'ed and returned to the source over IPv6.

A NAT64 approach can be utilised to communicate to resources within the VPC, not just over the internet, for example, a IPv6-only subnet host can use the NAT64 approach to speak to an IPv4 instance, as shown below, for brevity routes which enable internet access have been excluded.

DNS64 and NAT64 in action

The future is dual-stack

The IPv6-only approach comes with additional complexity with having to take care of IPv6 -> IPv4 conversion. However, that shouldn't stop you from implementing dual-stack (IPv4 + IPv6) when creating future VPCs or retrofitting existing VPCs.

If you are looking at implementing VPCs today in AWS, aim for dual-stack, as there is still minimal support for IPv6-only infrastructure, including:

  • Network Load Balancers (only support dual-stack)
  • Elastic Container Registry (only has IPv4 registry endpoints)
  • EKS (IPv6-only clusters must still be run in IPv4 enabled subnets)
  • CloudFront does not support IPv6-only origins
  • Private service endpoints are mostly IPv4

Using dual-stack subnets, each instance has an IPv4 and IPv6 address, allowing them to choose the most appropriate protocol to communicate with. The following diagram shows a public and private dual-stack configuration.

Dual-Stack VPC

When using this type of dual-stack configuration, there is no need to handle the complexities of DNS64 + NAT64. Simply configure your route tables to handle private IPv6 traffic to an egress-only IGW, and the public IPv6 subnets to a standard IGW. The IPv4 configuration remains as standard, private 0.0.0.0/0 to NAT, and public 0.0.0.0/0 to IGW.

Using such a dual-stack approach has the following advantages:

  • IPv6 will be preferred where possible and enabled for future projects and expansion.
  • The egress-only IGW does not charge for data transfer, so IPv6 private subnet traffic to the internet is not charged additional rates, unlike with IPv4 NAT Gateways. Saving costs when you utilise IPv6.
  • Your existing IPv4 setup remains unchanged.

Reducing IPv4 Usage

While dual-stack is a significant first step to implementing IPv6 in your infrastructure, it doesn't solve a billing issue if you already use too many public IPv4 addresses. Such approaches may be considered for reducing your current usage:

  • Evaluate the number of public load balancers; each will have a public IP assigned to each enabled availability zone.
  • Use NAT Gateways and private instances; reducing your public IP usage by only having public IPs attached to NATs. If internet-related data transfer fees become an issue, use dual-stack and communicate over IPv6 where possible.
  • Consider using technologies like Cloudflare Tunnels [11], allowing inbound traffic without load balancers or exposed origins.

Cloudflare Tunnels is an exciting approach and can leave you with almost zero public origin exposure (and IPv4 usage), consider the following setup.

Dual-stack VPC with Cloudflared Tunnels

Here we deploy an IPv4 or an IPv6 enabled EKS cluster into an IPv6 enabled subnet, and run cloudflared alongside an ingress controller (for example, ingress-nginx [12]). If running in an IPv4 EKS cluster, we would have to use host networking for the pod spec to expose IPv6 abilities. This would allow cloudflared to connect to cloudflare over the egress-only IGW (no data transfer costs - and no single point of failure). A NAT Gateway is then deployed for any outbound public IPv4 traffic.

I have deployed such approaches for clients using CloudFlare, and it provides a tremendous config-driven approach all the way from the CloudFlare edge into a running Kubernetes service; with some added advantages:

  • CloudFlare Zero Trust access for internal workloads, limit which organisational users can access what applications with CloudFlare Applications.
  • Deploy multiple tunnels to isolate internal and external workloads, all traffic must be initiated through the CloudFlare network.
  • Minimal IPv4 usage (only for NATs)
  • Reduced NAT data transfer costs (only for IPv4 traffic)
  • Each cloudflared establishes two tunnels to CloudFlare, the EIGW provides no single point of failure.

Conclusion

We've seen the basics of IPv6, the complexities around IPv6-only subnets and the advantages of deploying dual-stack VPCs. IPv6 doesn't have to be a complicated beast. Now it's time for you to be proactive in adopting IPv6. Take things slow, enable dualstack and start using IPv6 in your projects.

References

  1. New AWS Public IPv4 Address Charge & Public IP Insights
  2. AWS IPv4 Charges
  3. RFC 2460
  4. Packet Mania - IPv6 Addressing
  5. IPv6 Address Space
  6. Planning IPv6 Adoption in the AWS Cloud Network
  7. Using IPv6 Unique Local Addresses (ULA) in Google Cloud
  8. Amazon VPC Design
  9. Duckbill Group - AWS IPv6 Gaps
  10. API ModifySubnetAttribute
  11. Cloudflare Tunnel
  12. Ingress NGINX