github.com/rumpl/bof@v23.0.0-rc.2+incompatible/libnetwork/iptables/firewalld.go (about) 1 //go:build linux 2 // +build linux 3 4 package iptables 5 6 import ( 7 "fmt" 8 "strings" 9 10 dbus "github.com/godbus/dbus/v5" 11 "github.com/sirupsen/logrus" 12 ) 13 14 // IPV defines the table string 15 type IPV string 16 17 const ( 18 // Iptables point ipv4 table 19 Iptables IPV = "ipv4" 20 // IP6Tables point to ipv6 table 21 IP6Tables IPV = "ipv6" 22 // Ebtables point to bridge table 23 Ebtables IPV = "eb" 24 ) 25 26 const ( 27 dbusInterface = "org.fedoraproject.FirewallD1" 28 dbusPath = "/org/fedoraproject/FirewallD1" 29 dbusConfigPath = "/org/fedoraproject/FirewallD1/config" 30 dockerZone = "docker" 31 ) 32 33 // Conn is a connection to firewalld dbus endpoint. 34 type Conn struct { 35 sysconn *dbus.Conn 36 sysObj dbus.BusObject 37 sysConfObj dbus.BusObject 38 signal chan *dbus.Signal 39 } 40 41 // ZoneSettings holds the firewalld zone settings, documented in 42 // https://firewalld.org/documentation/man-pages/firewalld.dbus.html 43 type ZoneSettings struct { 44 version string 45 name string 46 description string 47 unused bool 48 target string 49 services []string 50 ports [][]interface{} 51 icmpBlocks []string 52 masquerade bool 53 forwardPorts [][]interface{} 54 interfaces []string 55 sourceAddresses []string 56 richRules []string 57 protocols []string 58 sourcePorts [][]interface{} 59 icmpBlockInversion bool 60 } 61 62 var ( 63 connection *Conn 64 65 firewalldRunning bool // is Firewalld service running 66 onReloaded []*func() // callbacks when Firewalld has been reloaded 67 ) 68 69 // FirewalldInit initializes firewalld management code. 70 func FirewalldInit() error { 71 var err error 72 73 if connection, err = newConnection(); err != nil { 74 return fmt.Errorf("Failed to connect to D-Bus system bus: %v", err) 75 } 76 firewalldRunning = checkRunning() 77 if !firewalldRunning { 78 connection.sysconn.Close() 79 connection = nil 80 } 81 if connection != nil { 82 go signalHandler() 83 if err := setupDockerZone(); err != nil { 84 return err 85 } 86 } 87 88 return nil 89 } 90 91 // New() establishes a connection to the system bus. 92 func newConnection() (*Conn, error) { 93 c := new(Conn) 94 if err := c.initConnection(); err != nil { 95 return nil, err 96 } 97 98 return c, nil 99 } 100 101 // Initialize D-Bus connection. 102 func (c *Conn) initConnection() error { 103 var err error 104 105 c.sysconn, err = dbus.SystemBus() 106 if err != nil { 107 return err 108 } 109 110 // This never fails, even if the service is not running atm. 111 c.sysObj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusPath)) 112 c.sysConfObj = c.sysconn.Object(dbusInterface, dbus.ObjectPath(dbusConfigPath)) 113 rule := fmt.Sprintf("type='signal',path='%s',interface='%s',sender='%s',member='Reloaded'", 114 dbusPath, dbusInterface, dbusInterface) 115 c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) 116 117 rule = fmt.Sprintf("type='signal',interface='org.freedesktop.DBus',member='NameOwnerChanged',path='/org/freedesktop/DBus',sender='org.freedesktop.DBus',arg0='%s'", 118 dbusInterface) 119 c.sysconn.BusObject().Call("org.freedesktop.DBus.AddMatch", 0, rule) 120 121 c.signal = make(chan *dbus.Signal, 10) 122 c.sysconn.Signal(c.signal) 123 124 return nil 125 } 126 127 func signalHandler() { 128 for signal := range connection.signal { 129 if strings.Contains(signal.Name, "NameOwnerChanged") { 130 firewalldRunning = checkRunning() 131 dbusConnectionChanged(signal.Body) 132 } else if strings.Contains(signal.Name, "Reloaded") { 133 reloaded() 134 } 135 } 136 } 137 138 func dbusConnectionChanged(args []interface{}) { 139 name := args[0].(string) 140 oldOwner := args[1].(string) 141 newOwner := args[2].(string) 142 143 if name != dbusInterface { 144 return 145 } 146 147 if len(newOwner) > 0 { 148 connectionEstablished() 149 } else if len(oldOwner) > 0 { 150 connectionLost() 151 } 152 } 153 154 func connectionEstablished() { 155 reloaded() 156 } 157 158 func connectionLost() { 159 // Doesn't do anything for now. Libvirt also doesn't react to this. 160 } 161 162 // call all callbacks 163 func reloaded() { 164 for _, pf := range onReloaded { 165 (*pf)() 166 } 167 } 168 169 // OnReloaded add callback 170 func OnReloaded(callback func()) { 171 for _, pf := range onReloaded { 172 if pf == &callback { 173 return 174 } 175 } 176 onReloaded = append(onReloaded, &callback) 177 } 178 179 // Call some remote method to see whether the service is actually running. 180 func checkRunning() bool { 181 var zone string 182 var err error 183 184 if connection != nil { 185 err = connection.sysObj.Call(dbusInterface+".getDefaultZone", 0).Store(&zone) 186 return err == nil 187 } 188 return false 189 } 190 191 // Passthrough method simply passes args through to iptables/ip6tables 192 func Passthrough(ipv IPV, args ...string) ([]byte, error) { 193 var output string 194 logrus.Debugf("Firewalld passthrough: %s, %s", ipv, args) 195 if err := connection.sysObj.Call(dbusInterface+".direct.passthrough", 0, ipv, args).Store(&output); err != nil { 196 return nil, err 197 } 198 return []byte(output), nil 199 } 200 201 // getDockerZoneSettings converts the ZoneSettings struct into a interface slice 202 func getDockerZoneSettings() []interface{} { 203 settings := ZoneSettings{ 204 version: "1.0", 205 name: dockerZone, 206 description: "zone for docker bridge network interfaces", 207 target: "ACCEPT", 208 } 209 slice := []interface{}{ 210 settings.version, 211 settings.name, 212 settings.description, 213 settings.unused, 214 settings.target, 215 settings.services, 216 settings.ports, 217 settings.icmpBlocks, 218 settings.masquerade, 219 settings.forwardPorts, 220 settings.interfaces, 221 settings.sourceAddresses, 222 settings.richRules, 223 settings.protocols, 224 settings.sourcePorts, 225 settings.icmpBlockInversion, 226 } 227 return slice 228 229 } 230 231 // setupDockerZone creates a zone called docker in firewalld which includes docker interfaces to allow 232 // container networking 233 func setupDockerZone() error { 234 var zones []string 235 // Check if zone exists 236 if err := connection.sysObj.Call(dbusInterface+".zone.getZones", 0).Store(&zones); err != nil { 237 return err 238 } 239 if contains(zones, dockerZone) { 240 logrus.Infof("Firewalld: %s zone already exists, returning", dockerZone) 241 return nil 242 } 243 logrus.Debugf("Firewalld: creating %s zone", dockerZone) 244 245 settings := getDockerZoneSettings() 246 // Permanent 247 if err := connection.sysConfObj.Call(dbusInterface+".config.addZone", 0, dockerZone, settings).Err; err != nil { 248 return err 249 } 250 // Reload for change to take effect 251 if err := connection.sysObj.Call(dbusInterface+".reload", 0).Err; err != nil { 252 return err 253 } 254 255 return nil 256 } 257 258 // AddInterfaceFirewalld adds the interface to the trusted zone 259 func AddInterfaceFirewalld(intf string) error { 260 var intfs []string 261 // Check if interface is already added to the zone 262 if err := connection.sysObj.Call(dbusInterface+".zone.getInterfaces", 0, dockerZone).Store(&intfs); err != nil { 263 return err 264 } 265 // Return if interface is already part of the zone 266 if contains(intfs, intf) { 267 logrus.Infof("Firewalld: interface %s already part of %s zone, returning", intf, dockerZone) 268 return nil 269 } 270 271 logrus.Debugf("Firewalld: adding %s interface to %s zone", intf, dockerZone) 272 // Runtime 273 if err := connection.sysObj.Call(dbusInterface+".zone.addInterface", 0, dockerZone, intf).Err; err != nil { 274 return err 275 } 276 return nil 277 } 278 279 // DelInterfaceFirewalld removes the interface from the trusted zone 280 func DelInterfaceFirewalld(intf string) error { 281 var intfs []string 282 // Check if interface is part of the zone 283 if err := connection.sysObj.Call(dbusInterface+".zone.getInterfaces", 0, dockerZone).Store(&intfs); err != nil { 284 return err 285 } 286 // Remove interface if it exists 287 if !contains(intfs, intf) { 288 return fmt.Errorf("Firewalld: unable to find interface %s in %s zone", intf, dockerZone) 289 } 290 291 logrus.Debugf("Firewalld: removing %s interface from %s zone", intf, dockerZone) 292 // Runtime 293 if err := connection.sysObj.Call(dbusInterface+".zone.removeInterface", 0, dockerZone, intf).Err; err != nil { 294 return err 295 } 296 return nil 297 } 298 299 func contains(list []string, val string) bool { 300 for _, v := range list { 301 if v == val { 302 return true 303 } 304 } 305 return false 306 }