github.com/tonistiigi/docker@v0.10.1-0.20240229224939-974013b0dc6a/libnetwork/drivers/bridge/setup_ip_tables_linux_test.go (about) 1 package bridge 2 3 import ( 4 "net" 5 "testing" 6 7 "github.com/docker/docker/internal/testutils/netnsutils" 8 "github.com/docker/docker/libnetwork/driverapi" 9 "github.com/docker/docker/libnetwork/iptables" 10 "github.com/docker/docker/libnetwork/netlabel" 11 "github.com/docker/docker/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 hostIPv6 := net.ParseIP("2001:db8:1::1") 216 for _, tc := range []struct { 217 desc string 218 enableIPTables bool 219 enableIP6Tables bool 220 enableIPv6 bool 221 enableIPMasquerade bool 222 hostIPv4 net.IP 223 hostIPv6 net.IP 224 // Hairpin NAT rules are not tested here because they are orthogonal to outgoing NAT. They 225 // exist to support the port forwarding DNAT rules: without any port forwarding there would be 226 // no need for any hairpin NAT rules, and when there is port forwarding then hairpin NAT rules 227 // are needed even if outgoing NAT is disabled. Hairpin NAT tests belong with the port 228 // forwarding DNAT tests. 229 wantIPv4Masq bool 230 wantIPv4Snat bool 231 wantIPv6Masq bool 232 wantIPv6Snat bool 233 }{ 234 { 235 desc: "everything disabled", 236 }, 237 { 238 desc: "iptables/ip6tables disabled", 239 enableIPv6: true, 240 enableIPMasquerade: true, 241 }, 242 { 243 desc: "host IP with iptables/ip6tables disabled", 244 enableIPv6: true, 245 enableIPMasquerade: true, 246 hostIPv4: hostIPv4, 247 hostIPv6: hostIPv6, 248 }, 249 { 250 desc: "masquerade disabled, no host IP", 251 enableIPTables: true, 252 enableIP6Tables: true, 253 enableIPv6: true, 254 }, 255 { 256 desc: "masquerade disabled, with host IP", 257 enableIPTables: true, 258 enableIP6Tables: true, 259 enableIPv6: true, 260 hostIPv4: hostIPv4, 261 hostIPv6: hostIPv6, 262 }, 263 { 264 desc: "IPv4 masquerade, IPv6 disabled", 265 enableIPTables: true, 266 enableIPMasquerade: true, 267 wantIPv4Masq: true, 268 }, 269 { 270 desc: "IPv4 SNAT, IPv6 disabled", 271 enableIPTables: true, 272 enableIPMasquerade: true, 273 hostIPv4: hostIPv4, 274 wantIPv4Snat: true, 275 }, 276 { 277 desc: "IPv4 masquerade, IPv6 masquerade", 278 enableIPTables: true, 279 enableIP6Tables: true, 280 enableIPv6: true, 281 enableIPMasquerade: true, 282 wantIPv4Masq: true, 283 wantIPv6Masq: true, 284 }, 285 { 286 desc: "IPv4 masquerade, IPv6 SNAT", 287 enableIPTables: true, 288 enableIP6Tables: true, 289 enableIPv6: true, 290 enableIPMasquerade: true, 291 hostIPv6: hostIPv6, 292 wantIPv4Masq: true, 293 wantIPv6Snat: true, 294 }, 295 { 296 desc: "IPv4 SNAT, IPv6 masquerade", 297 enableIPTables: true, 298 enableIP6Tables: true, 299 enableIPv6: true, 300 enableIPMasquerade: true, 301 hostIPv4: hostIPv4, 302 wantIPv4Snat: true, 303 wantIPv6Masq: true, 304 }, 305 { 306 desc: "IPv4 SNAT, IPv6 SNAT", 307 enableIPTables: true, 308 enableIP6Tables: true, 309 enableIPv6: true, 310 enableIPMasquerade: true, 311 hostIPv4: hostIPv4, 312 hostIPv6: hostIPv6, 313 wantIPv4Snat: true, 314 wantIPv6Snat: true, 315 }, 316 } { 317 t.Run(tc.desc, func(t *testing.T) { 318 defer netnsutils.SetupTestOSContext(t)() 319 dc := &configuration{ 320 EnableIPTables: tc.enableIPTables, 321 EnableIP6Tables: tc.enableIP6Tables, 322 } 323 r := &testRegisterer{t: t} 324 if err := Register(r, map[string]interface{}{netlabel.GenericData: dc}); err != nil { 325 t.Fatal(err) 326 } 327 if r.d == nil { 328 t.Fatal("testRegisterer.RegisterDriver never called") 329 } 330 nc := &networkConfiguration{ 331 BridgeName: br, 332 AddressIPv4: brIPv4, 333 AddressIPv6: brIPv6, 334 EnableIPv6: tc.enableIPv6, 335 EnableIPMasquerade: tc.enableIPMasquerade, 336 HostIPv4: tc.hostIPv4, 337 HostIPv6: tc.hostIPv6, 338 } 339 ipv4Data := []driverapi.IPAMData{{Pool: maskedBrIPv4, Gateway: brIPv4}} 340 ipv6Data := []driverapi.IPAMData{{Pool: maskedBrIPv6, Gateway: brIPv6}} 341 if !nc.EnableIPv6 { 342 nc.AddressIPv6 = nil 343 ipv6Data = nil 344 } 345 if err := r.d.CreateNetwork("nattest", map[string]interface{}{netlabel.GenericData: nc}, nil, ipv4Data, ipv6Data); err != nil { 346 t.Fatal(err) 347 } 348 defer func() { 349 if err := r.d.DeleteNetwork("nattest"); err != nil { 350 t.Fatal(err) 351 } 352 }() 353 // Log the contents of all chains to aid troubleshooting. 354 for _, ipv := range []iptables.IPVersion{iptables.IPv4, iptables.IPv6} { 355 ipt := iptables.GetIptable(ipv) 356 for _, table := range []iptables.Table{iptables.Nat, iptables.Filter, iptables.Mangle} { 357 out, err := ipt.Raw("-t", string(table), "-S") 358 if err != nil { 359 t.Error(err) 360 } 361 t.Logf("%s: %s %s table rules:\n%s", tc.desc, ipv, table, string(out)) 362 } 363 } 364 for _, rc := range []struct { 365 want bool 366 rule iptRule 367 }{ 368 // Rule order doesn't matter: At most one of the following IPv4 rules will exist, and the 369 // same goes for the IPv6 rules. 370 {tc.wantIPv4Masq, iptRule{iptables.IPv4, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv4.String(), "!", "-o", br, "-j", "MASQUERADE"}}}, 371 {tc.wantIPv4Snat, iptRule{iptables.IPv4, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv4.String(), "!", "-o", br, "-j", "SNAT", "--to-source", hostIPv4.String()}}}, 372 {tc.wantIPv6Masq, iptRule{iptables.IPv6, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv6.String(), "!", "-o", br, "-j", "MASQUERADE"}}}, 373 {tc.wantIPv6Snat, iptRule{iptables.IPv6, iptables.Nat, "POSTROUTING", []string{"-s", maskedBrIPv6.String(), "!", "-o", br, "-j", "SNAT", "--to-source", hostIPv6.String()}}}, 374 } { 375 assert.Equal(t, rc.rule.Exists(), rc.want) 376 } 377 }) 378 } 379 }