Inspect firewalld Safely Through Its D-Bus Interface

Firewalld talks over D-Bus long before firewall-cmd ever runs, and busctl lets you read that conversation directly without risking a single rule. You will finish with a small set of busctl commands for discovering firewalld's D-Bus API and reading the active and permanent settings of a zone. The examples use the locally installed firewalld 2.1.1 package. They do not change firewall state.

Allow about fifteen minutes. You need a shell, the busctl utility from systemd, and firewalld running on the host you are inspecting. Read-only calls normally need no sudo. If firewalld is stopped, the commands cannot reach its well-known D-Bus name; starting it is an operational decision outside this inspection.

1. Confirm the local version and service state

Start with ordinary, read-only checks. This prevents a common distraction: copying an example from a different firewalld release and assuming its interface is identical.

$ dpkg-query -W -f='${Package} ${Version}\n' firewalld
firewalld 2.1.1-1
$ command -v busctl
/usr/bin/busctl
$ systemctl is-active firewalld
active

RPM-based systems can use rpm -q firewalld instead of the dpkg-query command; your package revision and service state will differ. If the last command prints inactive, stop at this checkpoint or arrange an approved maintenance window. Do not start a firewall daemon merely to make a read-only example pass.

2. Discover the root object

The manpage describes one well-known service name, org.fedoraproject.FirewallD1, and the root object path /org/fedoraproject/FirewallD1. Ask D-Bus for the interfaces exported at that path:

$ busctl introspect org.fedoraproject.FirewallD1 \
    /org/fedoraproject/FirewallD1
NAME                                  TYPE      SIGNATURE RESULT/VALUE
org.fedoraproject.FirewallD1          interface -         -
org.fedoraproject.FirewallD1          method    -         reload
org.fedoraproject.FirewallD1          method    -         getDefaultZone
org.fedoraproject.FirewallD1          method    s         setDefaultZone
org.fedoraproject.FirewallD1          property  s         version
org.fedoraproject.FirewallD1          property  s         state

The full listing is longer than this excerpt. Look for the general interface, the .zone runtime interface, and the .config object used for permanent configuration. introspect only describes the exported API: it does not reload the daemon or alter a rule.

If you see "The name org.fedoraproject.FirewallD1 was not provided", firewalld is not registered on the system bus. Check systemctl status firewalld and the service logs with your normal operating procedure. A missing D-Bus name is not evidence that a particular zone or service is absent.

3. Read the default zone and daemon properties

Call getDefaultZone with its documented empty input signature. busctl call prints the returned D-Bus type followed by its value:

$ busctl call org.fedoraproject.FirewallD1 \
    /org/fedoraproject/FirewallD1 \
    org.fedoraproject.FirewallD1 getDefaultZone
s "public"

The zone name is host-specific. Save it in a shell variable if you will use it again, but quote it whenever it is passed to another command:

$ ZONE=$(busctl call org.fedoraproject.FirewallD1 \
    /org/fedoraproject/FirewallD1 \
    org.fedoraproject.FirewallD1 getDefaultZone | awk '{print $2}' | tr -d '"')
$ printf 'default zone: %s\n' "$ZONE"
default zone: public
$ busctl get-property org.fedoraproject.FirewallD1 \
    /org/fedoraproject/FirewallD1 \
    org.fedoraproject.FirewallD1 version
s "2.1.1"

Warning: do not use setDefaultZone for a test. The manpage says it changes both runtime and permanent configuration, and it changes which connections and interfaces use the default zone.

4. Read the active zone settings

Runtime zone operations live on /org/fedoraproject/FirewallD1 under org.fedoraproject.FirewallD1.zone. The current API is getZoneSettings2, which takes one string and returns a dictionary of typed values:

$ busctl call org.fedoraproject.FirewallD1 \
    /org/fedoraproject/FirewallD1 \
    org.fedoraproject.FirewallD1.zone getZoneSettings2 s "$ZONE"
a{sv} 7 "version" s "2.0" "name" s "public" "target" s "default" \
  "services" as 2 "dhcpv6-client" "ssh" "ports" a(ss) 0 \
  "masquerade" b false "interfaces" as 1 "eno1"

The actual dictionary varies with the machine. The documented keys include the zone version, name, description, target, services, port and protocol pairs, ICMP blocks, masquerading, forward ports, interfaces, sources and rich rules. Empty values may be omitted, so do not parse a fixed field count. The older getZoneSettings method is deprecated; use the 2 form when the installed interface provides it.

5. Compare permanent configuration without reloading

Permanent zone objects are exposed below /org/fedoraproject/FirewallD1/config/zone/. The final path component is an object identifier, not necessarily the zone name. First discover it:

$ busctl tree org.fedoraproject.FirewallD1 \
    /org/fedoraproject/FirewallD1/config/zone
|--/org/fedoraproject/FirewallD1/config/zone/public

Use the path reported on your machine. Read the permanent dictionary through the configuration interface:

$ busctl call org.fedoraproject.FirewallD1 \
    /org/fedoraproject/FirewallD1/config/zone/public \
    org.fedoraproject.FirewallD1.config.zone getSettings2
a{sv} ...

This is the useful comparison: getZoneSettings2 describes runtime state, while getSettings2 describes the saved zone. A runtime-only addition can therefore appear in the first result and not the second. Conversely, saved changes do not become runtime rules until firewalld reloads. Reading either dictionary does not apply a change.

6. Query one rule without changing it

For a focused check, ask whether the active zone contains a service. The method takes the zone and service names, then returns a boolean:

$ busctl call org.fedoraproject.FirewallD1 \
    /org/fedoraproject/FirewallD1 \
    org.fedoraproject.FirewallD1.zone queryService ss "$ZONE" ssh
b true

Use a service name returned by listServices or one already visible in the zone dictionary. Replace ssh with your actual service. For a port query, the corresponding method is queryPort and its arguments are the zone, port and protocol:

$ busctl call org.fedoraproject.FirewallD1 \
    /org/fedoraproject/FirewallD1 \
    org.fedoraproject.FirewallD1.zone queryPort sss "$ZONE" 443 tcp
b false

false means the item is not enabled in that zone's runtime settings. It does not tell you whether another zone allows it, nor whether a separate direct or rich rule affects the traffic.

7. Know the dangerous calls

The same interface includes methods that are easy to mistake for inspection. Do not paste these into a diagnostic script without an explicit change review:

Warning: there is no general undo for an accidental D-Bus write. Recovery depends on a known-good permanent configuration, backups and the change procedure for the host. For this guide, stay with introspection, properties and query methods.

Done means