1

I'm attempting to control firewalld via the Python dbus module.

I'd like to add an ip address to the trusted zone for both my current runtime as well as my permanent configuration.

Here's the documentation for firewalld's dbus interface: http://manpages.ubuntu.com/manpages/wily/man5/firewalld.dbus.5.html

What works: The runtime configuration

I'm able to add it to the runtime configuration just fine with this:

def trustIP(ip):
    ''' firewalld must already be running '''
    from dbus import SystemBus
    bus = SystemBus()
    runtimeProxy = bus.get_object('org.fedoraproject.FirewallD1',
                                 '/org/fedoraproject/FirewallD1')
    runtimeProxy.addSource('trusted', ip)

Pretty simple.

What doesn't work: The permanent configuration

Adding it to the permanent configuration has proved to be more difficult. Here's what I've tried so far interactively:

>>> from dbus import SystemBus
>>> bus = SystemBus()

# First I need to find out which object is for the trusted zone...
>>> config = bus.get_object('org.fedoraproject.FirewallD1',
                           '/org/fedoraproject/FirewallD1/config')
>>> config.getZoneByName('trusted')
dbus.ObjectPath('/org/fedoraproject/FirewallD1/config/zone/7')

>>> permanentProxy = bus.get_object('org.fedoraproject.FirewallD1', 
                                   '/org/fedoraproject/FirewallD1/config/zone/7')

# A quick check to make sure I have the right object:
>>> permanentProxy.getShort()
dbus.String(u'Trusted')

# Exactly what I expected, so move on and...
>>> permanentProxy.addSource('aaa.xxx.yyy.zzz')  # Actual ip removed...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.7/site-packages/dbus/proxies.py", line 145, in __call__
    **keywords)
  File "/usr/lib64/python2.7/site-packages/dbus/connection.py", line 651, in call_blocking
    message, timeout)
dbus.exceptions.DBusException:
    org.freedesktop.DBus.Python.dbus.exceptions.DBusException:
        dbus_to_python() takes exactly 1 argument (2 given)

I also tried checking permanentProxy.getDescription(), which returned the description as it should have, and I tried permanentProxy.setDescription('test') which failed with the exact same stack trace as permanentProxy.addSource('aaa.xxx.yyy.zzz').

I would jump to the conclusion that the bug lies in the python dbus module and assume it somehow doesn't handle arguments properly, except for the fact that runtimeProxy.addSource('trusted', ip) involved two arguments and works perfectly. config.getZoneByName('trusted') even has the same signature as permanentProxy.addSource('aaa.xxx.yyy.zzz')`, exactly one string, and works perfectly.

So maybe there's something weird I'm missing? But I don't know what that would be...

More stuff I tried without success

I considered the possibility that maybe addSource is supposed to be called without the string argument at all and maybe curries somehow or something, so I tried this:

>>> permanentProxy.addSource()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.7/site-packages/dbus/proxies.py", line 145, in __call__
    **keywords)
  File "/usr/lib64/python2.7/site-packages/dbus/connection.py", line 651, in call_blocking
    message, timeout)
dbus.exceptions.DBusException: org.freedesktop.DBus.Python.TypeError: Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/slip/dbus/service.py", line 123, in reply_handler
    result = method(self, *p, **k)
TypeError: addSource() takes at least 2 arguments (2 given)

This is just even weirder now... I have one Traceback within another traceback insisting that I need to pass in at least 2 arguments, but also saying I gave it two arguments (and I actually only gave it one, so how'd it come up with two anyways?)

A few more things I tried without success:

>>> permanentProxy.addSource(dbus_interface='org.fedoraproject.FirewallD1.config.zone')
ERROR:dbus.connection:Unable to set arguments () according to signature u's': <type 'exceptions.TypeError'>: More items found in D-Bus signature than in Python arguments
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.7/site-packages/dbus/proxies.py", line 145, in __call__
    **keywords)
  File "/usr/lib64/python2.7/site-packages/dbus/connection.py", line 641, in call_blocking
    message.append(signature=signature, *args)
TypeError: More items found in D-Bus signature than in Python arguments

>>> permanentProxy.addSource('aaa.xxx.yyy.zzz', dbus_interface='org.fedoraproject.FirewallD1.config.zone')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.7/site-packages/dbus/proxies.py", line 145, in __call__
    **keywords)
  File "/usr/lib64/python2.7/site-packages/dbus/connection.py", line 651, in call_blocking
    message, timeout)
dbus.exceptions.DBusException:
    org.freedesktop.DBus.Python.dbus.exceptions.DBusException:
        dbus_to_python() takes exactly 1 argument (2 given)

>>> from dbus import Interface
>>> Interface(permanentProxy, 'org.fedoraproject.FirewallD1.config.zone').addSource('aaa.xxx.yyy.zzz')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib64/python2.7/site-packages/dbus/proxies.py", line 145, in __call__
    **keywords)
  File "/usr/lib64/python2.7/site-packages/dbus/connection.py", line 651, in call_blocking
    message, timeout)
dbus.exceptions.DBusException:
    org.freedesktop.DBus.Python.dbus.exceptions.DBusException:
        dbus_to_python() takes exactly 1 argument (2 given)

Gah!

This really seems like a bug in dbus... somehow it's initially resolving addSource incorrectly and thinking that it needs fewer arguments, but if you give it fewer arguments like it wants, it'll pass that erroneous check, and then it'll properly resolve and fail because your arguments don't match it.

That's my theory anyways. Is someone seeing something I'm not? Is there some way I can work around this bug, if there really is one? IE... is there some kind of internal method I can use on dbus that will force it to call the proper method?

ArtOfWarfare
  • 20,617
  • 19
  • 137
  • 193
  • Looking in the source... I found `dbus.connection.Connection.call_blocking`, which on line 651 calls `self.send_message_with_reply_and_block`... which isn't defined on the class so must be inherited from its superclass, `_dbus_bindings.Connection`... which I haven't managed to find the source for yet. http://dbus.freedesktop.org/doc/dbus-python/api/ – ArtOfWarfare Dec 22 '15 at 21:11
  • I just realized that the failing conversion is from dbus to python... perhaps my call is being successful and it's just failing at telling me that it has been successful? – ArtOfWarfare Dec 22 '15 at 21:22

1 Answers1

1

The following works for me:

>>> import dbus
>>> bus = dbus.SystemBus()
>>> config = bus.get_object('org.fedoraproject.FirewallD1',
...                            '/org/fedoraproject/FirewallD1/config')
>>> path = config.getZoneByName('trusted')
>>> zone = bus.get_object('org.fedoraproject.FirewallD1', path)
>>> zone.addSource('192.168.1.0/24')

At this point, if I look in /etc/firewalld/zones/trusted.xml, I can see that the source address has been added as expected:

<?xml version="1.0" encoding="utf-8"?>
<zone target="ACCEPT">
  <short>Trusted</short>
  <description>All network connections are accepted.</description>
  <interface name="docker0"/>
  <interface name="virbr0"/>
  <source address="192.168.1.0/24"/>
</zone>

...indicating that I have successfully changed the persistent configuration.

The above also works if I use a literal path in the second get_object call, instead of the return value from config.getZoneByName.

For what it's worth, I'm running:

  • Fedora 23
  • firewalld-0.3.14.2-4.fc23.noarch
  • dbus-1.10.6-1.fc23.x86_64
  • dbus-python-1.2.0-12.fc23.x86_64

UPDATE

You're not seeing anything newer because you're on CentOS, rather than Fedora. It looks like the easiest way of solving this particular task may be to use the firewall python module that ships with FirewallD. The following works for me on CentOS 7:

>>> from firewall.client import *
>>> client = FirewallClient()
>>> zone = client.config().getZoneByName('public')
>>> settings = zone.getSettings()
>>> settings.addSource('192.168.1.0/24')
>>> zone.update(settings)

ANOTHER UPDATE

Browsing through the source to the firewall.client module, you can do this via straight dbus like this:

>>> zone = bus.get_object('org.fedoraproject.FirewallD1', path)
>>> settings = zone.getSettings()
>>> settings[11].append('192.168.20.0/24')
>>> zone.update(settings)

This also works fine under CentOS...but you're much better off using the firewall module.

larsks
  • 277,717
  • 41
  • 399
  • 399
  • I have dbus 1.6.12... yum doesn't say anything newer is available. The Python module is 1.1.1. firewalld is 0.3.9 - also the newest version available according to yum. And CentOS 7. Anyways, doing the exact same code as you didn't work for me - I got another dbus stack trace. Further, the file you mentioned doesn't exist. All that is in the `/etc/firewalld/zones` directory is `public.xml`. I'll update a few things and see if that fixes anything for me. – ArtOfWarfare Dec 23 '15 at 15:31
  • Do you happen to know how I can update to the version you're using? `yum` isn't letting me update because, according to it, I have the newest version already... that suggests that I need to add a different repository to it or something. – ArtOfWarfare Dec 23 '15 at 15:52
  • Your update works perfectly for me - thanks! This stuff all seems ludicrously under-documented to me - until you mentioned it, I had no idea this `firewall` module even existed. How did you know it existed? – ArtOfWarfare Dec 23 '15 at 16:15
  • I looked at the source to `/usr/bin/firewall-cmd`, because if you're looking for examples of the FirewallD API, that seems like a good place to start. – larsks Dec 23 '15 at 16:18
  • Is there a way to replace `dbus` in my first example (the working example for temporarily trusting an ip) with `firewall`? – ArtOfWarfare Dec 23 '15 at 16:38
  • I just realized there's also a `runtimeToPermanent()` function that org.fedoraproject.FirewallD1 defines... could have probably just used that. – ArtOfWarfare Dec 23 '15 at 17:44