Skip to content
Search Fedora

    Browse this documentation

    firewalld on Fedora: zones, runtime versus permanent configuration

    Fedora’s firewall is firewalld, not “append rules to iptables”. It organises rules into zones and splits configuration into runtime and permanent layers — the relationship between those two layers is what this page is about.

    Terminal window
    rpm -q firewalld
    sudo firewall-cmd --state
    firewalld-2.5.1-1.fc45.noarch
    running

    The default zone depends on how you installed

    Section titled “The default zone depends on how you installed”
    Terminal window
    sudo firewall-cmd --get-default-zone
    sudo firewall-cmd --get-zones
    public
    FedoraServer FedoraWorkstation block dmz drop external home internal nm-shared public trusted work

    Two measured machines give different default zones, and that is not a bug:

    Install methodDefault zoneAllowed out of the box
    F43 Server (kickstart firewall --enabled)FedoraServercockpit dhcpv6-client ssh
    F45 Cloudpublicdhcpv6-client mdns ssh

    The installer writes a different zone file per variant. So a tutorial claiming the default is “FedoraServer” or “public” is right and wrong at the same time — it depends on the variant.

    Both zones exist in the list (FedoraServer and FedoraWorkstation are Fedora-specific; the rest are generic firewalld zones).

    See the effective rules:

    Terminal window
    sudo firewall-cmd --list-all
    public (default, active)
    target: default
    interfaces: ens19 eth0
    services: dhcpv6-client mdns ssh
    ports:
    forward: yes
    masquerade: no

    interfaces lists the NICs using this zone. The default zone applies to interfaces with no explicit assignment.

    Measured on F45: firewall-cmd --state returns running, the default zone is public, get-zones lists FedoraServer and FedoraWorkstation, and list-all shows public allowing dhcpv6-client, mdns and ssh

    firewalld ships 264 service definitions (measured on F45; 259 on F43); look before writing your own:

    Terminal window
    sudo firewall-cmd --get-services | wc -w
    sudo firewall-cmd --info-service=http | head -6

    Allow one:

    Terminal window
    sudo firewall-cmd --add-service=http # runtime only
    sudo firewall-cmd --permanent --add-service=http
    sudo firewall-cmd --reload

    Runtime versus permanent: the layer people get wrong

    Section titled “Runtime versus permanent: the layer people get wrong”
    ActionTakes effect nowSurvives reboot
    --add-service=…yesno
    --permanent --add-service=…noyes
    --permanent … + --reloadyesyes
    --runtime-to-permanentyesyes

    One measured sequence makes it obvious:

    Terminal window
    sudo firewall-cmd --permanent --add-port=8080/tcp
    echo "runtime=[$(sudo firewall-cmd --list-ports)] perm=[$(sudo firewall-cmd --permanent --list-ports)]"
    success
    runtime=[] perm=[8080/tcp]

    A permanent rule without a reload leaves runtime empty — connections fail while --permanent --list-ports happily shows the port. That is a very easy misdiagnosis.

    Terminal window
    sudo firewall-cmd --reload
    echo "runtime=[$(sudo firewall-cmd --list-ports)] perm=[$(sudo firewall-cmd --permanent --list-ports)]"
    success
    runtime=[8080/tcp] perm=[8080/tcp]
    Terminal window
    ls /etc/firewalld/zones/
    FedoraServer.xml FedoraWorkstation.xml public.xml trusted.xml …

    One XML per zone. --permanent edits these files; --reload makes firewalld re-read them.

    Terminal window
    sudo cat /etc/firewalld/zones/FedoraServer.xml | head -10
    <?xml version="1.0" encoding="utf-8"?>
    <zone>
    <short>Public</short>
    <service name="ssh"/>
    <service name="dhcpv6-client"/>
    <service name="cockpit"/>
    </zone>
    Terminal window
    # assign a NIC to a zone
    sudo firewall-cmd --permanent --zone=internal --add-interface=ens19
    sudo firewall-cmd --reload
    # allow only one subnet
    sudo firewall-cmd --permanent --zone=internal --add-source=192.168.2.0/24
    sudo firewall-cmd --reload
    # temporary (gone after reload or reboot)
    sudo firewall-cmd --add-service=https
    sudo firewall-cmd --remove-service=https
    # what ports are open
    sudo firewall-cmd --list-ports
    Terminal window
    sudo firewall-cmd --state # 1) is it running
    sudo firewall-cmd --list-all # 2) what the default zone allows
    sudo firewall-cmd --get-active-zones # 3) which zone each NIC is in
    sudo firewall-cmd --permanent --list-all # 4) permanent config (effective after reload)
    ss -tlnp # 5) is anything actually listening

    Step 5 is the one usually skipped: a blocked port and a service that is not listening fail identically (connection refused or timeout). Confirm the port appears in ss -tlnp before blaming the firewall.