NewBuildkite hosted agents. Check out the Q1 Release for the latest features, including managed CI/CD offerings for Mac and Linux.

Session IP address pinning for dual-stack IPv6

Earlier this year, Buildkite introduced session IP address pinning as an additional security measure. This feature ensures that authorized sessions can only originate from the IP address that initially created the session. If any other IP address attempts to access the organization using the same session cookie, the authorization is immediately revoked, and the user is prompted to re-authenticate.

While this feature has been well-received for IPv4 addresses, the complexities of IPv6 addressing and dual-stack connections present some challenges. In Buildkite, this manifests as asking IPv6 users to re-authenticate too often.

This blog post will explore these challenges and propose improvements to enhance the user experience for dual-stack IPv6 users.

Session pinning with IPv4

Session pinning for IPv4 addresses is well understood and causes us few problems. A typical fixed-line connection will have one external IPv4 address that changes on the order of days. Sometimes they are static for the lifetime of your subscription, or even owned by your organization.

However, it's becoming more common for internet service providers (ISPs) to have multiple customers share a single address using CG-NAT, especially on mobile connections. Because multiple people can share a single IP address, we never rely on an IP address for authentication. It only provides additional defense in depth.

Understanding IPv6 addressing

IPv6 addresses have become increasingly popular due to the exhaustion of IPv4 addresses. With a 128-bit address space, IPv6 offers 2¹²⁸ addresses: practically infinite. The standard subnet size in IPv6 is a /64 (64 bits are a fixed prefix, 64 bits are flexible), which provides 2⁶⁴: 18.4 quintillion addresses. 

ISPs typically delegate larger prefixes, such as a /48 or a /56, enabling users to create multiple subnets for security and operational reasons. Although a single subnet within a prefix is usually sufficient for residential and small business connections, it's important to note that devices within a subnet may change addresses periodically to avoid tracking.

Devices connected to a router will then be allocated (or choose) addresses within that subnet. There are a few ways that can happen:

  • Modified EUI-64: Your device appends its own MAC address to your subnet. It means your device can be tracked around the internet and leaks information about your device (such as its manufacturer), so it is no longer used.
  • Secured: Your device appends a fixed but randomly generated component to your subnet. It still means your device (not just your connection) can be followed around the internet, so it is also generally avoided.
  • Temporary: To avoid tracking, your device will create random new addresses within your subnet on the order of hours to days. 
  • DHCPv6: Your device asks your router for an IP address, which is assigned sequentially or randomly within your subnet.

Temporary addressing is the default behavior since OS X Lion and Windows Vista. On macOS, it changes every 24 hours by default (check your setting on the command line by running sysctl net.inet6.ip6.temppltime). This means that even with a static prefix (which identifies your connection), your IPv6 address will still change regularly so your specific device can’t be tracked as easily.

Each network interface will have its own connection. For example, if you switch between using WiFi on a laptop and a docking station with an Ethernet connection, your IPv6 address will change.

Dual stack and Happy Eyeballs

Websites like Buildkite can be accessed over both IPv4 and IPv6. When a connection supports both protocols, the browser performs DNS lookups for A (IPv4) and AAAA (IPv6) records and attempts to connect using both types of addresses. This process, known as Happy Eyeballs (or Fast Fallback if you’re less prone to whimsy), allows the browser to choose the most optimal protocol based on response times.

However, this means that when response times between IPv4 and IPv6 vary, the browser may switch between the two, leading to potential session invalidation and re-authentication. This is why Buildkite might end up asking you to re-authenticate more often than you'd like.

Your results will be different, but right now I see:

$ dig A ;; ANSWER SECTION: 5 IN A 5 IN A 5 IN A 5 IN A
$ dig AAAA ;; ANSWER SECTION: 60 IN AAAA 2600:9000:204b:fe00:4:17ba:a80:93a1 60 IN AAAA 2600:9000:204b:7a00:4:17ba:a80:93a1 60 IN AAAA 2600:9000:204b:3400:4:17ba:a80:93a1 60 IN AAAA 2600:9000:204b:d000:4:17ba:a80:93a1 60 IN AAAA 2600:9000:204b:b000:4:17ba:a80:93a1 60 IN AAAA 2600:9000:204b:cc00:4:17ba:a80:93a1 60 IN AAAA 2600:9000:204b:3200:4:17ba:a80:93a1 60 IN AAAA 2600:9000:204b:2c00:4:17ba:a80:93a1

In this case, all the IPv4 addresses are in the same subnet ( All the IPv6 addresses are in different subnets, but have the same address within the subnet (4:17ba:a80:93a1). This is an implementation detail of the infrastructure provider we’re using at the time of writing (Amazon CloudFront).

Challenges with Session Pinning in Dual-Stack IPv6

Currently, Buildkite only supports pinning a single IP address, which can cause inconvenience for users with dual-stack IPv6 connections. The browser's Happy Eyeballs algorithm may arbitrarily switch between IPv4 and IPv6 addresses, and IPv6 addresses can also change frequently.

To address these challenges and enhance the user experience, we are considering the following improvements.

Fix 1: Pin both an IPv4 and IPv6 address

By allowing a session to have both an IPv4 and an IPv6 address, users will no longer be required to re-authenticate each time the browser switches between the two protocols. While you'd still need to authorize each address separately, it's an improvement on re-authenticating every time the browser switches between them.

Fix 2: Pin an IPv6 /64 subnet instead of a /128 address

In recognition that a single device may frequently change addresses within a /64 subnet, Buildkite is considering pinning the subnet prefix instead. This approach aligns with the behavior of IPv4, where users can seamlessly switch between WiFi and wired Ethernet on the same connection without re-authentication.

However, it's important to note that this solution does not cater to advanced networks that utilize multiple subnets within a prefix. In such cases, organization administrators may be allowed to pin a larger prefix, such as a /56, if needed.


As Buildkite strives to enhance security and user experience, improvements to session IP address pinning for dual-stack IPv6 connections are being explored. By allowing pinning of both IPv4 and IPv6 addresses and considering pinning a subnet prefix instead of a specific address, Buildkite aims to provide a more seamless and secure experience for users with dual-stack IPv6 connections. These enhancements will help address the challenges posed by the Happy Eyeballs algorithm and the dynamic nature of IPv6 addressing, ultimately improving the overall user experience.