Skip to content

Commit

Permalink
Add firewalld policy "docker-forwarding".
Browse files Browse the repository at this point in the history
Allow forwarding from any firewalld zone to the 'docker' zone.

This makes it possible to use routable IPv6 addresses on a bridge
network, with masquerading disabled, and have the host forward packets
to it.

Signed-off-by: Rob Murray <rob.murray@docker.com>
  • Loading branch information
robmry committed Apr 24, 2024
1 parent c8af8eb commit ff8de5e
Showing 1 changed file with 59 additions and 18 deletions.
77 changes: 59 additions & 18 deletions libnetwork/iptables/firewalld.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/containerd/log"
dbus "github.com/godbus/dbus/v5"
"github.com/pkg/errors"
)

// IPV defines the table string
Expand All @@ -22,10 +23,11 @@ const (
)

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

// Conn is a connection to firewalld dbus endpoint.
Expand Down Expand Up @@ -57,9 +59,21 @@ func firewalldInit() error {
}
if connection != nil {
go signalHandler()
if err := setupDockerZone(); err != nil {
zoneAdded, err := setupDockerZone()
if err != nil {
return err
}
policyAdded, policyAddErr := setupDockerForwardingPolicy()
if policyAddErr != nil {
// Log the error, but still reload firewalld if necessary.
log.G(context.TODO()).WithError(policyAddErr).Warnf("Firewalld: failed to add policy %s", dockerFwdPolicy)
}
if zoneAdded || policyAdded {
// Reload for changes to take effect.
if err := connection.sysObj.Call(dbusInterface+".reload", 0).Err; err != nil {
return err
}
}
}

return nil
Expand Down Expand Up @@ -186,10 +200,12 @@ type firewalldZone struct {
icmpBlockInversion bool
}

// settings returns the firewalldZone struct as an interface slice,
// which can be passed to "org.fedoraproject.FirewallD1.config.addZone".
// settings returns the firewalldZone struct as an interface slice, which can be
// passed to "org.fedoraproject.FirewallD1.config.addZone". Note that 'addZone',
// which is deprecated, requires this whole struct. Its replacement, 'addZone2'
// (introduced in firewalld 0.9.0) accepts a dictionary where only non-default
// values need to be specified.
func (z firewalldZone) settings() []interface{} {
// TODO(thaJeztah): does D-Bus require optional fields to be passed as well?
return []interface{}{
z.version,
z.name,
Expand All @@ -211,16 +227,16 @@ func (z firewalldZone) settings() []interface{} {
}

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

Expand All @@ -232,14 +248,39 @@ func setupDockerZone() error {
target: "ACCEPT",
}
if err := connection.sysConfObj.Call(dbusInterface+".config.addZone", 0, dockerZone, dz.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 false, err
}
return true, nil
}

return nil
// setupDockerForwardingPolicy creates a policy to allow forwarding to anywhere to the docker
// zone (where packets will be dealt with by docker's usual/non-firewalld configuration).
// The bool return value is true if a firewalld reload is required.
func setupDockerForwardingPolicy() (bool, error) {
// https://firewalld.org/documentation/man-pages/firewalld.dbus.html#FirewallD1.config
policy := map[string]interface{}{
"version": "1.0",
"description": "allow forwarding to the docker zone",
"ingress_zones": []string{"ANY"},
"egress_zones": []string{dockerZone},
"target": "ACCEPT",
}
if err := connection.sysConfObj.Call(dbusInterface+".config.addPolicy", 0, dockerFwdPolicy, policy).Err; err != nil {
var derr dbus.Error
if errors.As(err, &derr) {
if derr.Name == dbusInterface+".Exception" && strings.HasPrefix(err.Error(), "NAME_CONFLICT") {
log.G(context.TODO()).Debugf("Firewalld: %s policy already exists", dockerFwdPolicy)
return false, nil
}
if derr.Name == dbus.ErrMsgUnknownMethod.Name {
log.G(context.TODO()).Debugf("Firewalld: addPolicy %s: unknown method", dockerFwdPolicy)
return false, nil
}
}
return false, err
}
log.G(context.TODO()).Infof("Firewalld: created %s policy", dockerFwdPolicy)
return true, nil
}

// AddInterfaceFirewalld adds the interface to the trusted zone. It is a
Expand Down

0 comments on commit ff8de5e

Please sign in to comment.