github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/libnetwork/drivers/bridge/setup_ip_tables_linux_test.go (about)

     1  package bridge
     2  
     3  import (
     4  	"net"
     5  	"testing"
     6  
     7  	"github.com/Prakhar-Agarwal-byte/moby/internal/testutils/netnsutils"
     8  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/driverapi"
     9  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/iptables"
    10  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/netlabel"
    11  	"github.com/Prakhar-Agarwal-byte/moby/libnetwork/portmapper"
    12  	"github.com/vishvananda/netlink"
    13  	"gotest.tools/v3/assert"
    14  )
    15  
    16  const (
    17  	iptablesTestBridgeIP = "192.168.42.1"
    18  )
    19  
    20  // A testRegisterer implements the driverapi.Registerer interface.
    21  type testRegisterer struct {
    22  	t *testing.T
    23  	d *driver
    24  }
    25  
    26  func (r *testRegisterer) RegisterDriver(name string, di driverapi.Driver, _ driverapi.Capability) error {
    27  	if got, want := name, "bridge"; got != want {
    28  		r.t.Fatalf("got driver name %s, want %s", got, want)
    29  	}
    30  	d, ok := di.(*driver)
    31  	if !ok {
    32  		r.t.Fatalf("got driver type %T, want %T", di, &driver{})
    33  	}
    34  	r.d = d
    35  	return nil
    36  }
    37  
    38  func TestProgramIPTable(t *testing.T) {
    39  	// Create a test bridge with a basic bridge configuration (name + IPv4).
    40  	defer netnsutils.SetupTestOSContext(t)()
    41  
    42  	nh, err := netlink.NewHandle()
    43  	if err != nil {
    44  		t.Fatal(err)
    45  	}
    46  
    47  	createTestBridge(getBasicTestConfig(), &bridgeInterface{nlh: nh}, t)
    48  
    49  	// Store various iptables chain rules we care for.
    50  	rules := []struct {
    51  		rule  iptRule
    52  		descr string
    53  	}{
    54  		{iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-d", "127.1.2.3", "-i", "lo", "-o", "lo", "-j", "DROP"}}, "Test Loopback"},
    55  		{iptRule{ipv: iptables.IPv4, table: iptables.Nat, chain: "POSTROUTING", args: []string{"-s", iptablesTestBridgeIP, "!", "-o", DefaultBridgeName, "-j", "MASQUERADE"}}, "NAT Test"},
    56  		{iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-o", DefaultBridgeName, "-m", "conntrack", "--ctstate", "RELATED,ESTABLISHED", "-j", "ACCEPT"}}, "Test ACCEPT INCOMING"},
    57  		{iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "!", "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test ACCEPT NON_ICC OUTGOING"},
    58  		{iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "ACCEPT"}}, "Test enable ICC"},
    59  		{iptRule{ipv: iptables.IPv4, table: iptables.Filter, chain: "FORWARD", args: []string{"-i", DefaultBridgeName, "-o", DefaultBridgeName, "-j", "DROP"}}, "Test disable ICC"},
    60  	}
    61  
    62  	// Assert the chain rules' insertion and removal.
    63  	for _, c := range rules {
    64  		assertIPTableChainProgramming(c.rule, c.descr, t)
    65  	}
    66  }
    67  
    68  func TestSetupIPChains(t *testing.T) {
    69  	// Create a test bridge with a basic bridge configuration (name + IPv4).
    70  	defer netnsutils.SetupTestOSContext(t)()
    71  
    72  	nh, err := netlink.NewHandle()
    73  	if err != nil {
    74  		t.Fatal(err)
    75  	}
    76  
    77  	driverconfig := configuration{
    78  		EnableIPTables: true,
    79  	}
    80  	d := &driver{
    81  		config: driverconfig,
    82  	}
    83  	assertChainConfig(d, t)
    84  
    85  	config := getBasicTestConfig()
    86  	br := &bridgeInterface{nlh: nh}
    87  	createTestBridge(config, br, t)
    88  
    89  	assertBridgeConfig(config, br, d, t)
    90  
    91  	config.EnableIPMasquerade = true
    92  	assertBridgeConfig(config, br, d, t)
    93  
    94  	config.EnableICC = true
    95  	assertBridgeConfig(config, br, d, t)
    96  
    97  	config.EnableIPMasquerade = false
    98  	assertBridgeConfig(config, br, d, t)
    99  }
   100  
   101  func getBasicTestConfig() *networkConfiguration {
   102  	config := &networkConfiguration{
   103  		BridgeName:  DefaultBridgeName,
   104  		AddressIPv4: &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)},
   105  	}
   106  	return config
   107  }
   108  
   109  func createTestBridge(config *networkConfiguration, br *bridgeInterface, t *testing.T) {
   110  	if err := setupDevice(config, br); err != nil {
   111  		t.Fatalf("Failed to create the testing Bridge: %s", err.Error())
   112  	}
   113  	if err := setupBridgeIPv4(config, br); err != nil {
   114  		t.Fatalf("Failed to bring up the testing Bridge: %s", err.Error())
   115  	}
   116  	if config.EnableIPv6 {
   117  		if err := setupBridgeIPv6(config, br); err != nil {
   118  			t.Fatalf("Failed to bring up the testing Bridge: %s", err.Error())
   119  		}
   120  	}
   121  }
   122  
   123  // Assert base function which pushes iptables chain rules on insertion and removal.
   124  func assertIPTableChainProgramming(rule iptRule, descr string, t *testing.T) {
   125  	// Add
   126  	if err := programChainRule(rule, descr, true); err != nil {
   127  		t.Fatalf("Failed to program iptable rule %s: %s", descr, err.Error())
   128  	}
   129  
   130  	if !rule.Exists() {
   131  		t.Fatalf("Failed to effectively program iptable rule: %s", descr)
   132  	}
   133  
   134  	// Remove
   135  	if err := programChainRule(rule, descr, false); err != nil {
   136  		t.Fatalf("Failed to remove iptable rule %s: %s", descr, err.Error())
   137  	}
   138  	if rule.Exists() {
   139  		t.Fatalf("Failed to effectively remove iptable rule: %s", descr)
   140  	}
   141  }
   142  
   143  // Assert function which create chains.
   144  func assertChainConfig(d *driver, t *testing.T) {
   145  	var err error
   146  
   147  	d.natChain, d.filterChain, d.isolationChain1, d.isolationChain2, err = setupIPChains(d.config, iptables.IPv4)
   148  	if err != nil {
   149  		t.Fatal(err)
   150  	}
   151  	if d.config.EnableIP6Tables {
   152  		d.natChainV6, d.filterChainV6, d.isolationChain1V6, d.isolationChain2V6, err = setupIPChains(d.config, iptables.IPv6)
   153  		if err != nil {
   154  			t.Fatal(err)
   155  		}
   156  	}
   157  }
   158  
   159  // Assert function which pushes chains based on bridge config parameters.
   160  func assertBridgeConfig(config *networkConfiguration, br *bridgeInterface, d *driver, t *testing.T) {
   161  	nw := bridgeNetwork{
   162  		portMapper:   portmapper.New(),
   163  		portMapperV6: portmapper.New(),
   164  		config:       config,
   165  	}
   166  	nw.driver = d
   167  
   168  	// Attempt programming of ip tables.
   169  	err := nw.setupIP4Tables(config, br)
   170  	if err != nil {
   171  		t.Fatalf("%v", err)
   172  	}
   173  	if d.config.EnableIP6Tables {
   174  		if err := nw.setupIP6Tables(config, br); err != nil {
   175  			t.Fatalf("%v", err)
   176  		}
   177  	}
   178  }
   179  
   180  // Regression test for https://github.com/moby/moby/issues/46445
   181  func TestSetupIP6TablesWithHostIPv4(t *testing.T) {
   182  	defer netnsutils.SetupTestOSContext(t)()
   183  	d := newDriver()
   184  	dc := &configuration{
   185  		EnableIPTables:  true,
   186  		EnableIP6Tables: true,
   187  	}
   188  	if err := d.configure(map[string]interface{}{netlabel.GenericData: dc}); err != nil {
   189  		t.Fatal(err)
   190  	}
   191  	nc := &networkConfiguration{
   192  		BridgeName:         DefaultBridgeName,
   193  		AddressIPv4:        &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)},
   194  		EnableIPMasquerade: true,
   195  		EnableIPv6:         true,
   196  		AddressIPv6:        &net.IPNet{IP: net.ParseIP("2001:db8::1"), Mask: net.CIDRMask(64, 128)},
   197  		HostIPv4:           net.ParseIP("192.0.2.2"),
   198  	}
   199  	nh, err := netlink.NewHandle()
   200  	if err != nil {
   201  		t.Fatal(err)
   202  	}
   203  	br := &bridgeInterface{nlh: nh}
   204  	createTestBridge(nc, br, t)
   205  	assertBridgeConfig(nc, br, d, t)
   206  }
   207  
   208  func TestOutgoingNATRules(t *testing.T) {
   209  	br := "br-nattest"
   210  	brIPv4 := &net.IPNet{IP: net.ParseIP(iptablesTestBridgeIP), Mask: net.CIDRMask(16, 32)}
   211  	brIPv6 := &net.IPNet{IP: net.ParseIP("2001:db8::1"), Mask: net.CIDRMask(64, 128)}
   212  	maskedBrIPv4 := &net.IPNet{IP: brIPv4.IP.Mask(brIPv4.Mask), Mask: brIPv4.Mask}
   213  	maskedBrIPv6 := &net.IPNet{IP: brIPv6.IP.Mask(brIPv6.Mask), Mask: brIPv6.Mask}
   214  	hostIPv4 := net.ParseIP("192.0.2.2")
   215  	for _, tc := range []struct {
   216  		desc               string
   217  		enableIPTables     bool
   218  		enableIP6Tables    bool
   219  		enableIPv6         bool
   220  		enableIPMasquerade bool
   221  		hostIPv4           net.IP
   222  		// Hairpin NAT rules are not tested here because they are orthogonal to outgoing NAT.  They
   223  		// exist to support the port forwarding DNAT rules: without any port forwarding there would be
   224  		// no need for any hairpin NAT rules, and when there is port forwarding then hairpin NAT rules
   225  		// are needed even if outgoing NAT is disabled.  Hairpin NAT tests belong with the port
   226  		// forwarding DNAT tests.
   227  		wantIPv4Masq bool
   228  		wantIPv4Snat bool
   229  		wantIPv6Masq bool
   230  	}{
   231  		{
   232  			desc: "everything disabled",
   233  		},
   234  		{
   235  			desc:               "iptables/ip6tables disabled",
   236  			enableIPv6:         true,
   237  			enableIPMasquerade: true,
   238  		},
   239  		{
   240  			desc:               "host IP with iptables/ip6tables disabled",
   241  			enableIPv6:         true,
   242  			enableIPMasquerade: true,
   243  			hostIPv4:           hostIPv4,
   244  		},
   245  		{
   246  			desc:            "masquerade disabled, no host IP",
   247  			enableIPTables:  true,
   248  			enableIP6Tables: true,
   249  			enableIPv6:      true,
   250  		},
   251  		{
   252  			desc:            "masquerade disabled, with host IP",
   253  			enableIPTables:  true,
   254  			enableIP6Tables: true,
   255  			enableIPv6:      true,
   256  			hostIPv4:        hostIPv4,
   257  		},
   258  		{
   259  			desc:               "IPv4 masquerade, IPv6 disabled",
   260  			enableIPTables:     true,
   261  			enableIPMasquerade: true,
   262  			wantIPv4Masq:       true,
   263  		},
   264  		{
   265  			desc:               "IPv4 SNAT, IPv6 disabled",
   266  			enableIPTables:     true,
   267  			enableIPMasquerade: true,
   268  			hostIPv4:           hostIPv4,
   269  			wantIPv4Snat:       true,
   270  		},
   271  		{
   272  			desc:               "IPv4 masquerade, IPv6 masquerade",
   273  			enableIPTables:     true,
   274  			enableIP6Tables:    true,
   275  			enableIPv6:         true,
   276  			enableIPMasquerade: true,
   277  			wantIPv4Masq:       true,
   278  			wantIPv6Masq:       true,
   279  		},
   280  		{
   281  			desc:               "IPv4 SNAT, IPv6 masquerade",
   282  			enableIPTables:     true,
   283  			enableIP6Tables:    true,
   284  			enableIPv6:         true,
   285  			enableIPMasquerade: true,
   286  			hostIPv4:           hostIPv4,
   287  			wantIPv4Snat:       true,
   288  			wantIPv6Masq:       true,
   289  		},
   290  	} {
   291  		t.Run(tc.desc, func(t *testing.T) {
   292  			defer netnsutils.SetupTestOSContext(t)()
   293  			dc := &configuration{
   294  				EnableIPTables:  tc.enableIPTables,
   295  				EnableIP6Tables: tc.enableIP6Tables,
   296  			}
   297  			r := &testRegisterer{t: t}
   298  			if err := Register(r, map[string]interface{}{netlabel.GenericData: dc}); err != nil {
   299  				t.Fatal(err)
   300  			}
   301  			if r.d == nil {
   302  				t.Fatal("testRegisterer.RegisterDriver never called")
   303  			}
   304  			nc := &networkConfiguration{
   305  				BridgeName:         br,
   306  				AddressIPv4:        brIPv4,
   307  				AddressIPv6:        brIPv6,
   308  				EnableIPv6:         tc.enableIPv6,
   309  				EnableIPMasquerade: tc.enableIPMasquerade,
   310  				HostIPv4:           tc.hostIPv4,
   311  			}
   312  			ipv4Data := []driverapi.IPAMData{{Pool: maskedBrIPv4, Gateway: brIPv4}}
   313  			ipv6Data := []driverapi.IPAMData{{Pool: maskedBrIPv6, Gateway: brIPv6}}
   314  			if !nc.EnableIPv6 {
   315  				nc.AddressIPv6 = nil
   316  				ipv6Data = nil
   317  			}
   318  			if err := r.d.CreateNetwork("nattest", map[string]interface{}{netlabel.GenericData: nc}, nil, ipv4Data, ipv6Data); err != nil {
   319  				t.Fatal(err)
   320  			}
   321  			defer func() {
   322  				if err := r.d.DeleteNetwork("nattest"); err != nil {
   323  					t.Fatal(err)
   324  				}
   325  			}()
   326  			// Log the contents of all chains to aid troubleshooting.
   327  			for _, ipv := range []iptables.IPVersion{iptables.IPv4, iptables.IPv6} {
   328  				ipt := iptables.GetIptable(ipv)
   329  				for _, table := range []iptables.Table{iptables.Nat, iptables.Filter, iptables.Mangle} {
   330  					out, err := ipt.Raw("-t", string(table), "-S")
   331  					if err != nil {
   332  						t.Error(err)
   333  					}
   334  					t.Logf("%s: %s %s table rules:\n%s", tc.desc, ipv, table, string(out))
   335  				}
   336  			}
   337  			for _, rc := range []struct {
   338  				want bool
   339  				rule iptRule
   340  			}{
   341  				// Rule order doesn't matter: At most one of the following IPv4 rules will exist, and the
   342  				// same goes for the IPv6 rules.
   343  				{tc.wantIPv4Masq, iptRule{iptables.IPv4, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv4.String(), "!", "-o", br, "-j", "MASQUERADE"}}},
   344  				{tc.wantIPv4Snat, iptRule{iptables.IPv4, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv4.String(), "!", "-o", br, "-j", "SNAT", "--to-source", hostIPv4.String()}}},
   345  				{tc.wantIPv6Masq, iptRule{iptables.IPv6, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv6.String(), "!", "-o", br, "-j", "MASQUERADE"}}},
   346  			} {
   347  				assert.Equal(t, rc.rule.Exists(), rc.want)
   348  			}
   349  		})
   350  	}
   351  }