Policy Set: Gateway

Introduction to Policy Sets

Firewalld has supported policies for many years. Policies are super flexible and can describe all kinds of traffic patterns. That same flexibility can make it daunting for new users to get started. The solution is Policy Sets.

A Policy Set is a predefined collection of policies that solve a particular use case, e.g. a home gateway. This gives users a starting configuration that may be fine tuned for a specific environment. They also serve as documentation by example.

Policy Sets are administratively disabled by default. For them to activate you must remove the disable.

A whole set can be activated with one command:

# firewall-cmd --policy-set gateway --remove-disable

Policy Set: Gateway

The Gateway Policy Set may be used for a gateway, including a home router.

Getting Started

Creating a home router with the Gateway Policy Set is only a handful of commands. Two commands to add your LAN interface and uplink interface (Internet). One command to activate the Policy Set. One final command to reload the firewall.

# firewall-cmd --permanent --zone internal --add-interface eth0
# firewall-cmd --permanent --zone external --add-interface eth1
# firewall-cmd --permanent --policy-set gateway --remove-disable
# firewall-cmd --reload

That’s it. Congratulations! You now have an easy to use line rate home router!

Adding a Forward Port

One common thing for a home router is to expose some services, e.g. ssh for remote access. The Gateway Policy Set already has a policy to allow this. One just has to add the forward port.

This example forwards port 2222 from the Internet to the internal host 10.0.0.22:22.

# firewall-cmd --permanent --policy gateway-world-to-HOST \
               --add-forward-port port=2222:proto=tcp:toport=22:toaddr=10.0.0.22
# firewall-cmd --reload

Documentation

Every Policy Set has a dedicated man page that describes the set and its intended use case. You can discover all sets in the primary man page for Policy Sets. See man firewalld.policy-sets. For example, the gateway set man page is man firewalld.policy-set-gateway.

Availability

Policy sets are available in firewalld v2.3.0 and later.


Strict Forward Ports

Introduction

When Docker (or Podman) publishes container ports, the published ports are honored by firewalld. In some cases, users want firewalld to be strict and block those ports. This has been outlined in a previous post.

Firewalld now supports StrictForwardPorts which allows users to block published container ports.

Configuration

The behavior is configurable by changing StrictForwardPorts in /etc/firewalld/firewalld.conf.

/etc/firewalld/firewalld.conf:

# StrictForwardPorts
# If set to yes, the generated destination NAT (DNAT) rules will NOT accept
# traffic that was DNAT'd by other entities, e.g. docker. Firewalld will be
# strict and not allow published container ports until they're explicitly
# allowed via firewalld.
# If set to no, then docker (and podman) integrates seamlessly with firewalld.
# Published container ports are implicitly allowed.
# Defaults to "no".
StrictForwardPorts=no

Forwarding Ports with StrictForwardPorts=yes

When StrictForwardPorts=yes, the user must explicitly forward ports to containers using firewalld. All --published ports will be blocked.

Since the containers IP address will change every time the container is started, a forward port should be added at runtime. Luckily docker provides a way to get the container’s address programmatically.

Example to forward 8080 to a container’s port 80.

# CONTAINER_IP=$(docker inspect -f '{{range.NetworkSettings.Networks}}{{.IPAddress}}{{end}}' <container_name>)
# firewall-cmd --zone public --add-forward-port=port=8080:proto=tcp:toport=80:toaddr=${CONTAINER_IP}

What it Looks Like

This feature works by altering the underlying firewall rules that accept DNAT’d connections.

With StrictForwardPorts=no, there is a top-level and generic accept of all DNAT’d connections. This is the rule that would allow --published ports.

table inet firewalld {
    chain filter_FORWARD {
        ct state established,related accept
        ct status dnat accept   <--- THIS RULE
        [..]
    }
}

With StrictForwardPorts=yes, the generic is replaced by a rule per forward-port that accepts only those ports for which firewalld has explicit configuration. It block all other DNAT’d traffic.

table inet firewalld {
    chain filter_FORWARD {
	ct state established,related accept
	ct status dnat jump filter_FORWARD_dnat   <--- CHANGED RULE
	[..]
    }
}

table inet firewalld {
    chain filter_FORWARD_dnat {
	ct original proto-dst 8080 accept    <--- ONE PER forward-port
	reject with icmpx admin-prohibited
    }
}

Conclusion

This new configuration knob gives users more control over container traffic. The default behavior has not changed. That is, firewalld and docker (podman) integration is still seamless by default. Users that want strict control now have a configuration option to get the behavior they desire.

Bugs Referencing This Topic

This topic has been discussed and referenced in numerous reports.


Strict Filtering of Docker Containers

Introduction

Docker supports publishing ports for a container. This allows external access to the container. When firewalld is running these published ports are honored and a hole is opened in firewalld. For most users, e.g. workstations, this a good thing as docker works transparently.

For some users, this is not expected. They want firewalld to be strict. They want to only allow traffic explicitly configured via firewalld. Fortunately this can be achieved with some configuration.

Docker Configuration

To have full control of docker containers via firewalld one must first disable iptables in docker.

This can be done by adding iptables: false to the daemon configuration.

# cat /etc/docker/daemon.json
{
    "iptables": false
}

Then the host must be rebooted. Restarting Docker is not enough to clean up all the pre-existing iptables rules.

# reboot

Now the containers won’t have any iptables firewall rules automatically created.

Verify Docker Does Not Install iptables rules

This is optional and just for illustration purposes.

After the reboot starting a docker container will cause containers to not have internet access. This means that docker is not setting up iptables rules.

# docker run -it --rm debian:stable

# apt update
Ign:1 http://deb.debian.org/debian stable InRelease
Ign:2 http://deb.debian.org/debian stable-updates InRelease
Ign:3 http://deb.debian.org/debian-security stable-security InRelease
0% [Connecting to deb.debian.org]

In the next step this will be fixed by doing the networking natively in firewalld. Restarting the container is not necessary.

Firewalld Configuration

Now setup firewalld to natively perform all networking for docker with the following configuration.

firewall-cmd --permanent --zone docker --add-source 172.17.0.1/16

firewall-cmd --permanent --new-policy dockerToWorld
firewall-cmd --permanent --policy dockerToWorld --add-ingress-zone docker
firewall-cmd --permanent --policy dockerToWorld --add-egress-zone ANY
firewall-cmd --permanent --policy dockerToWorld --set-target ACCEPT
firewall-cmd --permanent --policy dockerToWorld --add-masquerade

firewall-cmd --reload

This creates a policy, dockerToWorld, to give the container internet access. Note that the --add-source above assumes the default address range used by docker.

Verify Firewalld Rules

After the firewalld rules are created verify the container has internet access.

# apt update
Get:1 http://deb.debian.org/debian stable InRelease [151 kB]
Get:2 http://deb.debian.org/debian stable-updates InRelease [55.4 kB]
Get:3 http://deb.debian.org/debian-security stable-security InRelease [48.0 kB]
Get:4 http://deb.debian.org/debian stable/main amd64 Packages [8786 kB]
Get:5 http://deb.debian.org/debian stable-updates/main amd64 Packages [12.7 kB]
Get:6 http://deb.debian.org/debian-security stable-security/main amd64 Packages [150 kB]
Fetched 9203 kB in 1s (7189 kB/s)
Reading package lists... Done
Building dependency tree... Done
Reading state information... Done
8 packages can be upgraded. Run 'apt list --upgradable' to see them.

Adding Firewalld Native Forward Ports

Since docker is no longer using iptables ports published with --publish will no longer work. It’s simply ignored. Ports must be exposed with firewalld.

The first step is to create another policy, dockerFwdPort, to allow external access to the container.

firewall-cmd --permanent --new-policy dockerFwdPort
firewall-cmd --permanent --policy dockerFwdPort --add-ingress-zone ANY
firewall-cmd --permanent --policy dockerFwdPort --add-egress-zone HOST

Note: Older firewalld versions (before v2.0.z) require using egress-zone=ANY.

To add port forwarding (equivalent of docker –publish) to a specific container use --add-forward-port in the dockerFwdPort policy. This example forwards port 8080 to to 80. Note that the containers IP address must be known.

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

Lastly reload the firewall.

firewall-cmd --reload

Results

This small amount of configuration allows firewalld to strictly filter docker container network traffic by doing all the networking natively in firewalld.

A Note About Podman

A similar configuration can be done with Podman. Podman 4.x can use environment variable NETAVARK_FW=none to disable the network plugin. Podman 5.x will have a containers.conf for it.

The firewalld configuration is the same except that the podman zone is used instead of the docker zone.

Bugs Referencing This Topic

This topic has been discussed and referenced in numerous reports.


firewalld 2.1.0 release

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

This is a feature release. It also includes all bug fixes since v2.0.0.

git shortlog --no-merges --grep "^feat" v2.0.0..v2.1.0

Thomas Haller (3):

  • feat(service): add DNS over QUIC (DoQ) Service
  • feat(icmp): add ICMPv6 Multicast Listener Discovery (MLD) types
  • feat(fw): add ReloadPolicy option in firewalld.conf

Vinícius Ferrão (1):

  • feat(service): add submission service (tcp 587)

Vixea (1):

  • feat(service): Add alvr

nser77 (1):

  • feat(service): add vrrp

Source available here:


firewalld 2.0.0 release

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

This is a major release. The major version is being bumped symbolically to reflect significant changes done in commit f4d2b80adc84 ("fix(policy): disallow zone drifting"). It does not contain any deliberate breaking changes.

Feature Blogs:

Additionally the release contains the below new features.

Eric Garver (4):

  • feat(direct): avoid iptables flush if using nftables backend
  • feat(zone): add support for priority
  • feat(nftables): add support for flowtable (software fastpath)
  • feat(nftables): support counters

Juris Lambda (2):

  • feat(service): add Zabbix Java Gateway
  • feat(service): add Zabbix Web Service

Nikolas Koesling (14):

  • feat(service): add game 0AD
  • feat(service): add game anno 1602
  • feat(service): add game anno 1800
  • feat(service): add game Civilization IV
  • feat(service): add game Civilization V
  • feat(service): add game factorio
  • feat(service): add game Minecraft
  • feat(service): add game Need For Speed: Most Wanted
  • feat(service): add game Stellaris
  • feat(service): add game Stronghold Crusader
  • feat(service): add game Super Tux kart
  • feat(service): add game Terraria
  • feat(service): add game Zero K
  • feat(service): add game Settlers

Pat Riehecky (3):

  • feat(service): add OpenTelemetry (OTLP) service
  • feat(service): define statsrv from RFC 996
  • feat(service): Add syscomlan

Source available here: