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>
| ... | ... |
@@ -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 |
-} |