Moby IPv6 enabled on IPv4-only network interfaces
Description
Moby is an open source container framework that is a key component of Docker Engine, Docker Desktop, and other distributions of container tooling or runtimes. In 26.0.0, IPv6 is not disabled on network interfaces, including those belonging to networks where --ipv6=false. An container with an ipvlan or macvlan interface will normally be configured to share an external network link with the host machine. Because of this direct access, (1) Containers may be able to communicate with other hosts on the local network over link-local IPv6 addresses, (2) if router advertisements are being broadcast over the local network, containers may get SLAAC-assigned addresses, and (3) the interface will be a member of IPv6 multicast groups. This means interfaces in IPv4-only networks present an unexpectedly and unnecessarily increased attack surface. The issue is patched in 26.0.2. To completely disable IPv6 in a container, use --sysctl=net.ipv6.conf.all.disable_ipv6=1 in the docker create or docker run command. Or, in the service configuration of a compose file.
AI Insight
LLM-synthesized narrative grounded in this CVE's description and references.
In Moby 26.0.0-26.0.1, IPv6 remains enabled on ipvlan/macvlan interfaces even with --ipv6=false, increasing attack surface.
Root
Cause In Moby versions 26.0.0 and 26.0.1, when creating networks with --ipv6=false, IPv6 is not disabled on the network interfaces of containers using ipvlan or macvlan drivers [1][3]. These interfaces share the host's external network link, and because IPv6 is enabled, containers can obtain link-local IPv6 addresses, receive SLAAC-assigned addresses via router advertisements, and join IPv6 multicast groups [3].
Exploitation
An attacker with access to a container on an IPv4-only ipvlan or macvlan network can leverage the unexpected IPv6 connectivity to communicate with other hosts on the local network over link-local IPv6 addresses [3]. Additionally, if the local network broadcasts router advertisements, the container may acquire a globally routable IPv6 address. The container's IPv6 interface also becomes a member of multicast groups. No authentication is required beyond container access; the attack surface is expanded due to the unintended IPv6 configuration.
Impact
The unintended IPv6 connectivity allows containers to listen for incoming connections on their IPv6 address, initiate outbound connections over IPv6, or launch denial-of-service attacks by flooding packets [3]. Since the container may not be subject to IPv6 firewall rules, there is an increased risk of data exfiltration (CVSS 4.7). A remote attacker on the local network could also send malicious router advertisements to redirect traffic (CVSS 4.5), potentially affecting availability. The overall CVSS score for the base issue is 2.7 (low severity), but the cumulative impact is significant for sensitive environments.
Mitigation
The vulnerability is patched in Moby 26.0.2 [1][3]. Users can also manually disable IPv6 in individual containers by adding --sysctl=net.ipv6.conf.all.disable_ipv6=1 to docker create or docker run, or in a Compose file's service configuration [1][3]. The commit implementing the fix is available in the Moby repository [4].
AI Insight generated on May 20, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
github.com/docker/dockerGo | >= 26.0.0, < 26.0.2 | 26.0.2 |
Affected products
99- osv-coords98 versionspkg:apk/chainguard/bufpkg:apk/chainguard/crictlpkg:apk/chainguard/critestpkg:apk/chainguard/cri-toolspkg:apk/chainguard/daggerpkg:apk/chainguard/dockerpkg:apk/chainguard/docker-composepkg:apk/chainguard/docker-config-mirror-gcrpkg:apk/chainguard/dockerdpkg:apk/chainguard/docker-dindpkg:apk/chainguard/docker-dind-compatpkg:apk/chainguard/dockerd-oci-entrypointpkg:apk/chainguard/dockerd-servicepkg:apk/chainguard/docker-initpkg:apk/chainguard/docker-modprobe-compatpkg:apk/chainguard/docker-oci-entrypointpkg:apk/chainguard/docker-rootlesspkg:apk/chainguard/grypepkg:apk/chainguard/harbor-scanner-trivypkg:apk/chainguard/harbor-scanner-trivy-fipspkg:apk/chainguard/helm-pushpkg:apk/chainguard/k3dpkg:apk/chainguard/k3d-proxypkg:apk/chainguard/k3d-toolspkg:apk/chainguard/kanikopkg:apk/chainguard/kaniko-compatpkg:apk/chainguard/kaniko-warmerpkg:apk/chainguard/kaniko-warmer-compatpkg:apk/chainguard/melangepkg:apk/chainguard/melange-microvm-initpkg:apk/chainguard/neuvector-scannerpkg:apk/chainguard/neuvector-scanner-monitorpkg:apk/chainguard/neuvector-scanner-taskpkg:apk/chainguard/policy-controllerpkg:apk/chainguard/policy-controller-fipspkg:apk/chainguard/policy-controller-testerpkg:apk/chainguard/policy-controller-tester-fipspkg:apk/chainguard/policy-controller-webhookpkg:apk/chainguard/prometheus-2.52pkg:apk/chainguard/prometheus-2.52-bitnami-compatpkg:apk/chainguard/syftpkg:apk/chainguard/tekton-pipelinespkg:apk/chainguard/tekton-pipelines-entrypointpkg:apk/chainguard/tekton-pipelines-eventspkg:apk/chainguard/tekton-pipelines-noppkg:apk/chainguard/tekton-pipelines-resolverspkg:apk/chainguard/tekton-pipelines-sidecarlogresultspkg:apk/chainguard/tekton-pipelines-webhookpkg:apk/chainguard/tekton-pipelines-workingdirinitpkg:apk/chainguard/wolfictlpkg:apk/wolfi/bufpkg:apk/wolfi/crictlpkg:apk/wolfi/critestpkg:apk/wolfi/cri-toolspkg:apk/wolfi/daggerpkg:apk/wolfi/dockerpkg:apk/wolfi/docker-composepkg:apk/wolfi/docker-config-mirror-gcrpkg:apk/wolfi/dockerdpkg:apk/wolfi/docker-dindpkg:apk/wolfi/docker-dind-compatpkg:apk/wolfi/dockerd-oci-entrypointpkg:apk/wolfi/dockerd-servicepkg:apk/wolfi/docker-initpkg:apk/wolfi/docker-modprobe-compatpkg:apk/wolfi/docker-oci-entrypointpkg:apk/wolfi/docker-rootlesspkg:apk/wolfi/grypepkg:apk/wolfi/harbor-scanner-trivypkg:apk/wolfi/helm-pushpkg:apk/wolfi/k3dpkg:apk/wolfi/k3d-proxypkg:apk/wolfi/k3d-toolspkg:apk/wolfi/kanikopkg:apk/wolfi/kaniko-compatpkg:apk/wolfi/kaniko-warmerpkg:apk/wolfi/kaniko-warmer-compatpkg:apk/wolfi/melangepkg:apk/wolfi/melange-microvm-initpkg:apk/wolfi/neuvector-scannerpkg:apk/wolfi/neuvector-scanner-monitorpkg:apk/wolfi/neuvector-scanner-taskpkg:apk/wolfi/policy-controllerpkg:apk/wolfi/policy-controller-testerpkg:apk/wolfi/policy-controller-webhookpkg:apk/wolfi/prometheus-2.52pkg:apk/wolfi/prometheus-2.52-bitnami-compatpkg:apk/wolfi/syftpkg:apk/wolfi/tekton-pipelinespkg:apk/wolfi/tekton-pipelines-entrypointpkg:apk/wolfi/tekton-pipelines-eventspkg:apk/wolfi/tekton-pipelines-noppkg:apk/wolfi/tekton-pipelines-resolverspkg:apk/wolfi/tekton-pipelines-sidecarlogresultspkg:apk/wolfi/tekton-pipelines-webhookpkg:apk/wolfi/tekton-pipelines-workingdirinitpkg:apk/wolfi/wolfictlpkg:golang/github.com/docker/docker
< 1.31.0-r0+ 97 more
- (no CPE)range: < 1.31.0-r0
- (no CPE)range: < 1.30.0-r1
- (no CPE)range: < 1.30.0-r1
- (no CPE)range: < 1.30.0-r1
- (no CPE)range: < 0.11.1-r1
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 2.26.1-r2
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 0.77.0-r1
- (no CPE)range: < 0.31.0-r1
- (no CPE)range: < 0.31.0-r1
- (no CPE)range: < 0.10.4-r8
- (no CPE)range: < 5.6.3-r1
- (no CPE)range: < 5.6.3-r1
- (no CPE)range: < 5.6.3-r1
- (no CPE)range: < 1.22.0-r2
- (no CPE)range: < 1.22.0-r2
- (no CPE)range: < 1.22.0-r2
- (no CPE)range: < 1.22.0-r2
- (no CPE)range: < 0.6.11-r3
- (no CPE)range: < 0.6.11-r3
- (no CPE)range: < 0_git20240417-r1
- (no CPE)range: < 0_git20240417-r1
- (no CPE)range: < 0_git20240417-r1
- (no CPE)range: < 0.9.0-r1
- (no CPE)range: < 0.9.0-r1
- (no CPE)range: < 0.9.0-r1
- (no CPE)range: < 0.9.0-r1
- (no CPE)range: < 0.9.0-r1
- (no CPE)range: < 2.52.0-r1
- (no CPE)range: < 2.52.0-r1
- (no CPE)range: < 1.2.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.15.19-r1
- (no CPE)range: < 1.31.0-r0
- (no CPE)range: < 1.30.0-r1
- (no CPE)range: < 1.30.0-r1
- (no CPE)range: < 1.30.0-r1
- (no CPE)range: < 0.11.1-r1
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 2.26.1-r2
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 26.0.2-r0
- (no CPE)range: < 0.77.0-r1
- (no CPE)range: < 0.31.0-r1
- (no CPE)range: < 0.10.4-r8
- (no CPE)range: < 5.6.3-r1
- (no CPE)range: < 5.6.3-r1
- (no CPE)range: < 5.6.3-r1
- (no CPE)range: < 1.22.0-r2
- (no CPE)range: < 1.22.0-r2
- (no CPE)range: < 1.22.0-r2
- (no CPE)range: < 1.22.0-r2
- (no CPE)range: < 0.6.11-r3
- (no CPE)range: < 0.6.11-r3
- (no CPE)range: < 0_git20240417-r1
- (no CPE)range: < 0_git20240417-r1
- (no CPE)range: < 0_git20240417-r1
- (no CPE)range: < 0.9.0-r1
- (no CPE)range: < 0.9.0-r1
- (no CPE)range: < 0.9.0-r1
- (no CPE)range: < 2.52.0-r1
- (no CPE)range: < 2.52.0-r1
- (no CPE)range: < 1.2.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.59.0-r1
- (no CPE)range: < 0.15.19-r1
- (no CPE)range: >= 26.0.0, < 26.0.2
- moby/mobyv5Range: >= 26.0.0, < 26.0.2
Patches
17cef0d9cd1cfMerge pull request from GHSA-x84c-p2g9-rqv9
4 files changed · +105 −10
integration/networking/bridge_test.go+37 −2 modified@@ -4,6 +4,7 @@ import ( "context" "fmt" "regexp" + "strings" "testing" "time" @@ -611,8 +612,8 @@ func TestInternalNwConnectivity(t *testing.T) { assert.Check(t, is.Contains(res.Stderr(), "Network is unreachable")) } -// Check that the container's interface has no IPv6 address when IPv6 is -// disabled in a container via sysctl. +// Check that the container's interfaces have no IPv6 address when IPv6 is +// disabled in a container via sysctl (including 'lo'). func TestDisableIPv6Addrs(t *testing.T) { skip.If(t, testEnv.DaemonInfo.OSType == "windows") @@ -676,6 +677,40 @@ func TestDisableIPv6Addrs(t *testing.T) { } } +// Check that an interface to an '--ipv6=false' network has no IPv6 +// address - either IPAM assigned, or kernel-assigned LL, but the loopback +// interface does still have an IPv6 address ('::1'). +func TestNonIPv6Network(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType == "windows") + + ctx := setupTest(t) + d := daemon.New(t) + d.StartWithBusybox(ctx, t) + defer d.Stop(t) + + c := d.NewClientT(t) + defer c.Close() + + const netName = "testnet" + network.CreateNoError(ctx, t, c, netName) + defer network.RemoveNoError(ctx, t, c, netName) + + id := container.Run(ctx, t, c, container.WithNetworkMode(netName)) + defer c.ContainerRemove(ctx, id, containertypes.RemoveOptions{Force: true}) + + loRes := container.ExecT(ctx, t, c, id, []string{"ip", "a", "show", "dev", "lo"}) + assert.Check(t, is.Contains(loRes.Combined(), " inet ")) + assert.Check(t, is.Contains(loRes.Combined(), " inet6 ")) + + eth0Res := container.ExecT(ctx, t, c, id, []string{"ip", "a", "show", "dev", "eth0"}) + assert.Check(t, is.Contains(eth0Res.Combined(), " inet ")) + assert.Check(t, !strings.Contains(eth0Res.Combined(), " inet6 "), + "result.Combined(): %s", eth0Res.Combined()) + + sysctlRes := container.ExecT(ctx, t, c, id, []string{"sysctl", "-n", "net.ipv6.conf.eth0.disable_ipv6"}) + assert.Check(t, is.Equal(strings.TrimSpace(sysctlRes.Combined()), "1")) +} + // Test that it's possible to set a sysctl on an interface in the container. // Regression test for https://github.com/moby/moby/issues/47619 func TestSetInterfaceSysctl(t *testing.T) {
integration/network/ipvlan/ipvlan_test.go+25 −1 modified@@ -94,6 +94,9 @@ func TestDockerNetworkIpvlan(t *testing.T) { }, { name: "L3Addressing", test: testIpvlanL3Addressing, + }, { + name: "NoIPv6", + test: testIpvlanNoIPv6, }, } { @@ -441,6 +444,28 @@ func testIpvlanL3Addressing(t *testing.T, ctx context.Context, client dclient.AP assert.Check(t, is.Contains(result.Combined(), "default dev eth0")) } +// Check that an ipvlan interface with '--ipv6=false' doesn't get kernel-assigned +// IPv6 addresses, but the loopback interface does still have an IPv6 address ('::1'). +func testIpvlanNoIPv6(t *testing.T, ctx context.Context, client dclient.APIClient) { + const netName = "ipvlannet" + net.CreateNoError(ctx, t, client, netName, net.WithIPvlan("", "l3")) + assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) + + id := container.Run(ctx, t, client, container.WithNetworkMode(netName)) + + loRes := container.ExecT(ctx, t, client, id, []string{"ip", "a", "show", "dev", "lo"}) + assert.Check(t, is.Contains(loRes.Combined(), " inet ")) + assert.Check(t, is.Contains(loRes.Combined(), " inet6 ")) + + eth0Res := container.ExecT(ctx, t, client, id, []string{"ip", "a", "show", "dev", "eth0"}) + assert.Check(t, is.Contains(eth0Res.Combined(), " inet ")) + assert.Check(t, !strings.Contains(eth0Res.Combined(), " inet6 "), + "result.Combined(): %s", eth0Res.Combined()) + + sysctlRes := container.ExecT(ctx, t, client, id, []string{"sysctl", "-n", "net.ipv6.conf.eth0.disable_ipv6"}) + assert.Check(t, is.Equal(strings.TrimSpace(sysctlRes.Combined()), "1")) +} + // TestIPVlanDNS checks whether DNS is forwarded, for combinations of l2/l3 mode, // with/without a parent interface, and with '--internal'. Note that, there's no // attempt here to give the ipvlan network external connectivity - when this test @@ -452,7 +477,6 @@ func testIpvlanL3Addressing(t *testing.T, ctx context.Context, client dclient.AP // https://github.com/moby/moby/issues/47662 func TestIPVlanDNS(t *testing.T) { skip.If(t, testEnv.IsRootless, "rootless mode has different view of network") - ctx := testutil.StartSpan(baseContext, t) net.StartDaftDNS(t, "127.0.0.1")
integration/network/macvlan/macvlan_test.go+29 −0 modified@@ -77,6 +77,9 @@ func TestDockerNetworkMacvlan(t *testing.T) { }, { name: "Addressing", test: testMacvlanAddressing, + }, { + name: "NoIPv6", + test: testMacvlanNoIPv6, }, } { tc := tc @@ -298,6 +301,32 @@ func testMacvlanAddressing(t *testing.T, ctx context.Context, client client.APIC assert.Check(t, strings.Contains(result.Combined(), "default via 2001:db8:abca::254 dev eth0")) } +// Check that a macvlan interface with '--ipv6=false' doesn't get kernel-assigned +// IPv6 addresses, but the loopback interface does still have an IPv6 address ('::1'). +func testMacvlanNoIPv6(t *testing.T, ctx context.Context, client client.APIClient) { + const netName = "macvlannet" + + net.CreateNoError(ctx, t, client, netName, + net.WithMacvlan(""), + net.WithOption("macvlan_mode", "bridge"), + ) + assert.Check(t, n.IsNetworkAvailable(ctx, client, netName)) + + id := container.Run(ctx, t, client, container.WithNetworkMode(netName)) + + loRes := container.ExecT(ctx, t, client, id, []string{"ip", "a", "show", "dev", "lo"}) + assert.Check(t, is.Contains(loRes.Combined(), " inet ")) + assert.Check(t, is.Contains(loRes.Combined(), " inet6 ")) + + eth0Res := container.ExecT(ctx, t, client, id, []string{"ip", "a", "show", "dev", "eth0"}) + assert.Check(t, is.Contains(eth0Res.Combined(), " inet ")) + assert.Check(t, !strings.Contains(eth0Res.Combined(), " inet6 "), + "result.Combined(): %s", eth0Res.Combined()) + + sysctlRes := container.ExecT(ctx, t, client, id, []string{"sysctl", "-n", "net.ipv6.conf.eth0.disable_ipv6"}) + assert.Check(t, is.Equal(strings.TrimSpace(sysctlRes.Combined()), "1")) +} + // TestMACVlanDNS checks whether DNS is forwarded, with/without a parent // interface, and with '--internal'. Note that there's no attempt here to give // the macvlan network external connectivity - when this test supplies a parent
libnetwork/osl/interface_linux.go+14 −7 modified@@ -363,17 +363,24 @@ func setInterfaceIP(nlh *netlink.Handle, iface netlink.Link, i *Interface) error } func setInterfaceIPv6(nlh *netlink.Handle, iface netlink.Link, i *Interface) error { - if i.AddressIPv6() == nil { + addr := i.AddressIPv6() + // IPv6 must be enabled on the interface if and only if the network is + // IPv6-enabled. For an interface on an IPv4-only network, if IPv6 isn't + // disabled, the interface will be put into IPv6 multicast groups making + // it unexpectedly susceptible to NDP cache poisoning, route injection, etc. + // (At present, there will always be a pre-configured IPv6 address if the + // network is IPv6-enabled.) + if err := setIPv6(i.ns.path, i.DstName(), addr != nil); err != nil { + return fmt.Errorf("failed to configure ipv6: %v", err) + } + if addr == nil { return nil } - if err := checkRouteConflict(nlh, i.AddressIPv6(), netlink.FAMILY_V6); err != nil { + if err := checkRouteConflict(nlh, addr, netlink.FAMILY_V6); err != nil { return err } - if err := setIPv6(i.ns.path, i.DstName(), true); err != nil { - return fmt.Errorf("failed to enable ipv6: %v", err) - } - ipAddr := &netlink.Addr{IPNet: i.AddressIPv6(), Label: "", Flags: syscall.IFA_F_NODAD} - return nlh.AddrAdd(iface, ipAddr) + nlAddr := &netlink.Addr{IPNet: addr, Label: "", Flags: syscall.IFA_F_NODAD} + return nlh.AddrAdd(iface, nlAddr) } func setInterfaceLinkLocalIPs(nlh *netlink.Handle, iface netlink.Link, i *Interface) error {
Vulnerability mechanics
Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
4- github.com/advisories/GHSA-x84c-p2g9-rqv9ghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2024-32473ghsaADVISORY
- github.com/moby/moby/commit/7cef0d9cd1cf221d8c0b7b7aeda69552649e0642ghsax_refsource_MISCWEB
- github.com/moby/moby/security/advisories/GHSA-x84c-p2g9-rqv9ghsax_refsource_CONFIRMWEB
News mentions
0No linked articles in our index yet.