The packet filtering in Linux is happening in the kernel and software framework called netfilter is responsible for that [1]. netfilter provides user-space utility program iptables for managing packet filtering and this had been the utility of choice for managing firewall until firewalld was introduced in RHEL 7. Now, you still can use iptables for managing firewall in RHEL 7, but for that you would need to disable firewalld service and enable iptables service. The latter is available from iptables-services package.
To get better understanding of how firewalld works (and because firewalld is based on iptables) let's first see how iptables works:
To list rules:
iptables -L -v
Describe a foundation of the rules, on top of which all other rules will be based:
iptables -P INPUT DROP
iptables -P OUTPUT DROP
Then, introducing the first rule:
iptables -A INPUT -p tcp --dport 80 -j ACCEPT
For output as well:
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
iptables-save > /etc/sysconfig/iptables
To clear policies:
iptables -F INPUT
iptables -F OUTPUT
Advantages of firewalld: In order to get the same effect as we have just got with iptables, firewalld's equivalent would be:
firewall-cmd --list-all - to list all rules, zones, services, ports etc.
And:
firewall-cmd --add-service=http --permanent
firewall-cmd --reload
And that is it.
firewall-cmd operates on the following objects: zone, port, service, forward-port, protocol. To operate on them you should keep in mind the following verbs: list, add, delete, query and sometimes get. While all except the latter apply to the current configuration, get is used to get "all available" objects. Now, what I mean by that will be shown in a moment. First, let's construct a command to list all currently open ports:
firewall-cmd --list-port
Now, because I executed this on my server, the output would be empty. Instead, for managing firewall there I use services. Let's see the list of active services firewall-cmd --list-service:
# firewall-cmd --list-service
ssh dhcpv6-client dns
Meaning, that these services are allowed to accept incoming connections.
A service is described with an XML-file, containing the information about what port needs to be open, what name and description the service should have. Standard services are located in:
/usr/lib/firewalld/services/
For example:
https.xml
http.xml
ssh.xml
tftp-client.xml
tftp.xml
etc. The content of https.xml:
<?xml version="1.0" encoding="utf-8"?>
<service>
<short>Secure WWW (HTTPS)</short>
<description>...</description>
<port protocol="tcp" port="443"/>
</service>
Custom services need to be placed in /etc/firewalld/services. And then:
firewall-cmd --reload
After that they appear in the list of available services:
firewall-cmd --get-service
(To list current settings use --list-service, --list-port).
Rich rules allow for fine configuration of firewall when needed.
For example, if we wanted to allow only a specific host to access specific port via TCP protocol then we would execute:
firewall-cmd --add-rich-rule='rule family="ipv4" source address="192.168.121.144" port protocol="tcp" port="53" accept' --permanent
firewall-cmd --reload
Important: It's important not to have intersecting rules in both rich rules and other objects e.g. service or forward-port. Having a service configured both as a service and as a rich rule would result in only service's configuration being applied while the whole rich rule being rejected.
First of all, for port forwarding to work, masquerade needs to be turned on, otherwise forwarded package will not return:
firewall-cmd --add-masquerade
Now, let's see how you forward ports. In my situation I have a virtual KDC running as a part of FreeIPA installation in a KVM virtual machine on a host with IP address 192.168.121.101, but I want different services from my other servers authenticate against said KDC, so what I would do is forward ports from hypervisor machine to the virtual KDC:
firewall-cmd --add-forward-port=port=10088:proto=tcp:toport=88:toaddr=192.168.121.101 --permanent
firewall-cmd --add-forward-port=port=10464:proto=tcp:toport=464:toaddr=192.168.121.101 --permanent
firewall-cmd --reload
To forward ports to the same machine the only thing that needs to be changed is toaddr=... part and it needs to be left out.
TODO: this article only covers iptables and
firewalld/firewall-cmd, which is the RHEL/CentOS side of the
world. Ubuntu defaults to its own tool, ufw ("Uncomplicated Firewall"),
instead of firewalld — both are just different frontends over the same
kernel netfilter layer, there's no technical reason Ubuntu couldn't use
firewalld too, it's ecosystem divergence: firewalld came out
of Red Hat/Fedora, ufw was built by Canonical specifically for Ubuntu with
a simpler mental model (default-deny-incoming, no zones). Worth adding a ufw
section here at some point.