cd381aea56
libnetwork/etchosts/etchosts_test.go:167:54: empty-lines: extra empty line at the end of a block (revive) libnetwork/osl/route_linux.go:185:74: empty-lines: extra empty line at the start of a block (revive) libnetwork/osl/sandbox_linux_test.go:323:36: empty-lines: extra empty line at the start of a block (revive) libnetwork/bitseq/sequence.go:412:48: empty-lines: extra empty line at the start of a block (revive) libnetwork/datastore/datastore_test.go:67:46: empty-lines: extra empty line at the end of a block (revive) libnetwork/datastore/mock_store.go:34:60: empty-lines: extra empty line at the end of a block (revive) libnetwork/iptables/firewalld.go:202:44: empty-lines: extra empty line at the end of a block (revive) libnetwork/iptables/firewalld_test.go:76:36: empty-lines: extra empty line at the end of a block (revive) libnetwork/iptables/iptables.go:256:67: empty-lines: extra empty line at the end of a block (revive) libnetwork/iptables/iptables.go:303:128: empty-lines: extra empty line at the start of a block (revive) libnetwork/networkdb/cluster.go:183:72: empty-lines: extra empty line at the end of a block (revive) libnetwork/ipams/null/null_test.go:44:38: empty-lines: extra empty line at the end of a block (revive) libnetwork/drivers/macvlan/macvlan_store.go:45:52: empty-lines: extra empty line at the end of a block (revive) libnetwork/ipam/allocator_test.go:1058:39: empty-lines: extra empty line at the start of a block (revive) libnetwork/drivers/bridge/port_mapping.go:88:111: empty-lines: extra empty line at the end of a block (revive) libnetwork/drivers/bridge/link.go:26:90: empty-lines: extra empty line at the end of a block (revive) libnetwork/drivers/bridge/setup_ipv6_test.go:17:34: empty-lines: extra empty line at the end of a block (revive) libnetwork/drivers/bridge/setup_ip_tables.go:392:4: empty-lines: extra empty line at the start of a block (revive) libnetwork/drivers/bridge/bridge.go:804:50: empty-lines: extra empty line at the start of a block (revive) libnetwork/drivers/overlay/ov_serf.go:183:29: empty-lines: extra empty line at the start of a block (revive) libnetwork/drivers/overlay/ov_utils.go:81:64: empty-lines: extra empty line at the end of a block (revive) libnetwork/drivers/overlay/peerdb.go:172:67: empty-lines: extra empty line at the start of a block (revive) libnetwork/drivers/overlay/peerdb.go:209:67: empty-lines: extra empty line at the start of a block (revive) libnetwork/drivers/overlay/peerdb.go:344:89: empty-lines: extra empty line at the start of a block (revive) libnetwork/drivers/overlay/peerdb.go:436:63: empty-lines: extra empty line at the start of a block (revive) libnetwork/drivers/overlay/overlay.go:183:36: empty-lines: extra empty line at the start of a block (revive) libnetwork/drivers/overlay/encryption.go:69:28: empty-lines: extra empty line at the end of a block (revive) libnetwork/drivers/overlay/ov_network.go:563:81: empty-lines: extra empty line at the start of a block (revive) libnetwork/default_gateway.go:32:43: empty-lines: extra empty line at the start of a block (revive) libnetwork/errors_test.go:9:40: empty-lines: extra empty line at the start of a block (revive) libnetwork/service_common.go:184:64: empty-lines: extra empty line at the end of a block (revive) libnetwork/endpoint.go:161:55: empty-lines: extra empty line at the end of a block (revive) libnetwork/store.go:320:33: empty-lines: extra empty line at the end of a block (revive) libnetwork/store_linux_test.go:11:38: empty-lines: extra empty line at the end of a block (revive) libnetwork/sandbox.go:571:36: empty-lines: extra empty line at the start of a block (revive) libnetwork/service_common.go:317:246: empty-lines: extra empty line at the start of a block (revive) libnetwork/endpoint.go:550:17: empty-lines: extra empty line at the end of a block (revive) libnetwork/sandbox_dns_unix.go:213:106: empty-lines: extra empty line at the start of a block (revive) libnetwork/controller.go:676:85: empty-lines: extra empty line at the end of a block (revive) libnetwork/agent.go:876:60: empty-lines: extra empty line at the end of a block (revive) libnetwork/resolver.go:324:69: empty-lines: extra empty line at the end of a block (revive) libnetwork/network.go:1153:92: empty-lines: extra empty line at the end of a block (revive) libnetwork/network.go:1955:67: empty-lines: extra empty line at the start of a block (revive) libnetwork/network.go:2235:9: empty-lines: extra empty line at the start of a block (revive) libnetwork/libnetwork_internal_test.go:336:26: empty-lines: extra empty line at the start of a block (revive) libnetwork/resolver_test.go:76:35: empty-lines: extra empty line at the end of a block (revive) libnetwork/libnetwork_test.go:303:38: empty-lines: extra empty line at the end of a block (revive) libnetwork/libnetwork_test.go:985:46: empty-lines: extra empty line at the end of a block (revive) libnetwork/ipam/allocator_test.go:1263:37: empty-lines: extra empty line at the start of a block (revive) libnetwork/errors_test.go:9:40: empty-lines: extra empty line at the end of a block (revive) Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
304 lines
7.6 KiB
Go
304 lines
7.6 KiB
Go
//go:build linux
|
|
// +build linux
|
|
|
|
package iptables
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
dbus "github.com/godbus/dbus/v5"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
// IPV defines the table string
|
|
type IPV string
|
|
|
|
const (
|
|
// Iptables point ipv4 table
|
|
Iptables IPV = "ipv4"
|
|
// IP6Tables point to ipv6 table
|
|
IP6Tables IPV = "ipv6"
|
|
// Ebtables point to bridge table
|
|
Ebtables IPV = "eb"
|
|
)
|
|
|
|
const (
|
|
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
|
|
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
|
|
|
|
firewalldRunning bool // is Firewalld service running
|
|
onReloaded []*func() // callbacks when Firewalld has been reloaded
|
|
)
|
|
|
|
// FirewalldInit initializes firewalld management code.
|
|
func FirewalldInit() error {
|
|
var err error
|
|
|
|
if connection, err = newConnection(); err != nil {
|
|
return fmt.Errorf("Failed to connect to D-Bus system bus: %v", err)
|
|
}
|
|
firewalldRunning = checkRunning()
|
|
if !firewalldRunning {
|
|
connection.sysconn.Close()
|
|
connection = nil
|
|
}
|
|
if connection != nil {
|
|
go signalHandler()
|
|
if err := setupDockerZone(); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
// Initialize 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))
|
|
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)
|
|
|
|
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)
|
|
|
|
return nil
|
|
}
|
|
|
|
func signalHandler() {
|
|
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)
|
|
oldOwner := args[1].(string)
|
|
newOwner := args[2].(string)
|
|
|
|
if name != dbusInterface {
|
|
return
|
|
}
|
|
|
|
if len(newOwner) > 0 {
|
|
connectionEstablished()
|
|
} else if len(oldOwner) > 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)()
|
|
}
|
|
}
|
|
|
|
// OnReloaded 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)
|
|
return err == nil
|
|
}
|
|
return false
|
|
}
|
|
|
|
// 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)
|
|
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",
|
|
}
|
|
return []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,
|
|
}
|
|
}
|
|
|
|
// 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
|
|
}
|