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.
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.
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.
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.
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.
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.
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.
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.
queryService or queryPort for a targeted, read-only check.