diff --git a/daemon/libnetwork/internal/rlkclient/rootlesskit_client_linux.go b/daemon/libnetwork/internal/rlkclient/rootlesskit_client_linux.go index d0886428e1..92bf81b599 100644 --- a/daemon/libnetwork/internal/rlkclient/rootlesskit_client_linux.go +++ b/daemon/libnetwork/internal/rlkclient/rootlesskit_client_linux.go @@ -110,11 +110,12 @@ func (c *PortDriverClient) ChildHostIP(proto string, hostIP netip.Addr) netip.Ad if c.childIP.IsValid() { return c.childIP } - // Preserve IPv4 loopback addresses, the child namespace's lo covers all - // of 127.0.0.0/8. Mapping them all to 127.0.0.1 makes bindings on the - // same port but distinct loopback addresses collide in the child - // namespace. - if hostIP.Is4() && hostIP.IsLoopback() { + // Child namespaces only listen on loopback addresses. Preserve requested + // loopback addresses (the child namespace's lo covers all of 127.0.0.0/8, + // and collapsing them makes bindings on the same port but distinct + // loopback addresses collide); otherwise use the canonical loopback + // address for the family. + if hostIP.IsLoopback() { return hostIP } if hostIP.Is6() { diff --git a/daemon/libnetwork/internal/rlkclient/rootlesskit_client_linux_test.go b/daemon/libnetwork/internal/rlkclient/rootlesskit_client_linux_test.go index 53989bdd28..b0a85b563e 100644 --- a/daemon/libnetwork/internal/rlkclient/rootlesskit_client_linux_test.go +++ b/daemon/libnetwork/internal/rlkclient/rootlesskit_client_linux_test.go @@ -77,6 +77,13 @@ func TestChildHostIP(t *testing.T) { hostIP: netip.MustParseAddr("127.0.1.2"), want: netip.MustParseAddr("127.0.1.2"), }, + { + name: "v6 loopback", + pdc: builtin, + proto: "tcp", + hostIP: netip.MustParseAddr("::1"), + want: netip.IPv6Loopback(), + }, { name: "v6 non-loopback", pdc: builtin,