Browse code

Gate setting of bridge-nf-call-ip6tables on "--ip6tables=true".

The code to enable "bridge-nf-call-iptables" or "bridge-nf-call-ip6tables"
was gated on "--iptables=true", it didn't check "--ip6tables=true".

So, split the top level call into IPv4/IPv6 so that the iptables-enable
settings can be checked independently, and simplfied the implementation.

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

Rob Murray authored on 2024/04/24 20:35:38
Showing 3 changed files
... ...
@@ -824,7 +824,8 @@ func (d *driver) createNetwork(config *networkConfiguration) (err error) {
824 824
 		{d.config.EnableIPTables || d.config.EnableIP6Tables, setupNetworkIsolationRules},
825 825
 
826 826
 		// Configure bridge networking filtering if ICC is off and IP tables are enabled
827
-		{!config.EnableICC && d.config.EnableIPTables, setupBridgeNetFiltering},
827
+		{!config.EnableICC && d.config.EnableIPTables, setupIPv4BridgeNetFiltering},
828
+		{!config.EnableICC && d.config.EnableIP6Tables, setupIPv6BridgeNetFiltering},
828 829
 	} {
829 830
 		if step.Condition {
830 831
 			bridgeSetup.queueStep(step.Fn)
... ...
@@ -12,27 +12,44 @@ import (
12 12
 	"github.com/containerd/log"
13 13
 )
14 14
 
15
-// Enumeration type saying which versions of IP protocol to process.
16
-type ipVersion int
17
-
18
-const (
19
-	ipvnone ipVersion = iota
20
-	ipv4
21
-	ipv6
22
-	ipvboth
23
-)
15
+// setupIPv4BridgeNetFiltering checks whether IPv4 forwarding is enabled and, if
16
+// it is, sets kernel param "bridge-nf-call-iptables=1" so that packets traversing
17
+// the bridge are filtered.
18
+func setupIPv4BridgeNetFiltering(*networkConfiguration, *bridgeInterface) error {
19
+	if enabled, err := getKernelBoolParam("/proc/sys/net/ipv4/ip_forward"); err != nil {
20
+		log.G(context.TODO()).Warnf("failed to check IPv4 forwarding: %v", err)
21
+		return nil
22
+	} else if enabled {
23
+		return enableBridgeNetFiltering("/proc/sys/net/bridge/bridge-nf-call-iptables")
24
+	}
25
+	return nil
26
+}
24 27
 
25
-// getIPVersion gets the IP version in use ( [ipv4], [ipv6] or [ipv4 and ipv6] )
26
-func getIPVersion(config *networkConfiguration) ipVersion {
27
-	ipVer := ipv4
28
-	if config.AddressIPv6 != nil || config.EnableIPv6 {
29
-		ipVer |= ipv6
28
+// setupIPv6BridgeNetFiltering checks whether IPv6 forwarding is enabled for the
29
+// bridge and, if it is, sets kernel param "bridge-nf-call-ip6tables=1" so that
30
+// packets traversing the bridge are filtered.
31
+func setupIPv6BridgeNetFiltering(config *networkConfiguration, _ *bridgeInterface) error {
32
+	if !config.EnableIPv6 || config.AddressIPv6 == nil {
33
+		return nil
34
+	}
35
+	bn := config.BridgeName
36
+	if bn == "" {
37
+		// No interface name, check the default used for new interfaces.
38
+		bn = "default"
39
+	}
40
+	if enabled, err := getKernelBoolParam("/proc/sys/net/ipv6/conf/" + bn + "/forwarding"); err != nil {
41
+		log.G(context.TODO()).Warnf("failed to check IPv6 forwarding: %v", err)
42
+		return nil
43
+	} else if enabled {
44
+		return enableBridgeNetFiltering("/proc/sys/net/bridge/bridge-nf-call-ip6tables")
30 45
 	}
31
-	return ipVer
46
+	return nil
32 47
 }
33 48
 
34
-func setupBridgeNetFiltering(config *networkConfiguration, _ *bridgeInterface) error {
35
-	if err := checkBridgeNetFiltering(config); err != nil {
49
+// Enable bridge net filtering if not already enabled. See github issue #11404
50
+func enableBridgeNetFiltering(nfParam string) error {
51
+	enabled, err := getKernelBoolParam(nfParam)
52
+	if err != nil {
36 53
 		var pathErr *os.PathError
37 54
 		if errors.As(err, &pathErr) && errors.Is(pathErr, syscall.ENOENT) {
38 55
 			if isRunningInContainer() {
... ...
@@ -43,115 +60,19 @@ func setupBridgeNetFiltering(config *networkConfiguration, _ *bridgeInterface) e
43 43
 		}
44 44
 		return fmt.Errorf("cannot restrict inter-container communication: %v", err)
45 45
 	}
46
-	return nil
47
-}
48
-
49
-// Enable bridge net filtering if ip forwarding is enabled. See github issue #11404
50
-func checkBridgeNetFiltering(config *networkConfiguration) error {
51
-	ipVer := getIPVersion(config)
52
-	iface := config.BridgeName
53
-	doEnable := func(ipVer ipVersion) error {
54
-		enabled, err := isPacketForwardingEnabled(ipVer, iface)
55
-		if err != nil {
56
-			var ipVerName string
57
-			if ipVer == ipv4 {
58
-				ipVerName = "IPv4"
59
-			} else {
60
-				ipVerName = "IPv6"
61
-			}
62
-			log.G(context.TODO()).Warnf("failed to check %s forwarding: %v", ipVerName, err)
63
-		} else if enabled {
64
-			enabled, err := getKernelBoolParam(getBridgeNFKernelParam(ipVer))
65
-			if err != nil || enabled {
66
-				return err
67
-			}
68
-			return setKernelBoolParam(getBridgeNFKernelParam(ipVer), true)
69
-		}
70
-		return nil
71
-	}
72
-
73
-	switch ipVer {
74
-	case ipv4, ipv6:
75
-		return doEnable(ipVer)
76
-	case ipvboth:
77
-		v4err := doEnable(ipv4)
78
-		v6err := doEnable(ipv6)
79
-		if v4err == nil {
80
-			return v6err
81
-		}
82
-		return v4err
83
-	default:
84
-		return nil
85
-	}
86
-}
87
-
88
-// Get kernel param path saying whether IPv${ipVer} traffic is being forwarded
89
-// on particular interface. Interface may be specified for IPv6 only. If
90
-// `iface` is empty, `default` will be assumed, which represents default value
91
-// for new interfaces.
92
-func getForwardingKernelParam(ipVer ipVersion, iface string) string {
93
-	switch ipVer {
94
-	case ipv4:
95
-		return "/proc/sys/net/ipv4/ip_forward"
96
-	case ipv6:
97
-		if iface == "" {
98
-			iface = "default"
99
-		}
100
-		return fmt.Sprintf("/proc/sys/net/ipv6/conf/%s/forwarding", iface)
101
-	default:
102
-		return ""
103
-	}
104
-}
105
-
106
-// Get kernel param path saying whether bridged IPv${ipVer} traffic shall be
107
-// passed to ip${ipVer}tables' chains.
108
-func getBridgeNFKernelParam(ipVer ipVersion) string {
109
-	switch ipVer {
110
-	case ipv4:
111
-		return "/proc/sys/net/bridge/bridge-nf-call-iptables"
112
-	case ipv6:
113
-		return "/proc/sys/net/bridge/bridge-nf-call-ip6tables"
114
-	default:
115
-		return ""
46
+	if !enabled {
47
+		return os.WriteFile(nfParam, []byte{'1', '\n'}, 0o644)
116 48
 	}
49
+	return nil
117 50
 }
118 51
 
119 52
 // Gets the value of the kernel parameters located at the given path
120 53
 func getKernelBoolParam(path string) (bool, error) {
121
-	enabled := false
122 54
 	line, err := os.ReadFile(path)
123 55
 	if err != nil {
124 56
 		return false, err
125 57
 	}
126
-	if len(line) > 0 {
127
-		enabled = line[0] == '1'
128
-	}
129
-	return enabled, err
130
-}
131
-
132
-// Sets the value of the kernel parameter located at the given path
133
-func setKernelBoolParam(path string, on bool) error {
134
-	value := byte('0')
135
-	if on {
136
-		value = byte('1')
137
-	}
138
-	return os.WriteFile(path, []byte{value, '\n'}, 0o644)
139
-}
140
-
141
-// Checks to see if packet forwarding is enabled
142
-func isPacketForwardingEnabled(ipVer ipVersion, iface string) (bool, error) {
143
-	switch ipVer {
144
-	case ipv4, ipv6:
145
-		return getKernelBoolParam(getForwardingKernelParam(ipVer, iface))
146
-	case ipvboth:
147
-		enabled, err := getKernelBoolParam(getForwardingKernelParam(ipv4, ""))
148
-		if err != nil || !enabled {
149
-			return enabled, err
150
-		}
151
-		return getKernelBoolParam(getForwardingKernelParam(ipv6, iface))
152
-	default:
153
-		return true, nil
154
-	}
58
+	return len(line) > 0 && line[0] == '1', nil
155 59
 }
156 60
 
157 61
 func isRunningInContainer() bool {
158 62
deleted file mode 100644
... ...
@@ -1,14 +0,0 @@
1
-//go:build linux
2
-
3
-package bridge
4
-
5
-import "testing"
6
-
7
-func TestIPConstantValues(t *testing.T) {
8
-	if ipv4|ipv6 != ipvboth {
9
-		t.Fatalf("bitwise or of ipv4(%04b) and ipv6(%04b) must yield ipvboth(%04b)", ipv4, ipv6, ipvboth)
10
-	}
11
-	if ipvboth&(^(ipv4 | ipv6)) != ipvnone {
12
-		t.Fatalf("ipvboth(%04b) with unset ipv4(%04b) and ipv6(%04b) bits shall equal to ipvnone", ipvboth, ipv4, ipv6)
13
-	}
14
-}