Переглянути джерело

Refactor of docker PR #11405

Signed-off-by: Alec Benson <albenson@redhat.com>
Alec Benson 10 роки тому
батько
коміт
90a410eb3d

+ 3 - 0
libnetwork/drivers/bridge/bridge.go

@@ -669,6 +669,9 @@ func (d *driver) CreateNetwork(id types.UUID, option map[string]interface{}) err
 
 		// Add inter-network communication rules.
 		{config.EnableIPTables, setupNetworkIsolationRules},
+
+		//Configure bridge networking filtering if ICC is off and IP tables are enabled
+		{!config.EnableICC && config.EnableIPTables, setupBridgeNetFiltering},
 	} {
 		if step.Condition {
 			bridgeSetup.queueStep(step.Fn)

+ 162 - 0
libnetwork/drivers/bridge/setup_bridgenetfiltering.go

@@ -0,0 +1,162 @@
+package bridge
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"syscall"
+
+	"github.com/Sirupsen/logrus"
+)
+
+// Enumeration type saying which versions of IP protocol to process.
+type ipVersion int
+
+const (
+	ipvnone ipVersion = iota
+	ipv4
+	ipv6
+	ipvboth
+)
+
+//Gets the IP version in use ( [ipv4], [ipv6] or [ipv4 and ipv6] )
+func getIPVersion(config *networkConfiguration) ipVersion {
+	ipVersion := ipv4
+	if config.FixedCIDRv6 != nil || config.EnableIPv6 {
+		ipVersion |= ipv6
+	}
+	return ipVersion
+}
+
+func setupBridgeNetFiltering(config *networkConfiguration, i *bridgeInterface) error {
+	err := checkBridgeNetFiltering(config, i)
+	if err != nil {
+		if ptherr, ok := err.(*os.PathError); ok {
+			if errno, ok := ptherr.Err.(syscall.Errno); ok && errno == syscall.ENOENT {
+				if isRunningInContainer() {
+					logrus.Warnf("running inside docker container, ignoring missing kernel params: %v", err)
+					err = nil
+				} else {
+					err = fmt.Errorf("please ensure that br_netfilter kernel module is loaded")
+				}
+			}
+		}
+		if err != nil {
+			return fmt.Errorf("cannot restrict inter-container communication: %v", err)
+		}
+	}
+	return nil
+}
+
+//Enable bridge net filtering if ip forwarding is enabled. See github issue #11404
+func checkBridgeNetFiltering(config *networkConfiguration, i *bridgeInterface) error {
+	ipVer := getIPVersion(config)
+	iface := config.BridgeName
+	doEnable := func(ipVer ipVersion) error {
+		var ipVerName string
+		if ipVer == ipv4 {
+			ipVerName = "IPv4"
+		} else {
+			ipVerName = "IPv6"
+		}
+		enabled, err := isPacketForwardingEnabled(ipVer, iface)
+		if err != nil {
+			logrus.Warnf("failed to check %s forwarding: %v", ipVerName, err)
+		} else if enabled {
+			enabled, err := getKernelBoolParam(getBridgeNFKernelParam(ipVer))
+			if err != nil || enabled {
+				return err
+			}
+			return setKernelBoolParam(getBridgeNFKernelParam(ipVer), true)
+		}
+		return nil
+	}
+
+	switch ipVer {
+	case ipv4, ipv6:
+		return doEnable(ipVer)
+	case ipvboth:
+		v4err := doEnable(ipv4)
+		v6err := doEnable(ipv6)
+		if v4err == nil {
+			return v6err
+		}
+		return v4err
+	default:
+		return nil
+	}
+}
+
+// Get kernel param path saying whether IPv${ipVer} traffic is being forwarded
+// on particular interface. Interface may be specified for IPv6 only. If
+// `iface` is empty, `default` will be assumed, which represents default value
+// for new interfaces.
+func getForwardingKernelParam(ipVer ipVersion, iface string) string {
+	switch ipVer {
+	case ipv4:
+		return "/proc/sys/net/ipv4/ip_forward"
+	case ipv6:
+		if iface == "" {
+			iface = "default"
+		}
+		return fmt.Sprintf("/proc/sys/net/ipv6/conf/%s/forwarding", iface)
+	default:
+		return ""
+	}
+}
+
+// Get kernel param path saying whether bridged IPv${ipVer} traffic shall be
+// passed to ip${ipVer}tables' chains.
+func getBridgeNFKernelParam(ipVer ipVersion) string {
+	switch ipVer {
+	case ipv4:
+		return "/proc/sys/net/bridge/bridge-nf-call-iptables"
+	case ipv6:
+		return "/proc/sys/net/bridge/bridge-nf-call-ip6tables"
+	default:
+		return ""
+	}
+}
+
+//Gets the value of the kernel parameters located at the given path
+func getKernelBoolParam(path string) (bool, error) {
+	enabled := false
+	line, err := ioutil.ReadFile(path)
+	if err != nil {
+		return false, err
+	}
+	if len(line) > 0 {
+		enabled = line[0] == '1'
+	}
+	return enabled, err
+}
+
+//Sets the value of the kernel parameter located at the given path
+func setKernelBoolParam(path string, on bool) error {
+	value := byte('0')
+	if on {
+		value = byte('1')
+	}
+	return ioutil.WriteFile(path, []byte{value, '\n'}, 0644)
+}
+
+//Checks to see if packet forwarding is enabled
+func isPacketForwardingEnabled(ipVer ipVersion, iface string) (bool, error) {
+	switch ipVer {
+	case ipv4, ipv6:
+		return getKernelBoolParam(getForwardingKernelParam(ipVer, iface))
+	case ipvboth:
+		enabled, err := getKernelBoolParam(getForwardingKernelParam(ipv4, ""))
+		if err != nil || !enabled {
+			return enabled, err
+		}
+		return getKernelBoolParam(getForwardingKernelParam(ipv6, iface))
+	default:
+		return true, nil
+	}
+}
+
+func isRunningInContainer() bool {
+	_, err := os.Stat("/.dockerinit")
+	return !os.IsNotExist(err)
+}

+ 12 - 0
libnetwork/drivers/bridge/setup_bridgenetfiltering_test.go

@@ -0,0 +1,12 @@
+package bridge
+
+import "testing"
+
+func TestIPConstantValues(t *testing.T) {
+	if ipv4|ipv6 != ipvboth {
+		t.Fatalf("bitwise or of ipv4(%04b) and ipv6(%04b) must yield ipvboth(%04b)", ipv4, ipv6, ipvboth)
+	}
+	if ipvboth&(^(ipv4 | ipv6)) != ipvnone {
+		t.Fatalf("ipvboth(%04b) with unset ipv4(%04b) and ipv6(%04b) bits shall equal to ipvnone", ipvboth, ipv4, ipv6)
+	}
+}