123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306 |
- //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",
- }
- 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
- }
|