Browse code

Make libnetwork responsible for DOCKER-USER setup/reload

It no longer needs to be moved to the top of the filter-FORWARD
chain after creating a new bridge network. But, it does need to
be rearranged after setting up Swarm ingress.

Similarly, the jump to DOCKER-INGRESS no longer needs to be
shuffled back to the top of the FORWARD chain after adding a
new network.

Signed-off-by: Rob Murray <rob.murray@docker.com>

Rob Murray authored on 2024/10/21 23:50:01
Showing 10 changed files
... ...
@@ -854,10 +854,6 @@ func (daemon *Daemon) initNetworkController(cfg *config.Config, activeSandboxes
854 854
 		return err
855 855
 	}
856 856
 
857
-	if err := daemon.netController.SetupUserChains(); err != nil {
858
-		log.G(context.TODO()).WithError(err).Warnf("initNetworkController")
859
-	}
860
-
861 857
 	// Set HostGatewayIP to the default bridge's IP if it is empty
862 858
 	setHostGatewayIP(daemon.netController, cfg)
863 859
 	return nil
... ...
@@ -168,7 +168,7 @@ func New(cfgOptions ...config.Option) (*Controller, error) {
168 168
 		return nil, err
169 169
 	}
170 170
 
171
-	setupArrangeUserFilterRule(c)
171
+	c.setupUserChains()
172 172
 	return c, nil
173 173
 }
174 174
 
... ...
@@ -700,30 +700,9 @@ addToStore:
700 700
 		}
701 701
 	}
702 702
 
703
-	if c.isSwarmNode() {
704
-		c.mu.Lock()
705
-		arrangeIngressFilterRule()
706
-		c.mu.Unlock()
707
-	}
708
-
709
-	if err := c.SetupUserChains(); err != nil {
710
-		log.G(context.TODO()).WithError(err).Warnf("Controller.NewNetwork %s:", name)
711
-	}
712
-
713 703
 	return nw, nil
714 704
 }
715 705
 
716
-// Sets up the DOCKER-USER chain for each iptables version (IPv4, IPv6) that's
717
-// enabled in the controller's configuration.
718
-func (c *Controller) SetupUserChains() error {
719
-	for _, ipVersion := range c.enabledIptablesVersions() {
720
-		if err := setupUserChain(ipVersion); err != nil {
721
-			return err
722
-		}
723
-	}
724
-	return nil
725
-}
726
-
727 706
 var joinCluster NetworkWalker = func(nw *Network) bool {
728 707
 	if nw.configOnly {
729 708
 		return false
... ...
@@ -2,6 +2,7 @@ package libnetwork
2 2
 
3 3
 import (
4 4
 	"context"
5
+	"errors"
5 6
 	"fmt"
6 7
 
7 8
 	"github.com/containerd/log"
... ...
@@ -10,24 +11,24 @@ import (
10 10
 
11 11
 const userChain = "DOCKER-USER"
12 12
 
13
-var ctrl *Controller
14
-
15
-func setupArrangeUserFilterRule(c *Controller) {
16
-	ctrl = c
17
-	iptables.OnReloaded(arrangeUserFilterRule)
18
-}
19
-
20
-// arrangeUserFilterRule sets up the DOCKER-USER chain for each iptables version
21
-// (IPv4, IPv6) that's enabled in the controller's configuration.
22
-func arrangeUserFilterRule() {
23
-	if ctrl == nil {
24
-		return
25
-	}
26
-	for _, ipVersion := range ctrl.enabledIptablesVersions() {
27
-		if err := setupUserChain(ipVersion); err != nil {
28
-			log.G(context.TODO()).WithError(err).Warn("arrangeUserFilterRule")
13
+// Sets up the DOCKER-USER chain for each iptables version (IPv4, IPv6) that's
14
+// enabled in the controller's configuration.
15
+func (c *Controller) setupUserChains() {
16
+	setup := func() error {
17
+		var errs []error
18
+		for _, ipVersion := range c.enabledIptablesVersions() {
19
+			errs = append(errs, setupUserChain(ipVersion))
29 20
 		}
21
+		return errors.Join(errs...)
22
+	}
23
+	if err := setup(); err != nil {
24
+		log.G(context.Background()).WithError(err).Warn("configuring " + userChain)
30 25
 	}
26
+	iptables.OnReloaded(func() {
27
+		if err := setup(); err != nil {
28
+			log.G(context.Background()).WithError(err).Warn("configuring " + userChain + " on firewall reload")
29
+		}
30
+	})
31 31
 }
32 32
 
33 33
 // setupUserChain sets up the DOCKER-USER chain for the given [iptables.IPVersion].
... ...
@@ -69,7 +69,7 @@ func TestUserChain(t *testing.T) {
69 69
 				_, err = iptable6.Raw("-A", fwdChainName, "-j", "DROP")
70 70
 				assert.Check(t, err)
71 71
 			}
72
-			arrangeUserFilterRule()
72
+			c.setupUserChains()
73 73
 
74 74
 			golden.Assert(t, getRules(t, iptable4, fwdChainName),
75 75
 				fmt.Sprintf("TestUserChain_iptables-%v_append-%v_fwdafter4", tc.iptables, tc.append))
... ...
@@ -2,6 +2,4 @@
2 2
 
3 3
 package libnetwork
4 4
 
5
-func setupArrangeUserFilterRule(c *Controller) {}
6
-func arrangeUserFilterRule()                   {}
7
-func setupUserChain(ipVersion any) error       { return nil }
5
+func (c *Controller) setupUserChains() {}
... ...
@@ -363,7 +363,10 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro
363 363
 			if err := iptable.RawCombinedOutput("-I", "FORWARD", "-j", ingressChain); err != nil {
364 364
 				return fmt.Errorf("failed to add jump rule to %s in filter table forward chain: %v", ingressChain, err)
365 365
 			}
366
-			arrangeUserFilterRule()
366
+			// The jump to DOCKER-USER needs to be before the jump to DOCKER-INGRESS.
367
+			if err := setupUserChain(iptables.IPv4); err != nil {
368
+				log.G(context.TODO()).Warnf("Failed to restore "+userChain+" after creating "+ingressChain+": %v", err)
369
+			}
367 370
 		}
368 371
 
369 372
 		oifName, err := findOIFName(gwIP)
... ...
@@ -452,26 +455,6 @@ func programIngress(gwIP net.IP, ingressPorts []*PortConfig, isDelete bool) erro
452 452
 	return nil
453 453
 }
454 454
 
455
-// In the filter table FORWARD chain the first rule should be to jump to
456
-// DOCKER-USER so the user is able to filter packet first.
457
-// The second rule should be jump to INGRESS-CHAIN.
458
-// This chain has the rules to allow access to the published ports for swarm tasks
459
-// from local bridge networks and docker_gwbridge (ie:taks on other swarm networks)
460
-func arrangeIngressFilterRule() {
461
-	// TODO IPv6 support
462
-	iptable := iptables.GetIptable(iptables.IPv4)
463
-	if iptable.ExistChain(ingressChain, iptables.Filter) {
464
-		if iptable.Exists(iptables.Filter, "FORWARD", "-j", ingressChain) {
465
-			if err := iptable.RawCombinedOutput("-D", "FORWARD", "-j", ingressChain); err != nil {
466
-				log.G(context.TODO()).Warnf("failed to delete jump rule to ingressChain in filter table: %v", err)
467
-			}
468
-		}
469
-		if err := iptable.RawCombinedOutput("-I", "FORWARD", "-j", ingressChain); err != nil {
470
-			log.G(context.TODO()).Warnf("failed to add jump rule to ingressChain in filter table: %v", err)
471
-		}
472
-	}
473
-}
474
-
475 455
 func findOIFName(ip net.IP) (string, error) {
476 456
 	nlh := ns.NlHandle()
477 457
 
... ...
@@ -1,4 +1,5 @@
1 1
 -P FORWARD ACCEPT
2
+-A FORWARD -j DOCKER-USER
2 3
 -A FORWARD -m set --match-set docker-ext-bridges-v4 dst -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
3 4
 -A FORWARD -j DOCKER-ISOLATION-STAGE-1
4 5
 -A FORWARD -m set --match-set docker-ext-bridges-v4 dst -j DOCKER
... ...
@@ -1,4 +1,5 @@
1 1
 -P FORWARD ACCEPT
2
+-A FORWARD -j DOCKER-USER
2 3
 -A FORWARD -m set --match-set docker-ext-bridges-v6 dst -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
3 4
 -A FORWARD -j DOCKER-ISOLATION-STAGE-1
4 5
 -A FORWARD -m set --match-set docker-ext-bridges-v6 dst -j DOCKER
... ...
@@ -1,4 +1,5 @@
1 1
 -P FORWARD ACCEPT
2
+-A FORWARD -j DOCKER-USER
2 3
 -A FORWARD -m set --match-set docker-ext-bridges-v4 dst -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
3 4
 -A FORWARD -j DOCKER-ISOLATION-STAGE-1
4 5
 -A FORWARD -m set --match-set docker-ext-bridges-v4 dst -j DOCKER
... ...
@@ -1,4 +1,5 @@
1 1
 -P FORWARD ACCEPT
2
+-A FORWARD -j DOCKER-USER
2 3
 -A FORWARD -m set --match-set docker-ext-bridges-v6 dst -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
3 4
 -A FORWARD -j DOCKER-ISOLATION-STAGE-1
4 5
 -A FORWARD -m set --match-set docker-ext-bridges-v6 dst -j DOCKER