This is a note about enabling NAT (SNAT, more precisely) and IP masquerading on a Linux host that runs Rocky Linux 9. The host has two network interfaces: eth0 and wg0. Interface eth0 connects to the outside network and is assigned an public IP address while interface wg0 is on a private network. The objective is to make the Linux host as router for the private network so that the traffic originated from the private network can go to the outside network. The steps to achieve this objective using firewalld are as follows:
- Enable IPv4 forwarding
echo "net.ipv4.ip_forward = 1" | sudo tee -a /etc/sysctl.conf sudo sysctl -p
- Assign interface eth0 to the external zone
firewall-cmd --permanent --zone=external --change-interface=eth0
- Assign interface wg0 to the internal zone
firewall-cmd --permanent --zone=internal --change-interface=wg0
-
Set the zone target of the internal zone to ACCEPT
firewall-cmd --permanent --zone=internal --set-target=ACCEPT
-
Finally, reload firewalld's configuration.
firewall-cmd --reload
There is no need to meddle with anything else, such as adding nftables rules and set masquerading for the outward facing network interface. This is because the external zone is by default with masqerading enabled. This can be verified by
firewall-cmd --zone=external --query-masquerade
or by looking at the zone definition file at /usr/lib/firewalld/zones/external.xml.
In addition, the external zone's is also enabled to forward packets. We can examine this by looking at the zone definition file at /usr/lib/firewalld/zones/external.xml or by
firewall-cmd --zone=external --query-forward
The issue seems to lie at the zones' targets. First, let's view the zones' configuaration::
firewall-cmd --zone=external --list-all
Of course, we can also just check the target:
firewall-cmd --permanent --zone=external --get-target
firewall-cmd --zone=internal --list-all
Of course, we can also just check the target:
firewall-cmd --permanent --zone=internal --get-target
The targets of the both external and internal zones are both originally default. The internal zone's default target is in fact interpreted as reject, thus, preventing from packet forwarding to the outside network. This is explained as
For a forwarded packet that ingresses zoneA and egresses zoneB:
- if zoneA's target is ACCEPT, DROP, or REJECT then the packet is accepted, dropped, or rejected respectively.
- if zoneA's target is default, then the packet is accepted, dropped, or rejected based on zoneB's target. If zoneB's target is also default, then the packet will be rejected by firewalld's catchall reject.
Since both ingress (internal) and egress (external) are both "default", the result is that the internal zone's target becomes REJECT".
One question, I have in mind is, why do I not assign the internal facing interface to the trusted zone? That might be for another day.
Reference
This note benefited tremendously from the following resources:
- https://askubuntu.com/questions/1463093/what-is-target-default-of-a-zones-configuration-in-firewalld
- https://github.com/firewalld/firewalld/issues/590#issuecomment-605200548
- man firewall-cmd
- man firewalld.zone
- man firewalld
No comments:
Post a Comment