I recently built a simple gateway/router using firewalld and NAT on CentOS 7 and thought I’d share the firewall-cmd commands used. The machine in question was used as a gateway/router for two VMWare virtual networks being used by students to build Windows domains. As their domain controllers were running DHCP servers, their networks had to be isolated from the TAFE (college) network and each other to avoid problems, but the students also needed access to the TAFE network for DNS forwarding and internet access purposes.
The gateway/router had three virtual NICs, one in each of the students’ virtual networks and one in TAFE’s network. Creating a new custom firewall zone for each student network was easy enough:
firewall-cmd --permanent --new-zone=student1 firewall-cmd --permanent --new-zone=student2 firewall-cmd --reload
Note that only one new zone can be added at a time, and that the –permanent option must be used. The presence of the new zones in firewalld’s permanent configuration can be verified by typing:
firewall-cmd --permanent --get-zones
Zones can be removed if necessary by typing:
firewall-cmd --permanent --delete-zone=student1
By default, firewalld assigns all interfaces to the default zone, usually the “public” zone. So, in our case the following command produced the following output (interface names have been simplified):
firewall-cmd --list-all public (default, active) interfaces: eth1 eth2 eth3 sources: services: dhcpv6-client ssh ports: masquerade: no forward-ports: icmp-blocks: rich rules:
Here, firewalld knows about three interfaces, eth1 eth2 and eth3, and all are assigned to the “public” zone. We want to assign eth1 to the “student1” zone and eth2 to the “student2” zone, and leave eth3 in the “public” zone which we will use for TAFE’s network.
Initially, we tried using some firewall-cmd options for adding interfaces to zones (and removing them) as follows:
firewall-cmd --permanent --zone=public --remove-interface=eth1 firewall-cmd --permanent --zone=student1 --add-interface=eth1
The problem with this approach was that every time firewalld or the server were restarted, all interfaces were reassigned back to the “public” zone despite the use of the –permanent option in the above commands. It turns out that the only way to permanently assign an interface to a zone is to edit the interface’s configuration (ifcfg) file in the /etc/sysconfig/network-scripts/ directory and add a ZONE option as follows:
If this option is missing or empty, the default firewalld zone will always be reassigned to the interface when it is restarted. So, with the ZONE option added to both ifcfg-eth1 and ifcfg-eth2 and networking restarted, we moved on to configuring routing using NAT (network address translation).
The following rules allow machines on the isolated internal networks (eth1 and eth2) to send NATed packets to TAFE’s network (eth3), and also allow responses back. Machines on TAFE’s network cannot initiate communications with student machines on the internal networks:
firewall-cmd --direct --add-rule ipv4 nat POSTROUTING 0 -o eth_ext -j MASQUERADE firewall-cmd --direct --add-rule ipv4 filter FORWARD 0 -i eth1 -o eth3 -j ACCEPT firewall-cmd --direct --add-rule ipv4 filter FORWARD 0 -i eth3 -o eth1 -m state --state RELATED,ESTABLISHED -j ACCEPT firewall-cmd --direct --add-rule ipv4 filter FORWARD 0 -i eth2 -o eth3 -j ACCEPT firewall-cmd --direct --add-rule ipv4 filter FORWARD 0 -i eth3 -o eth2 -m state --state RELATED,ESTABLISHED -j ACCEPT
Pretty simple really. Note that this gateway/router forwards all traffic regardless of service, port or protocol, and functions more as a dumb router than a firewall. It could be locked down to only allow specific services and ports, but works fine for our purposes.