Skip to content

Commit 9f1e3ae

Browse files
authored
Merge pull request moby#49861 from robmry/bridge_test_firewaller
Unit test the bridge driver in terms of its firewaller
2 parents 294f0c3 + ba0ad9e commit 9f1e3ae

File tree

5 files changed

+280
-153
lines changed

5 files changed

+280
-153
lines changed

libnetwork/drivers/bridge/bridge_linux.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ func (d *driver) configure(option map[string]interface{}) error {
509509
}
510510

511511
var err error
512-
d.firewaller, err = iptabler.NewIptabler(context.Background(), firewaller.Config{
512+
d.firewaller, err = newFirewaller(context.Background(), firewaller.Config{
513513
IPv4: config.EnableIPTables,
514514
IPv6: config.EnableIP6Tables,
515515
Hairpin: !config.EnableUserlandProxy || config.UserlandProxyPath == "",
@@ -537,6 +537,10 @@ func (d *driver) configure(option map[string]interface{}) error {
537537
return d.initStore()
538538
}
539539

540+
var newFirewaller = func(ctx context.Context, config firewaller.Config) (firewaller.Firewaller, error) {
541+
return iptabler.NewIptabler(ctx, config)
542+
}
543+
540544
func (d *driver) getNetwork(id string) (*bridgeNetwork, error) {
541545
d.Lock()
542546
defer d.Unlock()

libnetwork/drivers/bridge/bridge_linux_test.go

Lines changed: 51 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,23 @@ import (
55
"context"
66
"encoding/json"
77
"fmt"
8+
"maps"
89
"net"
10+
"net/netip"
911
"os/exec"
10-
"regexp"
12+
"slices"
1113
"strconv"
1214
"testing"
1315

1416
"github.com/docker/docker/internal/nlwrap"
1517
"github.com/docker/docker/internal/testutils/netnsutils"
1618
"github.com/docker/docker/internal/testutils/storeutils"
1719
"github.com/docker/docker/libnetwork/driverapi"
20+
"github.com/docker/docker/libnetwork/drivers/bridge/internal/firewaller"
1821
"github.com/docker/docker/libnetwork/internal/netiputil"
1922
"github.com/docker/docker/libnetwork/ipamapi"
2023
"github.com/docker/docker/libnetwork/ipams/defaultipam"
2124
"github.com/docker/docker/libnetwork/ipamutils"
22-
"github.com/docker/docker/libnetwork/iptables"
2325
"github.com/docker/docker/libnetwork/netlabel"
2426
"github.com/docker/docker/libnetwork/netutils"
2527
"github.com/docker/docker/libnetwork/options"
@@ -33,13 +35,6 @@ import (
3335
"gotest.tools/v3/icmd"
3436
)
3537

36-
const (
37-
// FIXME(robmry) - remove these and make the tests work for non-iptables firewalls.
38-
dockerChain = "DOCKER"
39-
isolationChain1 = "DOCKER-ISOLATION-STAGE-1"
40-
isolationChain2 = "DOCKER-ISOLATION-STAGE-2"
41-
)
42-
4338
func TestEndpointMarshalling(t *testing.T) {
4439
ip1, _ := types.ParseCIDR("172.22.0.9/16")
4540
ip2, _ := types.ParseCIDR("2001:db8::9")
@@ -582,9 +577,22 @@ func TestCreateFail(t *testing.T) {
582577

583578
func TestCreateMultipleNetworks(t *testing.T) {
584579
defer netnsutils.SetupTestOSContext(t)()
580+
useStubFirewaller(t)
585581

586582
d := newDriver(storeutils.NewTempStore(t))
587583

584+
checkFirewallerNetworks := func() {
585+
t.Helper()
586+
fw := d.firewaller.(*firewaller.StubFirewaller)
587+
got := maps.Clone(fw.Networks)
588+
for _, wantNw := range d.networks {
589+
_, ok := got[wantNw.config.BridgeName]
590+
assert.Check(t, ok, "Rules for bridge %s (nid:%s) have been deleted", wantNw.config.BridgeName, wantNw.id)
591+
delete(got, wantNw.config.BridgeName)
592+
}
593+
assert.Check(t, is.Len(slices.Collect(maps.Keys(got)), 0), "Rules for bridges have not been deleted")
594+
}
595+
588596
config := &configuration{
589597
EnableIPTables: true,
590598
}
@@ -601,78 +609,48 @@ func TestCreateMultipleNetworks(t *testing.T) {
601609
if err := d.CreateNetwork(context.Background(), "1", genericOption, nil, getIPv4Data(t), nil); err != nil {
602610
t.Fatalf("Failed to create bridge: %v", err)
603611
}
604-
605-
verifyV4INCEntries(d.networks, t)
612+
checkFirewallerNetworks()
606613

607614
config2 := &networkConfiguration{BridgeName: "net_test_2", EnableIPv4: true}
608615
genericOption[netlabel.GenericData] = config2
609616
if err := d.CreateNetwork(context.Background(), "2", genericOption, nil, getIPv4Data(t), nil); err != nil {
610617
t.Fatalf("Failed to create bridge: %v", err)
611618
}
612-
613-
verifyV4INCEntries(d.networks, t)
619+
checkFirewallerNetworks()
614620

615621
config3 := &networkConfiguration{BridgeName: "net_test_3", EnableIPv4: true}
616622
genericOption[netlabel.GenericData] = config3
617623
if err := d.CreateNetwork(context.Background(), "3", genericOption, nil, getIPv4Data(t), nil); err != nil {
618624
t.Fatalf("Failed to create bridge: %v", err)
619625
}
620-
621-
verifyV4INCEntries(d.networks, t)
626+
checkFirewallerNetworks()
622627

623628
config4 := &networkConfiguration{BridgeName: "net_test_4", EnableIPv4: true}
624629
genericOption[netlabel.GenericData] = config4
625630
if err := d.CreateNetwork(context.Background(), "4", genericOption, nil, getIPv4Data(t), nil); err != nil {
626631
t.Fatalf("Failed to create bridge: %v", err)
627632
}
628-
629-
verifyV4INCEntries(d.networks, t)
633+
checkFirewallerNetworks()
630634

631635
if err := d.DeleteNetwork("1"); err != nil {
632636
t.Log(err)
633637
}
634-
verifyV4INCEntries(d.networks, t)
638+
checkFirewallerNetworks()
635639

636640
if err := d.DeleteNetwork("2"); err != nil {
637641
t.Log(err)
638642
}
639-
verifyV4INCEntries(d.networks, t)
643+
checkFirewallerNetworks()
640644

641645
if err := d.DeleteNetwork("3"); err != nil {
642646
t.Log(err)
643647
}
644-
verifyV4INCEntries(d.networks, t)
648+
checkFirewallerNetworks()
645649

646650
if err := d.DeleteNetwork("4"); err != nil {
647651
t.Log(err)
648652
}
649-
verifyV4INCEntries(d.networks, t)
650-
}
651-
652-
// Verify the network isolation rules are installed for each network
653-
func verifyV4INCEntries(networks map[string]*bridgeNetwork, t *testing.T) {
654-
iptable := iptables.GetIptable(iptables.IPv4)
655-
out1, err := iptable.Raw("-S", isolationChain1)
656-
if err != nil {
657-
t.Fatal(err)
658-
}
659-
out2, err := iptable.Raw("-S", isolationChain2)
660-
if err != nil {
661-
t.Fatal(err)
662-
}
663-
664-
for _, n := range networks {
665-
re := regexp.MustCompile(fmt.Sprintf("-i %s ! -o %s -j %s", n.config.BridgeName, n.config.BridgeName, isolationChain2))
666-
matches := re.FindAllString(string(out1[:]), -1)
667-
if len(matches) != 1 {
668-
t.Fatalf("Cannot find expected inter-network isolation rules in IP Tables for network %s:\n%s.", n.id, string(out1[:]))
669-
}
670-
re = regexp.MustCompile(fmt.Sprintf("-o %s -j DROP", n.config.BridgeName))
671-
matches = re.FindAllString(string(out2[:]), -1)
672-
if len(matches) != 1 {
673-
t.Fatalf("Cannot find expected inter-network isolation rules in IP Tables for network %s:\n%s.", n.id, string(out2[:]))
674-
}
675-
}
653+
checkFirewallerNetworks()
676654
}
677655

678656
type testInterface struct {
@@ -812,6 +790,8 @@ func TestQueryEndpointInfoHairpin(t *testing.T) {
812790

813791
func testQueryEndpointInfo(t *testing.T, ulPxyEnabled bool) {
814792
defer netnsutils.SetupTestOSContext(t)()
793+
useStubFirewaller(t)
794+
815795
d := newDriver(storeutils.NewTempStore(t))
816796
portallocator.Get().ReleaseAll()
817797

@@ -924,9 +904,9 @@ func getPortMapping() []types.PortBinding {
924904

925905
func TestLinkContainers(t *testing.T) {
926906
defer netnsutils.SetupTestOSContext(t)()
907+
useStubFirewaller(t)
927908

928909
d := newDriver(storeutils.NewTempStore(t))
929-
iptable := iptables.GetIptable(iptables.IPv4)
930910

931911
config := &configuration{
932912
EnableIPTables: true,
@@ -1003,47 +983,29 @@ func TestLinkContainers(t *testing.T) {
1003983
t.Fatalf("Failed to program external connectivity: %v", err)
1004984
}
1005985

1006-
out, _ := iptable.Raw("-L", dockerChain)
1007-
for _, pm := range exposedPorts {
1008-
regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port)
1009-
re := regexp.MustCompile(regex)
1010-
matches := re.FindAllString(string(out[:]), -1)
1011-
if len(matches) != 1 {
1012-
t.Fatalf("IP Tables programming failed %s", string(out[:]))
1013-
}
1014-
1015-
regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port)
1016-
matched, _ := regexp.Match(regex, out[:])
1017-
if !matched {
1018-
t.Fatalf("IP Tables programming failed %s", string(out[:]))
1019-
}
986+
checkLink := func(expExists bool) {
987+
t.Helper()
988+
dnw, ok := d.networks["net1"]
989+
assert.Assert(t, ok)
990+
fnw := dnw.firewallerNetwork.(*firewaller.StubFirewallerNetwork)
991+
parentAddr, ok := netip.AddrFromSlice(te2.iface.addr.IP)
992+
assert.Assert(t, ok)
993+
childAddr, ok := netip.AddrFromSlice(te1.iface.addr.IP)
994+
assert.Assert(t, ok)
995+
exists := fnw.LinkExists(parentAddr, childAddr, exposedPorts)
996+
assert.Check(t, is.Equal(exists, expExists))
1020997
}
998+
checkLink(true)
1021999

10221000
err = d.RevokeExternalConnectivity("net1", "ep2")
10231001
if err != nil {
10241002
t.Fatalf("Failed to revoke external connectivity: %v", err)
10251003
}
1026-
10271004
err = d.Leave("net1", "ep2")
10281005
if err != nil {
10291006
t.Fatal("Failed to unlink ep1 and ep2")
10301007
}
1031-
1032-
out, _ = iptable.Raw("-L", dockerChain)
1033-
for _, pm := range exposedPorts {
1034-
regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port)
1035-
re := regexp.MustCompile(regex)
1036-
matches := re.FindAllString(string(out[:]), -1)
1037-
if len(matches) != 0 {
1038-
t.Fatalf("Leave should have deleted relevant IPTables rules %s", string(out[:]))
1039-
}
1040-
1041-
regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port)
1042-
matched, _ := regexp.Match(regex, out[:])
1043-
if matched {
1044-
t.Fatalf("Leave should have deleted relevant IPTables rules %s", string(out[:]))
1045-
}
1046-
}
1008+
checkLink(false)
10471009

10481010
// Error condition test with an invalid endpoint-id "ep4"
10491011
sbOptions = make(map[string]interface{})
@@ -1056,25 +1018,8 @@ func TestLinkContainers(t *testing.T) {
10561018
t.Fatal(err)
10571019
}
10581020
err = d.ProgramExternalConnectivity(context.Background(), "net1", "ep2", sbOptions)
1059-
if err != nil {
1060-
out, _ = iptable.Raw("-L", dockerChain)
1061-
for _, pm := range exposedPorts {
1062-
regex := fmt.Sprintf("%s dpt:%d", pm.Proto.String(), pm.Port)
1063-
re := regexp.MustCompile(regex)
1064-
matches := re.FindAllString(string(out[:]), -1)
1065-
if len(matches) != 0 {
1066-
t.Fatalf("Error handling should rollback relevant IPTables rules %s", string(out[:]))
1067-
}
1068-
1069-
regex = fmt.Sprintf("%s spt:%d", pm.Proto.String(), pm.Port)
1070-
matched, _ := regexp.Match(regex, out[:])
1071-
if matched {
1072-
t.Fatalf("Error handling should rollback relevant IPTables rules %s", string(out[:]))
1073-
}
1074-
}
1075-
} else {
1076-
t.Fatal("Expected Join to fail given link conditions are not satisfied")
1077-
}
1021+
assert.Check(t, err != nil, "Expected Join to fail given link conditions are not satisfied")
1022+
checkLink(false)
10781023
}
10791024

10801025
func TestValidateConfig(t *testing.T) {
@@ -1396,6 +1341,14 @@ func TestCreateParallel(t *testing.T) {
13961341
}
13971342
}
13981343

1344+
func useStubFirewaller(t *testing.T) {
1345+
origNewFirewaller := newFirewaller
1346+
newFirewaller = func(_ context.Context, config firewaller.Config) (firewaller.Firewaller, error) {
1347+
return firewaller.NewStubFirewaller(config), nil
1348+
}
1349+
t.Cleanup(func() { newFirewaller = origNewFirewaller })
1350+
}
1351+
13991352
// Regression test for https://github.com/moby/moby/issues/46445
14001353
func TestSetupIP6TablesWithHostIPv4(t *testing.T) {
14011354
defer netnsutils.SetupTestOSContext(t)()

0 commit comments

Comments
 (0)