github.com/adityamillind98/moby@v23.0.0-rc.4+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 return []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 } 228 229 // setupDockerZone creates a zone called docker in firewalld which includes docker interfaces to allow 230 // container networking 231 func setupDockerZone() error { 232 var zones []string 233 // Check if zone exists 234 if err := connection.sysObj.Call(dbusInterface+".zone.getZones", 0).Store(&zones); err != nil { 235 return err 236 } 237 if contains(zones, dockerZone) { 238 logrus.Infof("Firewalld: %s zone already exists, returning", dockerZone) 239 return nil 240 } 241 logrus.Debugf("Firewalld: creating %s zone", dockerZone) 242 243 settings := getDockerZoneSettings() 244 // Permanent 245 if err := connection.sysConfObj.Call(dbusInterface+".config.addZone", 0, dockerZone, settings).Err; err != nil { 246 return err 247 } 248 // Reload for change to take effect 249 if err := connection.sysObj.Call(dbusInterface+".reload", 0).Err; err != nil { 250 return err 251 } 252 253 return nil 254 } 255 256 // AddInterfaceFirewalld adds the interface to the trusted zone 257 func AddInterfaceFirewalld(intf string) error { 258 var intfs []string 259 // Check if interface is already added to the zone 260 if err := connection.sysObj.Call(dbusInterface+".zone.getInterfaces", 0, dockerZone).Store(&intfs); err != nil { 261 return err 262 } 263 // Return if interface is already part of the zone 264 if contains(intfs, intf) { 265 logrus.Infof("Firewalld: interface %s already part of %s zone, returning", intf, dockerZone) 266 return nil 267 } 268 269 logrus.Debugf("Firewalld: adding %s interface to %s zone", intf, dockerZone) 270 // Runtime 271 if err := connection.sysObj.Call(dbusInterface+".zone.addInterface", 0, dockerZone, intf).Err; err != nil { 272 return err 273 } 274 return nil 275 } 276 277 // DelInterfaceFirewalld removes the interface from the trusted zone 278 func DelInterfaceFirewalld(intf string) error { 279 var intfs []string 280 // Check if interface is part of the zone 281 if err := connection.sysObj.Call(dbusInterface+".zone.getInterfaces", 0, dockerZone).Store(&intfs); err != nil { 282 return err 283 } 284 // Remove interface if it exists 285 if !contains(intfs, intf) { 286 return fmt.Errorf("Firewalld: unable to find interface %s in %s zone", intf, dockerZone) 287 } 288 289 logrus.Debugf("Firewalld: removing %s interface from %s zone", intf, dockerZone) 290 // Runtime 291 if err := connection.sysObj.Call(dbusInterface+".zone.removeInterface", 0, dockerZone, intf).Err; err != nil { 292 return err 293 } 294 return nil 295 } 296 297 func contains(list []string, val string) bool { 298 for _, v := range list { 299 if v == val { 300 return true 301 } 302 } 303 return false 304 }