Make 'internal' bridge networks accessible from host

Prior to release 25.0.0, the bridge in an internal network was assigned
an IP address - making the internal network accessible from the host,
giving containers on the network access to anything listening on the
bridge's address (or INADDR_ANY on the host).

This change restores that behaviour. It does not restore the default
route that was configured in the container, because packets sent outside
the internal network's subnet have always been dropped. So, a 'connect()'
to an address outside the subnet will still fail fast.

Signed-off-by: Rob Murray <rob.murray@docker.com>
(cherry picked from commit 419f5a6372)
Signed-off-by: Albin Kerouanton <albinker@gmail.com>
This commit is contained in:
Rob Murray 2024-02-07 18:53:27 +00:00 committed by Albin Kerouanton
parent 00b2e1072b
commit c761353e7c
3 changed files with 63 additions and 8 deletions

View file

@ -477,3 +477,60 @@ func TestDefaultBridgeAddresses(t *testing.T) {
})
}
}
// Test that a container on an 'internal' network has IP connectivity with
// the host (on its own subnet, because the n/w bridge has an address on that
// subnet, and it's in the host's namespace).
// Regression test for https://github.com/moby/moby/issues/47329
func TestInternalNwConnectivity(t *testing.T) {
skip.If(t, testEnv.DaemonInfo.OSType == "windows")
ctx := setupTest(t)
d := daemon.New(t)
d.StartWithBusybox(ctx, t, "-D", "--experimental", "--ip6tables")
defer d.Stop(t)
c := d.NewClientT(t)
defer c.Close()
const bridgeName = "intnw"
const gw4 = "172.30.0.1"
const gw6 = "fda9:4130:4715::1234"
network.CreateNoError(ctx, t, c, bridgeName,
network.WithInternal(),
network.WithIPv6(),
network.WithIPAM("172.30.0.0/24", gw4),
network.WithIPAM("fda9:4130:4715::/64", gw6),
network.WithDriver("bridge"),
network.WithOption("com.docker.network.bridge.name", bridgeName),
)
defer network.RemoveNoError(ctx, t, c, bridgeName)
const ctrName = "intctr"
id := container.Run(ctx, t, c,
container.WithName(ctrName),
container.WithImage("busybox:latest"),
container.WithCmd("top"),
container.WithNetworkMode(bridgeName),
)
defer c.ContainerRemove(ctx, id, containertypes.RemoveOptions{Force: true})
execCtx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
res := container.ExecT(execCtx, t, c, id, []string{"ping", "-c1", "-W3", gw4})
assert.Check(t, is.Equal(res.ExitCode, 0))
assert.Check(t, is.Equal(res.Stderr(), ""))
assert.Check(t, is.Contains(res.Stdout(), "1 packets transmitted, 1 packets received"))
res = container.ExecT(execCtx, t, c, id, []string{"ping6", "-c1", "-W3", gw6})
assert.Check(t, is.Equal(res.ExitCode, 0))
assert.Check(t, is.Equal(res.Stderr(), ""))
assert.Check(t, is.Contains(res.Stdout(), "1 packets transmitted, 1 packets received"))
// Addresses outside the internal subnet must not be accessible.
res = container.ExecT(execCtx, t, c, id, []string{"ping", "-c1", "-W3", "8.8.8.8"})
assert.Check(t, is.Equal(res.ExitCode, 1))
assert.Check(t, is.Contains(res.Stderr(), "Network is unreachable"))
}

View file

@ -32,10 +32,6 @@ func setupBridgeIPv4(config *networkConfiguration, i *bridgeInterface) error {
// are decoupled, we should assign it only when it's really needed.
i.bridgeIPv4 = config.AddressIPv4
if config.Internal {
return nil
}
if !config.InhibitIPv4 {
addrv4List, err := i.addresses(netlink.FAMILY_V4)
if err != nil {
@ -57,8 +53,10 @@ func setupBridgeIPv4(config *networkConfiguration, i *bridgeInterface) error {
}
}
// Store the default gateway
i.gatewayIPv4 = config.AddressIPv4.IP
if !config.Internal {
// Store the default gateway
i.gatewayIPv4 = config.AddressIPv4.IP
}
return nil
}

View file

@ -20,12 +20,12 @@ func setupVerifyAndReconcileIPv4(config *networkConfiguration, i *bridgeInterfac
addrv4, _ := selectIPv4Address(addrsv4, config.AddressIPv4)
// Verify that the bridge has an IPv4 address.
if !config.Internal && addrv4.IPNet == nil {
if addrv4.IPNet == nil {
return &ErrNoIPAddr{}
}
// Verify that the bridge IPv4 address matches the requested configuration.
if config.AddressIPv4 != nil && addrv4.IPNet != nil && !addrv4.IP.Equal(config.AddressIPv4.IP) {
if config.AddressIPv4 != nil && !addrv4.IP.Equal(config.AddressIPv4.IP) {
return &IPv4AddrNoMatchError{IP: addrv4.IP, CfgIP: config.AddressIPv4.IP}
}