Merge pull request from GHSA-x84c-p2g9-rqv9
Disable IPv6 for endpoints in '--ipv6=false' networks.
This commit is contained in:
commit
82d8f8d6e6
4 changed files with 105 additions and 10 deletions
|
@ -94,6 +94,9 @@ func TestDockerNetworkIpvlan(t *testing.T) {
|
||||||
}, {
|
}, {
|
||||||
name: "L3Addressing",
|
name: "L3Addressing",
|
||||||
test: testIpvlanL3Addressing,
|
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"))
|
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,
|
// 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
|
// 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
|
// 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
|
// https://github.com/moby/moby/issues/47662
|
||||||
func TestIPVlanDNS(t *testing.T) {
|
func TestIPVlanDNS(t *testing.T) {
|
||||||
skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
|
skip.If(t, testEnv.IsRootless, "rootless mode has different view of network")
|
||||||
|
|
||||||
ctx := testutil.StartSpan(baseContext, t)
|
ctx := testutil.StartSpan(baseContext, t)
|
||||||
|
|
||||||
net.StartDaftDNS(t, "127.0.0.1")
|
net.StartDaftDNS(t, "127.0.0.1")
|
||||||
|
|
|
@ -77,6 +77,9 @@ func TestDockerNetworkMacvlan(t *testing.T) {
|
||||||
}, {
|
}, {
|
||||||
name: "Addressing",
|
name: "Addressing",
|
||||||
test: testMacvlanAddressing,
|
test: testMacvlanAddressing,
|
||||||
|
}, {
|
||||||
|
name: "NoIPv6",
|
||||||
|
test: testMacvlanNoIPv6,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
tc := tc
|
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"))
|
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
|
// TestMACVlanDNS checks whether DNS is forwarded, with/without a parent
|
||||||
// interface, and with '--internal'. Note that there's no attempt here to give
|
// interface, and with '--internal'. Note that there's no attempt here to give
|
||||||
// the macvlan network external connectivity - when this test supplies a parent
|
// the macvlan network external connectivity - when this test supplies a parent
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -611,8 +612,8 @@ func TestInternalNwConnectivity(t *testing.T) {
|
||||||
assert.Check(t, is.Contains(res.Stderr(), "Network is unreachable"))
|
assert.Check(t, is.Contains(res.Stderr(), "Network is unreachable"))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check that the container's interface has no IPv6 address when IPv6 is
|
// Check that the container's interfaces have no IPv6 address when IPv6 is
|
||||||
// disabled in a container via sysctl.
|
// disabled in a container via sysctl (including 'lo').
|
||||||
func TestDisableIPv6Addrs(t *testing.T) {
|
func TestDisableIPv6Addrs(t *testing.T) {
|
||||||
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
|
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.
|
// 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
|
// Regression test for https://github.com/moby/moby/issues/47619
|
||||||
func TestSetInterfaceSysctl(t *testing.T) {
|
func TestSetInterfaceSysctl(t *testing.T) {
|
||||||
|
|
|
@ -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 {
|
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
|
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
|
return err
|
||||||
}
|
}
|
||||||
if err := setIPv6(i.ns.path, i.DstName(), true); err != nil {
|
nlAddr := &netlink.Addr{IPNet: addr, Label: "", Flags: syscall.IFA_F_NODAD}
|
||||||
return fmt.Errorf("failed to enable ipv6: %v", err)
|
return nlh.AddrAdd(iface, nlAddr)
|
||||||
}
|
|
||||||
ipAddr := &netlink.Addr{IPNet: i.AddressIPv6(), Label: "", Flags: syscall.IFA_F_NODAD}
|
|
||||||
return nlh.AddrAdd(iface, ipAddr)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setInterfaceLinkLocalIPs(nlh *netlink.Handle, iface netlink.Link, i *Interface) error {
|
func setInterfaceLinkLocalIPs(nlh *netlink.Handle, iface netlink.Link, i *Interface) error {
|
||||||
|
|
Loading…
Reference in a new issue