Understanding firewalld "zones"

I’m a longtime desktop Linux user but new to networking and am trying to learn more about firewalld and typical server use cases.

Firewalld has pretty extensive documentation, but I’m having trouble making sense of the “zone” concept. In the very first section of the documentation (link) they illustrate zones in a fairly intuitive way as representing, in essence, “sets” of systems, and each of the firewall rules you set up with firewalld governs traffic traveling from one zone (the ingress zone) to another zone (the egress zone). That all makes sense.

But elsewhere, I see the word “zone” applied to not sets of systems (nodes in the network), but rather to kinds of firewall rules (arcs in the network). For example, later on in the page linked above, it discusses the idea of an “implicit zone policy” where instead of defining ingress and egress zones for a given rule, you just pick a single zone, and now the policy applies to traffic moving from that zone to (and not from?) the host.

I guess I can wrap my head around the implicit zone thing (although if I am understanding it correctly, it really seems to me like a sloppy shorthand for a firewall rule whose egress zone is simply the zone consisting of the host alone), but then here in the documentation we find a list of “pre-defined zones” which don’t seem like zones in the “set of network nodes” sense at all. For example, one of the predefined zones is work:

For use in work areas. You mostly trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.

This sounds like an entire firewall policy to me, complete with its own rules about which connections will be accepted or rejected and assumptions about which computers are “trusted” (which begs the question of what “zone” those computers belong too).

Honestly, I’m quite confused by all of this and wondering if I’m missing something fundamental. Any clarification on the easiest way to think about firewalld’s “zone” concept and how to square it with the various uses of the term through the firewalld docs would be much appreciated.

Thank you!

In the early days … FirewallD was about configuring firewall of workstation (the “INPUT chain”), not of router (the “forward CHAIN”).
The “proper” support for router config (“Policy Objects”) is a recent addition.

So, you have a machine and you want to dictate how others can connect to it. There are many ways to order the logic, but FirewallD chose essentially

IF from A THEN use rulesetA
ELSE IF from B THEN use rulesetB
ELSE use default_ruleset

Where ‘use rulesetA’ could be

IF to ssh THEN accept
IF to http THEN accept
ELSE reject

The ‘from A’ is certain IPs explicitly and/or traffic via interface that does not match any explicit IP list. In fact, nothing should go to the default_ruleset, because every interface is in some zone.

In other words, first find out where a packet comes from, then decide what to do based on where it wants to go.

Now to the routing, the “FORWARD”. We still care where packets come from (which zone). Where they should go is bit different as it is no longer this machine (it is to member of some zone). There are therefore also two different cases: intra and inter.

The intra: we route traffic between two subnets that all are “part of the same”, members of same zone. Traffic is “within zone”. Most likely we just let traffic through.

The inter: traffic between two different zones. The classic example is the home router that is between home LAN and the outside WAN. FirewallD wants a policy object that lists what can go from LAN to WAN, and separate policy object for WAN to LAN traffic (if any). These are for new connections – established connections can always continue. Example of WAN to LAN would be a port forward to “server” in LAN.

Did any of that help?

Thank you! This makes a lot of sense, and I really appreciate the explanation.

My understanding trails off a bit, though, in the part where you discuss intra and inter. I get that, in general, a firewall will be more lenient with traffic that is being routed within/intra the network and more strict with traffic that’s trying to cross out or in, but this seems like a clean separation between two “sides” of the router and leaves me confused as to why firewalld has so many “predefined zones” if all it’s going to do is behave in the way you describe.

Like, going back to the work zone whose definition I quoted in the original post—is the idea that this zone is meant for the case when firewalld is being used on the network router (your FORWARD case)? How can I find out specifically what kinds of traffic this zone will and will not allow (is it documented)?

And would you agree that the sense of the term “zone” used in the first section of the firewalld documentation is different from that used in the “predefined zones” section—or is there a deeper conceptual connection that I’m not grasping?

First, I’m not fond of FirewallD. Red Hat writes in Chapter 1. Using and configuring firewalld Red Hat Enterprise Linux 9 | Red Hat Customer Portal

1.1. When to use firewalld, nftables, or iptables

The following is a brief overview in which scenario you should use one of the following utilities:

  • firewalld: Use the firewalld utility for simple firewall use cases. The utility is easy to use and covers the typical use cases for these scenarios.
  • nftables: Use the nftables utility to set up complex and performance-critical firewalls, such as for a whole network.

I read that “simple” as not router. Something like desktop, laptop, … up to server that is on multiple networks.

FirewallD has predefined zones – list of rules, not list of members – for those “typical use cases”:

# firewall-cmd --get-zones
block dmz drop external home internal nm-shared public trusted work

Of those I’ve used block external public trusted as starting points.
FirewallD can show definition of zone:

# firewall-cmd --info-zone=work
  target: default
  icmp-block-inversion: no
  services: cockpit dhcpv6-client ssh
  forward: yes
  masquerade: no
  rich rules: 

That is info constructed from:

# cat /usr/lib/firewalld/zones/work.xml
<?xml version="1.0" encoding="utf-8"?>
  <description>For use in work areas. You mostly trust the other computers on networks to not harm your computer. Only selected incoming connections are accepted.</description>
  <service name="ssh"/>
  <service name="dhcpv6-client"/>
  <service name="cockpit"/>

… or from /etc/firewalld/zones/work.xml, if you have modified the zone.
I see that the ‘work’ is practically identical to ‘public’, which is the default.

The above allows three services: cockpit dhcpv6-client ssh. One can study them too:

# firewall-cmd --info-service=dhcpv6-client
  ports: 546/udp
  destination: ipv6:fe80::/64

What is actually in kernel? You can see that with

# nft list ruleset

If the ‘work’ above would be in use, then there would be:

	chain filter_IN_public_allow {
		tcp dport 22 ct state { new, untracked } accept
		ip6 daddr fe80::/64 udp dport 546 ct state { new, untracked } accept
		tcp dport 9090 ct state { new, untracked } accept

The target: default essentially says any new connection that does not match the three rules above (for incoming traffic) will meet:

reject with icmpx admin-prohibited

On one hand a “zone” is a set of hosts that do send us packets. (By default the set is everybody, because only one zone, ‘public’, is used.) One could have ‘home’ and ‘external’. (Btw, the ‘external’ enables masquerade, i.e. sNAT., on its interfaces.)

On the other hand “zone” is the set of rules that are used for that set of hosts.

Thank you, really appreciate the clarification.

To get a sense for what’s possible with firewalld: Suppose I am using AlmaLinux on a workstation for misc software development, and I have noticed that some tools for previewing webapps serve to localhost (e.g. the Flask debugger) while others serve to the LAN (e.g. the python -m http.server builtin). Is there a way I can set the firewall to prevent anyone (other than localhost) from interacting with the python -m http.server?

And what if I am running AlmaLinux on a router in front of a LAN–can I use it to prevent people within the network from randomly opening ports and serving websites to each other?

The zones are outsiders; the localhost is always allowed to connect.
The default zone is public and that allows connection to only three ports (shown in previous comment: ssh, cockpit, and dhcpv6 – 22/tcp, 9090/tcp, and 546/udp with IPv6).
The default is to prevent; the allows are explicit.

If the Python http does not listen to on any of those three and you don’t add the port that it does listen, then the anyone is already prevented.

Router filters traffic that goes through it – from one network to another.

LAN is usually physically machines (including the router) connected to network switch. Traffic between to machines on LAN is through the switch.
Traffic from machine on LAn to, say forums.almalinux.org goes through switch to the router, and through router onwards.

Router has thus no say, since two members of network talk directly with each other.