瀏覽代碼

Merge pull request #9397 from jpopelka/9395-firewalld

Firewalld support
Tibor Vass 10 年之前
父節點
當前提交
259effc6df

+ 16 - 1
daemon/networkdriver/bridge/driver.go

@@ -226,13 +226,18 @@ func InitDriver(config *Config) error {
 		bridgeIPv6Addr = networkv6.IP
 	}
 
+	if config.EnableIptables {
+		iptables.FirewalldInit()
+	}
+
 	// Configure iptables for link support
 	if config.EnableIptables {
 		if err := setupIPTables(addrv4, config.InterContainerCommunication, config.EnableIpMasq); err != nil {
 			logrus.Errorf("Error configuing iptables: %s", err)
 			return err
 		}
-
+		// call this on Firewalld reload
+		iptables.OnReloaded(func() { setupIPTables(addrv4, config.InterContainerCommunication, config.EnableIpMasq) })
 	}
 
 	if config.EnableIpForward {
@@ -262,10 +267,16 @@ func InitDriver(config *Config) error {
 		if err != nil {
 			return err
 		}
+		// call this on Firewalld reload
+		iptables.OnReloaded(func() { iptables.NewChain("DOCKER", bridgeIface, iptables.Nat) })
+
 		chain, err := iptables.NewChain("DOCKER", bridgeIface, iptables.Filter)
 		if err != nil {
 			return err
 		}
+		// call this on Firewalld reload
+		iptables.OnReloaded(func() { iptables.NewChain("DOCKER", bridgeIface, iptables.Filter) })
+
 		portMapper.SetIptablesChain(chain)
 	}
 
@@ -310,6 +321,10 @@ func InitDriver(config *Config) error {
 	// Block BridgeIP in IP allocator
 	ipAllocator.RequestIP(bridgeIPv4Network, bridgeIPv4Network.IP)
 
+	if config.EnableIptables {
+		iptables.OnReloaded(portMapper.ReMapAll) // call this on Firewalld reload
+	}
+
 	return nil
 }
 

+ 12 - 0
daemon/networkdriver/portmapper/mapper.go

@@ -132,6 +132,18 @@ func (pm *PortMapper) Map(container net.Addr, hostIP net.IP, hostPort int) (host
 	return m.host, nil
 }
 
+// re-apply all port mappings
+func (pm *PortMapper) ReMapAll() {
+	logrus.Debugln("Re-applying all port mappings.")
+	for _, data := range pm.currentMappings {
+		containerIP, containerPort := getIPAndPort(data.container)
+		hostIP, hostPort := getIPAndPort(data.host)
+		if err := pm.forward(iptables.Append, data.proto, hostIP, hostPort, containerIP.String(), containerPort); err != nil {
+			logrus.Errorf("Error on iptables add: %s", err)
+		}
+	}
+}
+
 func (pm *PortMapper) Unmap(host net.Addr) error {
 	pm.lock.Lock()
 	defer pm.lock.Unlock()

+ 5 - 1
links/links.go

@@ -7,6 +7,7 @@ import (
 
 	"github.com/docker/docker/daemon/networkdriver/bridge"
 	"github.com/docker/docker/nat"
+	"github.com/docker/docker/pkg/iptables"
 )
 
 type Link struct {
@@ -143,6 +144,8 @@ func (l *Link) Enable() error {
 	if err := l.toggle("-A", false); err != nil {
 		return err
 	}
+	// call this on Firewalld reload
+	iptables.OnReloaded(func() { l.toggle("-I", false) })
 	l.IsEnabled = true
 	return nil
 }
@@ -152,7 +155,8 @@ func (l *Link) Disable() {
 	// exist in iptables
 	// -D == iptables delete flag
 	l.toggle("-D", true)
-
+	// call this on Firewalld reload
+	iptables.OnReloaded(func() { l.toggle("-D", true) })
 	l.IsEnabled = false
 }
 

+ 163 - 0
pkg/iptables/firewalld.go

@@ -0,0 +1,163 @@
+package iptables
+
+import (
+	"fmt"
+	"github.com/Sirupsen/logrus"
+	"github.com/godbus/dbus"
+	"strings"
+)
+
+type IPV string
+
+const (
+	Iptables  IPV = "ipv4"
+	Ip6tables IPV = "ipv6"
+	Ebtables  IPV = "eb"
+)
+const (
+	dbusInterface = "org.fedoraproject.FirewallD1"
+	dbusPath      = "/org/fedoraproject/FirewallD1"
+)
+
+// Conn is a connection to firewalld dbus endpoint.
+type Conn struct {
+	sysconn *dbus.Conn
+	sysobj  *dbus.Object
+	signal  chan *dbus.Signal
+}
+
+var (
+	connection       *Conn
+	firewalldRunning bool      // is Firewalld service running
+	onReloaded       []*func() // callbacks when Firewalld has been reloaded
+)
+
+func FirewalldInit() {
+	var err error
+
+	connection, err = newConnection()
+
+	if err != nil {
+		logrus.Errorf("Failed to connect to D-Bus system bus: %s", err)
+	}
+
+	firewalldRunning = checkRunning()
+}
+
+// New() establishes a connection to the system bus.
+func newConnection() (*Conn, error) {
+	c := new(Conn)
+	if err := c.initConnection(); err != nil {
+		return nil, err
+	}
+
+	return c, nil
+}
+
+// Innitialize D-Bus connection.
+func (c *Conn) initConnection() error {
+	var err error
+
+	c.sysconn, err = dbus.SystemBus()
+	if err != nil {
+		return err
+	}
+
+	// This never fails, even if the service is not running atm.
+	c.sysobj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath))
+
+	rule := fmt.Sprintf("type='signal',path='%s',interface='%s',sender='%s',member='Reloaded'",
+		dbusPath, dbusInterface, dbusInterface)
+	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
+
+	rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'",
+		dbusInterface)
+	c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule)
+
+	c.signal = make(chan *dbus.Signal, 10)
+	c.sysconn.Signal(c.signal)
+	go signalHandler()
+
+	return nil
+}
+
+func signalHandler() {
+	if connection != nil {
+		for signal := range connection.signal {
+			if strings.Contains(signal.Name, "NameOwnerChanged") {
+				firewalldRunning = checkRunning()
+				dbusConnectionChanged(signal.Body)
+			} else if strings.Contains(signal.Name, "Reloaded") {
+				reloaded()
+			}
+		}
+	}
+}
+
+func dbusConnectionChanged(args []interface{}) {
+	name := args[0].(string)
+	old_owner := args[1].(string)
+	new_owner := args[2].(string)
+
+	if name != dbusInterface {
+		return
+	}
+
+	if len(new_owner) > 0 {
+		connectionEstablished()
+	} else if len(old_owner) > 0 {
+		connectionLost()
+	}
+}
+
+func connectionEstablished() {
+	reloaded()
+}
+
+func connectionLost() {
+	// Doesn't do anything for now. Libvirt also doesn't react to this.
+}
+
+// call all callbacks
+func reloaded() {
+	for _, pf := range onReloaded {
+		(*pf)()
+	}
+}
+
+// add callback
+func OnReloaded(callback func()) {
+	for _, pf := range onReloaded {
+		if pf == &callback {
+			return
+		}
+	}
+	onReloaded = append(onReloaded, &callback)
+}
+
+// Call some remote method to see whether the service is actually running.
+func checkRunning() bool {
+	var zone string
+	var err error
+
+	if connection != nil {
+		err = connection.sysobj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone)
+		logrus.Infof("Firewalld running: %t", err == nil)
+		return err == nil
+	}
+	logrus.Info("Firewalld not running")
+	return false
+}
+
+// Firewalld's passthrough method simply passes args through to iptables/ip6tables
+func Passthrough(ipv IPV, args ...string) ([]byte, error) {
+	var output string
+
+	logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args)
+	err := connection.sysobj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output)
+	if output != "" {
+		logrus.Debugf("passthrough output: %s", output)
+	}
+
+	return []byte(output), err
+}

+ 78 - 0
pkg/iptables/firewalld_test.go

@@ -0,0 +1,78 @@
+package iptables
+
+import (
+	"net"
+	"strconv"
+	"testing"
+)
+
+func TestFirewalldInit(t *testing.T) {
+	FirewalldInit()
+}
+
+func TestReloaded(t *testing.T) {
+	var err error
+	var fwdChain *Chain
+
+	fwdChain, err = NewChain("FWD", "lo", Filter)
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer fwdChain.Remove()
+
+	// copy-pasted from iptables_test:TestLink
+	ip1 := net.ParseIP("192.168.1.1")
+	ip2 := net.ParseIP("192.168.1.2")
+	port := 1234
+	proto := "tcp"
+
+	err = fwdChain.Link(Append, ip1, ip2, port, proto)
+	if err != nil {
+		t.Fatal(err)
+	} else {
+		// to be re-called again later
+		OnReloaded(func() { fwdChain.Link(Append, ip1, ip2, port, proto) })
+	}
+
+	rule1 := []string{
+		"-i", fwdChain.Bridge,
+		"-o", fwdChain.Bridge,
+		"-p", proto,
+		"-s", ip1.String(),
+		"-d", ip2.String(),
+		"--dport", strconv.Itoa(port),
+		"-j", "ACCEPT"}
+
+	if !Exists(fwdChain.Table, fwdChain.Name, rule1...) {
+		t.Fatalf("rule1 does not exist")
+	}
+
+	// flush all rules
+	fwdChain.Remove()
+
+	reloaded()
+
+	// make sure the rules have been recreated
+	if !Exists(fwdChain.Table, fwdChain.Name, rule1...) {
+		t.Fatalf("rule1 hasn't been recreated")
+	}
+}
+
+func TestPassthrough(t *testing.T) {
+	rule1 := []string{
+		"-i", "lo",
+		"-p", "udp",
+		"--dport", "123",
+		"-j", "ACCEPT"}
+
+	if firewalldRunning {
+		_, err := Passthrough(Iptables, append([]string{"-A"}, rule1...)...)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if !Exists(Filter, "INPUT", rule1...) {
+			t.Fatalf("rule1 does not exist")
+		}
+	}
+
+}

+ 7 - 0
pkg/iptables/iptables.go

@@ -275,6 +275,13 @@ func Exists(table Table, chain string, rule ...string) bool {
 
 // Call 'iptables' system command, passing supplied arguments
 func Raw(args ...string) ([]byte, error) {
+	if firewalldRunning {
+		output, err := Passthrough(Iptables, args...)
+		if err == nil || !strings.Contains(err.Error(), "was not provided by any .service files") {
+			return output, err
+		}
+
+	}
 
 	if err := initCheck(); err != nil {
 		return nil, err