firewalld 0.8.5 release

Warning: This release introduced a regression that caused a significant increase in memory usage. However, functionality appears okay. If memory usage is a concern then this release should be avoided.

A new release of firewalld, version 0.8.5, is available.

This is a bug fix only release. This is the last release for the stable-0.8 branch.

Donald Yandt (1):

  • docs(dbus): fix invalid method names

Vrinda Punj (1):

  • fix(rich): non-printable characters removed from rich rules

diegoe (1):

  • docs(firewall-cmd): small description grammar fix

Source available here:

TCP MSS Clamping in Firewalld

What is TCP MSS Clamping?

The maximum segment size is defined as the largest amount of data that can be received in a single TCP segment. TCP MSS clamping is a feature that sets the maximum segment size used by a TCP session. The way that it achieves this is during the TCP 3 way handshake, a server can set the MSS in the outgoing TCP SYN packets signalling the maximum segment size of the data packets that it can receive.

Why is it needed?

The way that TCP MSS clamping was used in Firewalld before the addition of this feature was by adding an iptables rule via direct rules. An example of enabling the TCP MSS clamp feature through direct iptables rules would be the following command:

# firewall-cmd --permanent --direct --add-passthrough ipv4 -t mangle -I FORWARD -p tcp --syn -j TCPMSS --clamp-mss-to-pmtu

In the example above, TCP MSS clamping is directly used by writing iptables rules. However, since Firewalld is supposed to be an abstraction of iptables and nftables, it is more clean to have it enabled as an option within Firewalld instead of having the user enable it by writing direct rules.

What does this feature do?

This feature adds TCP MSS clamping as an option in Firewalld rich rules, which gets translated into the corresponding rules for whichever backend is enabled in firewalld.conf.

What does this feature look like?

This feature adds an enable TCP MSS clamp option to Firewalld rich rules. The user has an option called tcp-mss-clamp in rich rules. The tcp-mss-clamp option takes in an optional operand called value which allows the user to set the maximum segment size. The maximum segment size can be set to pmtu (path maximum transmission unit) or a value greater than or equal to to 536. If the user sets value to pmtu, it sets the maximum segment size to the smallest MTU (maximum transmission unit) of all the nodes between the source and the destination. This is a useful default because the user doesn’t have to manually set the MSS to the smallest MTU in the network path. By setting MSS to pmtu, all packets will be small enough to be able to traverse the network path without being dropped or fragmented.

Examples of writing adding this feature with the operand value would be:

# firewall-cmd --add-rich-rule='rule tcp-mss-clamp value=pmtu'
# firewall-cmd --add-rich-rule='rule tcp-mss-clamp value=536'

If value is not provided then the maximum segment size is set to pmtu. An example of a command where value is not provided is the following:

# firewall-cmd --add-rich-rule=’rule tcp-mss-clamp’

The rich rule gets translated into either nftables or iptables rules depending on which backend is enabled. For instance, if the user enables the TCP MSS clamp option and sets the maximum segment size as pmtu, and the nftables backend is enabled, the following command would allow the user to see the corresponding rule that sets the maximum segment size to pmtu added to nftables:

#  nft list chain inet firewalld filter_FWDO_public_allow
table inet firewalld {
        chain filter_FWDO_public_allow {
                tcp flags syn tcp option maxseg size set rt mtu

As seen above, the rich rule that enabled TCP MSS clamping got translated to the appropriate nftables rule.

When is this available?

This will be available on the next feature release of Firewalld.

firewalld 0.8.4 release

A new release of firewalld, version 0.8.4, is available.

This is a bug fix only release.

Eric Garver (25):

  • fix(rich): nftables: log level “warning”
  • fix(rich): icmptypes with one family
  • fix(LastUpdatedOrderedDict): __getitem__(): fetch from list if int
  • fix(rich): use correct error code for invalid priority
  • fix(icmptype): when applying rules get ict from perm config
  • fix(rich): clamp the IP families to those actually enabled
  • fix(rich icmptype): verify rule and icmptype families don’t conflict
  • fix(nftables): packet marks with masks
  • fix(nftables): icmp types with code == 0
  • fix(ipXtables): rich: avoid duplicate rules for icmp-type w/ mark action
  • fix(policy): cache rule_str for rich rules
  • fix(icmptype): nftables: runtimeToPermanent if ip6tables not available
  • docs(firewall-cmd): clarify lockdown whitelist command paths

Paul Wouters (1):

  • improvement(service): IPsec: Update description and add TCP port 4500

Vladislav Grigoryev (1):

  • fix(cli): unify indentation for forward-ports and rich rules

Source available here:

firewalld 0.9.1 release

A new release of firewalld, version 0.9.1, is available.

This is a bug fix only release.

Eric Garver (3):

  • docs(firewall-cmd): clarify lockdown whitelist command paths
  • fix(dbus): getActivePolicies shouldn’t return a policy if a zone is not active
  • fix(policy): zone interface/source changes should affect all using zone

Source available here:

Policy Objects: Filtering Container and Virtual Machine Traffic


A handful of container and virtual machine runtimes have some level of integration with firewalld. The reality is the integration is minimal partly due to limitations in older firewalld versions. With firewalld’s new Policy Objects feature we can improve the situation and allow users to filter their container and virtual machine traffic.

Podman, for example, adds the container’s block of address to the trusted zone. This effectively means firewalld does no filtering on the container traffic. All the traffic is immediately accepted. Podman will also install its own iptables rules to do other things: forward ports, masquerading. With policy objects this duality of iptables and firewalld is unnecessary as it can all be done in firewalld.

This post is both an illustration of what container and virtual machine runtimes can do to better integrate with firewalld and an example of what is possible with policy objects.

Podman as an Example

In this post we’ll use Podman as our example. First it’s useful to know how Podman currently interacts with firewalld and iptables.

Podman adds each container to firewalld’s trusted zone. This is what allows the container traffic to pass through firewalld.

# firewall-cmd --zone trusted --list-all
trusted (active)
  target: ACCEPT
  icmp-block-inversion: no
  masquerade: no
  rich rules: 

Podman also uses iptables to setup masquerading for the container. This is the part that could not be done via firewalld before policy objects.

# iptables -t nat -L

target     prot opt source               destination         
CNI-a4307a257bab71a40e0f6b64  all  --           anywhere             /* name: "podman" id: "7b9e4aef72c4cf188df65589f43bf4256488959700557163f57e96920d18ec2a" */


Chain CNI-a4307a257bab71a40e0f6b64 (1 references)
target     prot opt source               destination         
ACCEPT     all  --  anywhere            /* name: "podman" id: "7b9e4aef72c4cf188df65589f43bf4256488959700557163f57e96920d18ec2a" */
MASQUERADE  all  --  anywhere            !          /* name: "podman" id: "7b9e4aef72c4cf188df65589f43bf4256488959700557163f57e96920d18ec2a" */

What’s Missing?

What’s missing from all the above is filtering. Podman has no method of filtering incoming or outgoing traffic on behalf of the container. Later in this post we’ll see that’s now possible in firewalld.

Creating a Podman Zone

To make things easier to reason about we’ll create a new zone called podman. This will make it clear that the zone is intended for containers.

# firewall-cmd --permanent --new-zone podman

Filtering to the World

Above we saw that Podman is creating iptables rules to perform masquerading. Let’s see how this could be done natively in firewalld instead.

First we need a new policy for our outbound container traffic.

# firewall-cmd --permanent --new-policy podmanToWorld
# firewall-cmd --permanent --policy podmanToWorld --add-ingress-zone podman
# firewall-cmd --permanent --policy podmanToWorld --add-egress-zone ANY

Note: ANY means any egress zone, but it does not include traffic destined to the host running firewalld.

At this point you can add anything you want to the new policy and it will filter traffic originating from the container and destined to any other host.

Let’s use this new policy to enable masquerading for the containers.

# firewall-cmd --permanent --policy podmanToWorld --add-masquerade

But we can also filter anything we want. Let’s say I don’t want my containers connecting to remote ftp servers.

# firewall-cmd --permanent --policy podmanToWorld --add-rich-rule='rule service name="ftp" reject'

As another example, maybe I don’t want my containers communicating with the network. We can reject that traffic as well.

# firewall-cmd --permanent --policy podmanToWorld --add-rich-rule='rule family=ipv4 destination address= reject'

Forwarding Ports to a Container

We can also use a policy to forward ports from the host to the container.

# firewall-cmd --permanent --new-policy fwdPortsPodman
# firewall-cmd --permanent --policy fwdPortsPodman --add-ingress-zone ANY
# firewall-cmd --permanent --policy fwdPortsPodman --add-egress-zone ANY

Now add the forward ports.

# firewall-cmd --permanent --policy fwdPortsPodman --add-forward-port=port=8080:proto=tcp:toport=80:toaddr=

Note: In the past it was possible to forward ports to a container, but it required adding the forward-port to the ingress zone, e.g. public. This has the major disadvantage that you must know the expected ingress zone. That’s not necessary with policies.

Filtering to the Host

You can also use a policy to filter traffic from the containers to the host.

# firewall-cmd --permanent --new-policy podmanToHost
# firewall-cmd --permanent --policy podmanToHost --add-ingress-zone podman
# firewall-cmd --permanent --policy podmanToHost --add-egress-zone HOST

A good setting would be to block all traffic to the host by default then selectively open the specific things you need.

# firewall-cmd --permanent --policy podmanToHost --set-target REJECT
# firewall-cmd --permanent --policy podmanToHost --add-service dhcp
# firewall-cmd --permanent --policy podmanToHost --add-service dns

Note: Alternatively you can get the same effect by adding the services to the podman zone and setting --set-target=reject. This is because most, but not all (e.g. masquerade), features added to a zone filter on the INPUT chain.

Move the Container to the Podman Zone

The last step is to reload firewalld and move the container to the new podman zone. This will cause all the policies we created to become active.

# firewall-cmd --reload
# firewall-cmd --zone podman --change-source

Note that this is a runtime configuration change. That’s because podman adds the container’s IP address to the runtime only.

Removing Podman’s iptables Rules

Now that firewalld is handling the masquerading we can flush the iptables rules created by Podman. After this there will be no Podman related rules in iptables. Everything is being handled by firewalld.

# iptables -t nat -F

Disclaimer: This command is a flush of all NAT rules. If there are other rules not installed by Podman they’ll also be deleted.


Hopefully this post illustrated the flexibility of policy objects and how you might use them with Podman. The same approach can be taken for docker and libvirt as well. In the future it would be great to see these projects expand their integration with firewalld and avoid the direct usage of iptables.