Skip to content

Commit

Permalink
Add docker interfaces to firewalld docker zone
Browse files Browse the repository at this point in the history
If firewalld is running, create a new docker zone and
add the docker interfaces to the docker zone to allow
container networking for distros with firewalld enabled

Fixes: #2496

Signed-off-by: Arko Dasgupta <arko.dasgupta@docker.com>
  • Loading branch information
Arko Dasgupta committed May 8, 2020
1 parent 1ea375d commit 7a72092
Show file tree
Hide file tree
Showing 2 changed files with 159 additions and 10 deletions.
156 changes: 146 additions & 10 deletions iptables/firewalld.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,20 +19,46 @@ const (
// Ebtables point to bridge table
Ebtables IPV = "eb"
)

const (
dbusInterface = "org.fedoraproject.FirewallD1"
dbusPath = "/org/fedoraproject/FirewallD1"
dbusInterface = "org.fedoraproject.FirewallD1"
dbusPath = "/org/fedoraproject/FirewallD1"
dbusConfigPath = "/org/fedoraproject/FirewallD1/config"
dockerZone = "docker"
)

// Conn is a connection to firewalld dbus endpoint.
type Conn struct {
sysconn *dbus.Conn
sysobj dbus.BusObject
signal chan *dbus.Signal
sysconn *dbus.Conn
sysObj dbus.BusObject
sysConfObj dbus.BusObject
signal chan *dbus.Signal
}

// ZoneSettings holds the firewalld zone settings, documented in
// https://firewalld.org/documentation/man-pages/firewalld.dbus.html
type ZoneSettings struct {
version string
name string
description string
unused bool
target string
services []string
ports [][]interface{}
icmpBlocks []string
masquerade bool
forwardPorts [][]interface{}
interfaces []string
sourceAddresses []string
richRules []string
protocols []string
sourcePorts [][]interface{}
icmpBlockInversion bool
}

var (
connection *Conn
connection *Conn

firewalldRunning bool // is Firewalld service running
onReloaded []*func() // callbacks when Firewalld has been reloaded
)
Expand All @@ -51,6 +77,9 @@ func FirewalldInit() error {
}
if connection != nil {
go signalHandler()
if err := setupDockerZone(); err != nil {
return err
}
}

return nil
Expand All @@ -76,8 +105,8 @@ func (c *Conn) initConnection() error {
}

// This never fails, even if the service is not running atm.
c.sysobj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath))

c.sysObj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath))
c.sysConfObj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusConfigPath))
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)
Expand Down Expand Up @@ -150,7 +179,7 @@ func checkRunning() bool {
var err error

if connection != nil {
err = connection.sysobj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone)
err = connection.sysObj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone)
return err == nil
}
return false
Expand All @@ -160,8 +189,115 @@ func checkRunning() bool {
func Passthrough(ipv IPV, args ...string) ([]byte, error) {
var output string
logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args)
if err := connection.sysobj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output); err != nil {
if err := connection.sysObj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output); err != nil {
return nil, err
}
return []byte(output), nil
}

// getDockerZoneSettings converts the ZoneSettings struct into a interface slice
func getDockerZoneSettings() []interface{} {
settings := ZoneSettings{
version: "1.0",
name: dockerZone,
description: "zone for docker bridge network interfaces",
target: "ACCEPT",
}
slice := []interface{}{
settings.version,
settings.name,
settings.description,
settings.unused,
settings.target,
settings.services,
settings.ports,
settings.icmpBlocks,
settings.masquerade,
settings.forwardPorts,
settings.interfaces,
settings.sourceAddresses,
settings.richRules,
settings.protocols,
settings.sourcePorts,
settings.icmpBlockInversion,
}
return slice

}

// setupDockerZone creates a zone called docker in firewalld which includes docker interfaces to allow
// container networking
func setupDockerZone() error {
var zones []string
// Check if zone exists
if err := connection.sysObj.Call(dbusInterface+".zone.getZones", 0).Store(&zones); err != nil {
return err
}
if contains(zones, dockerZone) {
logrus.Infof("Firewalld: %s zone already exists, returning", dockerZone)
return nil
}
logrus.Debugf("Firewalld: creating %s zone", dockerZone)

settings := getDockerZoneSettings()
// Permanent
if err := connection.sysConfObj.Call(dbusInterface+".config.addZone", 0, dockerZone, settings).Err; err != nil {
return err
}
// Reload for change to take effect
if err := connection.sysObj.Call(dbusInterface+".reload", 0).Err; err != nil {
return err
}

return nil
}

// AddInterfaceFirewalld adds the interface to the trusted zone
func AddInterfaceFirewalld(intf string) error {
var intfs []string
// Check if interface is already added to the zone
if err := connection.sysObj.Call(dbusInterface+".zone.getInterfaces", 0, dockerZone).Store(&intfs); err != nil {
return err
}
// Return if interface is already part of the zone
if contains(intfs, intf) {
logrus.Infof("Firewalld: interface %s already part of %s zone, returning", intf, dockerZone)
return nil
}

logrus.Debugf("Firewalld: adding %s interface to %s zone", intf, dockerZone)
// Runtime
if err := connection.sysObj.Call(dbusInterface+".zone.addInterface", 0, dockerZone, intf).Err; err != nil {
return err
}
return nil
}

// DelInterfaceFirewalld removes the interface from the trusted zone
func DelInterfaceFirewalld(intf string) error {
var intfs []string
// Check if interface is part of the zone
if err := connection.sysObj.Call(dbusInterface+".zone.getInterfaces", 0, dockerZone).Store(&intfs); err != nil {
return err
}
// Remove interface if it exists
if !contains(intfs, intf) {
return fmt.Errorf("Firewalld: unable to find interface %s in %s zone", intf, dockerZone)
}

logrus.Debugf("Firewalld: removing %s interface from %s zone", intf, dockerZone)
// Runtime
if err := connection.sysObj.Call(dbusInterface+".zone.removeInterface", 0, dockerZone, intf).Err; err != nil {
return err
}
return nil
}

func contains(list []string, val string) bool {
for _, v := range list {
if v == val {
return true
}
}
return false
}
13 changes: 13 additions & 0 deletions iptables/iptables.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,19 @@ func ProgramChain(c *ChainInfo, bridgeName string, hairpinMode, enable bool) err
return errors.New("Could not program chain, missing chain name")
}

// Either add or remove the interface from the firewalld zone
if firewalldRunning {
if enable {
if err := AddInterfaceFirewalld(bridgeName); err != nil {
return err
}
} else {
if err := DelInterfaceFirewalld(bridgeName); err != nil {
return err
}
}
}

switch c.Table {
case Nat:
preroute := []string{
Expand Down

0 comments on commit 7a72092

Please sign in to comment.