firewalld in CentOS is built on iptables and some other programs. firewalld uses some more friendly configuration methods to implement iptables operations. It also extends some features that are not supported by iptables itself, such as timed firewall rules. The full program and library dependencies are available on the firewalld website at https://firewalld.org/.

iptables processes network packets by manipulating the Linux kernel netfilter module.

Please temporarily disable the firewalld firewall on your machine before operating the command, and make sure you have access to the physical machine (or a physical terminal in a virtual machine), as some rules will block access to SSH port 22 and prevent subsequent operations.

1
2
# stop firewalld
systemctl stop firewalld

iptables basic concepts and operations

Policy Chain

The iptables filtering rules are used to determine the processing of packets in different situations by matching the rules in a policy chain, which contains three policy chains.

  • INPUT : This chain is used to control incoming connections and packets, such as allowing SSH incoming connections from certain IPs.
  • FORWARD : Forwarding control link, this chain is used if you need to configure routing features on the machine.
  • OUTPUT : Outgoing chain, this chain is matched when the current host sends a request to the outside, and usually no special rules are configured for it when there is no special need.

You can view the currently set iptables rules with the following command.

1
2
3
4
~]# iptables -S
-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

Policy Chain Default Behavior

The corresponding policy chain will have a default behavior. It means that the chain is defined to handle packets when it does not match any rule.

Use the following command to view the behavior of the current policy chain (the number of packets and traffic not matching this chain are also output).

1
2
3
4
~]# iptables -L -v | grep policy
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
Chain OUTPUT (policy ACCEPT 821 packets, 293K bytes)

If the current policy chain does not behave as shown above, the following command can be used to set the behavior of accepting all data.

1
2
3
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT

The following command can be tried to discard packets in all cases.

1
2
3
iptables -P INPUT DROP
iptables -P OUTPUT DROP
iptables -P FORWARD DROP

Note: At this point, the external host will not be able to access the host, and the internal host will not be able to access the external.

Response of the chain

With the default policy chain in place, custom rules can be added. When matching a rule that matches the requirements (e.g. target port/source of data) how you want the data to be processed.

The following three “responses” are commonly used for chaining

  • ACCEPT : All connections and data are allowed.
  • DROP : Drops the connection. The sender will be unaware, as if the host does not exist.
  • REJECT: Connection is rejected and an error message is returned. The sender will know that the request was blocked by the firewall.

The behavior of the peer machine configured with different INPUT policy chains can be tested with the ping command.

ACCEPT:

1
2
3
4
5
6
7
# iptables -P INPUT ACCEPT
~]# ping 10.1.1.50 -c 1
PING 10.1.1.50 (10.1.1.50) 56(84) bytes of data.
64 bytes from 10.1.1.50: icmp_seq=1 ttl=64 time=0.227 ms
--- 10.1.1.50 ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 0.227/0.227/0.227/0.000 ms

DROP:

1
2
3
4
5
# iptables -P INPUT DROP
~]# ping 10.1.1.50 -c 1
PING 10.1.1.50 (10.1.1.50) 56(84) bytes of data.
--- 10.1.1.50 ping statistics ---
1 packets transmitted, 0 received, 100% packet loss, time 0ms

REJECT:

1
2
3
4
5
6
# iptables -A INPUT -j REJECT
~]# ping 10.1.1.50 -c 1
PING 10.1.1.50 (10.1.1.50) 56(84) bytes of data.
From 10.1.1.50 icmp_seq=1 Destination Port Unreachable
--- 10.1.1.50 ping statistics ---
1 packets transmitted, 0 received, +1 errors, 100% packet loss, time 0ms

Strictly speaking, ACCEPT and DROP are the target of the chain, while REJECT is the target extension, so REJECT cannot be configured for chains, only REJECT rules can be added to chains.

Chain rules

Once the chain is configured, rules can be added to it. For adding a rule, use a command of the following form.

1
iptables -A <chain name> [optional match rule] -j <target>

The targets include custom chains in addition to the ACCEPT/DROP/REJECT mentioned above, as well as other extended targets.

Before attempting to follow up, keep in mind the following command to reset the firewall.

1
2
3
4
5
6
iptables -X # Delete all user definitions
iptables -F # Rules for refreshing the chain
# If the policy chain is modified
iptables -P INPUT ACCEPT
iptables -P OUTPUT ACCEPT
iptables -P FORWARD ACCEPT

drops all ICMP protocols

External pinging of the current host will prompt a timeout. The current machine will not be detected when using the traceroute command for route tracing.

1
iptables -A INPUT -p icmp -j DROP

Allowed connection ports only

Configure the INPUT policy chain to default to DROP first and then configure allow rules for it.

1
2
iptables -P INPUT DROP
iptables -A INPUT -p tcp --dport=22 -j ACCEPT

discards data from a specified source

Data from a fixed IP can be specified to be discarded.

1
iptables -A INPUT -s 10.1.1.50 -j DROP

Or, to discard all data under the source segment.

1
iptables -A INPUT -s 10.1.1.0/24 -j DROP

Custom Chains

Chains can be created, and custom chains can be added to INPUT policy chains after adding rules using the following command.

1
2
3
iptables -N mychain
iptables -A mychain -p ICMP -j DROP
iptables -A INPUT -j mychain

After the addition is complete the configuration is:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
~]# iptables -L
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
mychain    all  --  anywhere             anywhere

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination

Chain mychain (1 references)
target     prot opt source               destination
DROP       icmp --  anywhere             anywhere

You can see that the default behavior of the INPUT policy chain is ACCEPT, with an arbitrary matching rule targeting mychain.

So when matching input data, all data tries to match mychain’s rules.

In mychain, if the matching protocol is ICMP, the data is discarded.

Save and Restore

If you do not execute the command to save the configuration, all changes will be discarded after a system reboot.

Use the iptables-save command to save changes to take effect after a reboot and output the configuration save command.

Use iptables-restore to read in the configuration from standard input and restore it.

1
2
iptables-save > myconfig
iptables-restore < myconfig

firewalld Basic Concepts and Operations

Make sure firewalld is started before operating it.

1
2
3
4
# Confirm Status
systemctl status firewalld
# start
systemctl start firewalld

Network zones (ZONE)

The firewall presets a number of network zones, and the system assigns network interfaces to public zones by default.

The firewall preset configuration files are defined in the /usr/lib/firewalld/zones directory, and the user-defined files are in the /etc/firewalld/zones directory. The detailed policy and format can be found in firewalld.zone(5), or see the linked article at the end of this article.

In the initial state, the firewall-cmd -list-all command allows you to get the status of the default network zone public.

To modify the default network zone, edit the DefaultZone field in the /etc/firewalld/firewalld.conf file.

Use firewall-cmd -list-all-zones to display all network zone configurations.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: ens192
  sources:
  services: dhcpv6-client ssh
  ports:
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

Open ports

You can open the port by direct command, please use reload command to reload the firewall configuration after the configuration is done.

1
2
firewall-cmd --zone=public --add-port=8080/tcp --permanent
firewall-cmd --reload

Port access can be removed using the corresponding remove-port command.

1
2
firewall-cmd --zone=public --remove-port=8080/tcp --permanent
firewall-cmd --reload

Please note: To set permanent settings, add the -permanent option, which will be added to all relevant firewall commands below without exception.

Open ports by configuring the service file.

In the initial configuration, only two services, dhcpv6-client and ssh, are open for external access in the public network area of firewalld. They are exposed on ports 546/udp/ipv6 and 22/tcp.

A set of service configuration files described in XML is defined in the /usr/lib/firewalld/services directory.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
]# cat /usr/lib/firewalld/services/ssh.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>SSH</short>
  <port protocol="tcp" port="22"/>
</service>
]# cat /usr/lib/firewalld/services/dhcpv6-client.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>DHCPv6 Client</short>
  <port protocol="udp" port="546"/>
  <destination ipv6="fe80::/64"/>
</service>

Copy the SSH service as a template to the user configuration directory. and modify port 22 to 8080, the contents of the file are as follows

1
2
3
4
5
6
7
cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/myweb.xml
]# cat /etc/firewalld/services/myweb.xml
<?xml version="1.0" encoding="utf-8"?>
<service>
  <short>MYWEB</short>
  <port protocol="tcp" port="8080"/>
</service>

The next step is to open the port by means of a service.

1
2
3
4
5
6
# add
firewall-cmd --zone public --add-service=myweb --permanent
firewall-cmd --reload
# remove
firewall-cmd --zone public --add-service=myweb --permanent
firewall-cmd --reload

A simple HTTP server can be started in Python to test whether port 8080 is open.

1
2
3
4
5
# Python 2.x
python -m SimpleHTTPServer 8080
# Python 3.x
python -m http.server 8080
curl -v <host>:8080

Forwarding Port

By configuring the forwarding port, you can forward the data accessing the specified port to other specified host ports, here is the example of the current host, forwarding all accesses to port 8080 to 8081.

1
2
firewall-cmd --zone=public --add-forward-port=port=8080:proto=tcp:toport=8081 --permanent
firewall-cmd --reload

The preceding Python command starts an HTTP server with port 8081, at which point external access to the data on port 8080 of the current host will be able to access the content directly.

The corresponding command to remove forwarding is also

1
2
firewall-cmd --zone=public --remove-forward-port=port=8080:proto=tcp:toport=8081 --permanent
firewall-cmd --reload

Rich Language

Use Rich Language to create relatively complex rule configurations, such as restricting tcp port 8080 under ipv4 to be accessed only by the 10.1.1.0/2 network segment.

1
2
3
4
# add
firewall-cmd --zone=public --add-rich-rule='rule family=ipv4 source address=10.1.1.0/24 port port=8080 protocol=tcp accept' --permanent
# remove
firewall-cmd --zone=public --remove-rich-rule='rule family=ipv4 source address=10.1.1.0/24 port port=8080 protocol=tcp accept' --permanent

Rich Languange is an abstract representation of the iptables utility.

The full format description can be found in firewalld.zone(5), but in XML format.

firewalld and iptables

All tables of iptables

Before discussing firewalld in relation to iptables, it is important to briefly understand all the tables in iptables.

iptables supports five tables to provide configuration in various environments.

filter table

The default table with three built-in chains: INPUT (for packets destined for local sockets), FORWARD (for packets routed through the local machine), OUTPUT (for locally generated packets).

nat table

This table is queried when packets are encountered for creating new connections. It contains three built-in chains: PREROUTING (for modifying packets as soon as they come in), OUTPUT (for modifying locally generated packets before routing), and POSTROUTING (for modifying packets before they come out).

NAT support for IPV6 from Linux kernel version 3.7

mangle table

This table is dedicated to packet modifications. Until Linux kernel 2.4.17 only two chains were supported: PREROUTING (for modifying incoming packets before routing) and OUTPUT (for modifying locally generated packets before routing). Starting with kernel 2.4.18, three additional chains are supported: INPUT (for sending to the local machine before), FORWARD (for routing packets through the local machine), and POSTROUTING (for modifying data before the packet is passed out).

raw table

This table is primarily used to configure exemptions from connection tracking in conjunction with the NOTRACK target. It has a higher priority for netfilter hook registration and is invoked before ip_conntrack and any other IP table. It contains two chains: PREROUTING (for packets arriving through any network interface), and OUTPUT (for any packets generated by the local program).

security Table

Used for Mandatory Access Control (MAC) network rules. It is a security policy, not discussed in detail in the text, which contains two chains: INPUT / OUTPUT.

The order of the call chains between the regular tables.

Order of call chains between regular tables

filter table basic policy

When the firewalld firewall is in its initial state.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
~]# firewall-cmd --list-all
public (active)
  target: default
  icmp-block-inversion: no
  interfaces: ens192
  sources:
  services: dhcpv6-client ssh
  ports:
  protocols:
  masquerade: no
  forward-ports:
  source-ports:
  icmp-blocks:
  rich rules:

INPUT chain

INPUT chain

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# iptables -L -v
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
  677 46644 ACCEPT     all  --  any    any     anywhere             anywhere             ctstate RELATED,ESTABLISHED
    0     0 ACCEPT     all  --  lo     any     anywhere             anywhere
    0     0 INPUT_direct  all  --  any    any     anywhere             anywhere
    0     0 INPUT_ZONES_SOURCE  all  --  any    any     anywhere             anywhere
    0     0 INPUT_ZONES  all  --  any    any     anywhere             anywhere
    0     0 DROP       all  --  any    any     anywhere             anywhere             ctstate INVALID
    0     0 REJECT     all  --  any    any     anywhere             anywhere             reject-with icmp-host-prohibited
# iptables -S
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -j INPUT_direct
-A INPUT -j INPUT_ZONES_SOURCE
-A INPUT -j INPUT_ZONES
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -j REJECT --reject-with icmp-host-prohibited

It first accepts all established connections, as well as all data from the lo network interface (local loopback).

1
2
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT

The two custom chain rules with target INPUT_direct / INPUT_ZONES_SOURCE are empty and reserved for specific firewalld configuration functions.

1
2
3
4
5
6
7
8
# iptables -L -v
Chain INPUT_ZONES_SOURCE (1 references)
 pkts bytes target     prot opt in     out     source               destination
Chain INPUT_direct (1 references)
 pkts bytes target     prot opt in     out     source               destination
# iptables -S
-A INPUT -j INPUT_direct
-A INPUT -j INPUT_ZONES_SOURCE

The INPUT_ZONES target contains the configuration of the network zone under.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# iptables -L -v
Chain INPUT_ZONES (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 IN_public  all  --  ens192 any     anywhere             anywhere            [goto]
    0     0 IN_public  all  --  +      any     anywhere             anywhere            [goto]
Chain IN_public (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 IN_public_log  all  --  any    any     anywhere             anywhere
    0     0 IN_public_deny  all  --  any    any     anywhere             anywhere
    0     0 IN_public_allow  all  --  any    any     anywhere             anywhere
    0     0 ACCEPT     icmp --  any    any     anywhere             anywhere
Chain IN_public_allow (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere             tcp dpt:ssh ctstate NEW,UNTRACKED
Chain IN_public_deny (1 references)
 pkts bytes target     prot opt in     out     source               destination
Chain IN_public_log (1 references)
 pkts bytes target     prot opt in     out     source               destination
# iptables -S
-A INPUT_ZONES -i ens192 -g IN_public
-A INPUT_ZONES -g IN_public
-A IN_public -j IN_public_log
-A IN_public -j IN_public_deny
-A IN_public -j IN_public_allow
-A IN_public -p icmp -j ACCEPT
-A IN_public_allow -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW,UNTRACKED -j ACCEPT

The INPUT_ZONES chain contains chains named IN_<network_zone>, which will also match the network interface. The default network zone will be the last matching rule.

1
2
-A INPUT_ZONES -i ens192 -g IN_public # ens192 is assigned to the public network zone
-A INPUT_ZONES -g IN_public # The default network zone is public

IN_public contains four rules, adds three chain targets: IN_public_log / IN_public_deny / IN_public_allow, and accepts all ICMP requests. The IN_public_log / IN_public_deny rule is empty and reserved for specific functions.

1
2
3
4
-A IN_public -j IN_public_log
-A IN_public -j IN_public_deny
-A IN_public -j IN_public_allow
-A IN_public -p icmp -j ACCEPT

The actual pass rule is contained in IN_public_allow, which allows port 22.

1
2
3
4
5
6
7
# iptables -L -v
Chain IN_public_allow (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     tcp  --  any    any     anywhere             anywhere             tcp dpt:ssh ctstate NEW,UNTRACKED

# iptables -S
-A IN_public_allow -p tcp -m tcp --dport 22 -m conntrack --ctstate NEW,UNTRACKED -j ACCEPT

At the end of the chain of INPUT, if no rule is matched, all invalid packets are discarded and all other requests are rejected.

1
2
-A INPUT -m conntrack --ctstate INVALID -j DROP
-A INPUT -j REJECT --reject-with icmp-host-prohibited

FORWARD chain

FORWARD chain

The FORWARD chain is very similar to the INPUT chain in the filter table, except that for network zones, custom chains are distinguished between IN and OUT.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
# iptables -L -v
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     all  --  any    any     anywhere             anywhere             ctstate RELATED,ESTABLISHED
    0     0 ACCEPT     all  --  lo     any     anywhere             anywhere
    0     0 FORWARD_direct  all  --  any    any     anywhere             anywhere
    0     0 FORWARD_IN_ZONES_SOURCE  all  --  any    any     anywhere             anywhere
    0     0 FORWARD_IN_ZONES  all  --  any    any     anywhere             anywhere
    0     0 FORWARD_OUT_ZONES_SOURCE  all  --  any    any     anywhere             anywhere
    0     0 FORWARD_OUT_ZONES  all  --  any    any     anywhere             anywhere
    0     0 DROP       all  --  any    any     anywhere             anywhere             ctstate INVALID
    0     0 REJECT     all  --  any    any     anywhere             anywhere             reject-with icmp-host-prohibited
# iptables -S
-A FORWARD -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A FORWARD -i lo -j ACCEPT
-A FORWARD -j FORWARD_direct
-A FORWARD -j FORWARD_IN_ZONES_SOURCE
-A FORWARD -j FORWARD_IN_ZONES
-A FORWARD -j FORWARD_OUT_ZONES_SOURCE
-A FORWARD -j FORWARD_OUT_ZONES
-A FORWARD -m conntrack --ctstate INVALID -j DROP
-A FORWARD -j REJECT --reject-with icmp-host-prohibited

In the FORWARD_IN_ZONES chain, match all input data from the web interface.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
# iptables -L -v
Chain FORWARD_IN_ZONES (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 FWDI_public  all  --  ens192 any     anywhere             anywhere            [goto]
    0     0 FWDI_public  all  --  +      any     anywhere             anywhere            [goto]
Chain FWDI_public (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 FWDI_public_log  all  --  any    any     anywhere             anywhere
    0     0 FWDI_public_deny  all  --  any    any     anywhere             anywhere
    0     0 FWDI_public_allow  all  --  any    any     anywhere             anywhere
    0     0 ACCEPT     icmp --  any    any     anywhere             anywhere
# iptables -S
-A FORWARD_IN_ZONES -i ens192 -g FWDI_public
-A FORWARD_IN_ZONES -g FWDI_public
-A FWDI_public -j FWDI_public_log
-A FWDI_public -j FWDI_public_deny
-A FWDI_public -j FWDI_public_allow
-A FWDI_public -p icmp -j ACCEPT

In the FORWARD_OUT_ZONES chain, match all outputs to the web interface for all data.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# iptables -L -v
Chain FORWARD_OUT_ZONES (1 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 FWDO_public  all  --  any    ens192  anywhere             anywhere            [goto]
    0     0 FWDO_public  all  --  any    +       anywhere             anywhere            [goto]
Chain FWDO_public (2 references)
 pkts bytes target     prot opt in     out     source               destination
    0     0 FWDO_public_log  all  --  any    any     anywhere             anywhere
    0     0 FWDO_public_deny  all  --  any    any     anywhere             anywhere
    0     0 FWDO_public_allow  all  --  any    any     anywhere             anywhere
# iptables -S
-A FORWARD_OUT_ZONES -o ens192 -g FWDO_public
-A FORWARD_OUT_ZONES -g FWDO_public
-A FWDO_public -j FWDO_public_log
-A FWDO_public -j FWDO_public_deny
-A FWDO_public -j FWDO_public_allow

However, under the initial firewalld configuration, all data under the FORWARD chain is discarded or rejected except for the match in FWDI_public to allow incoming ICMP protocols from the ens192 network interface.

OUTPUT chain

The rules for the OUTPUT chain are simple.

1
2
3
4
5
6
Chain OUTPUT (policy ACCEPT 699 packets, 117K bytes)
 pkts bytes target     prot opt in     out     source               destination
    0     0 ACCEPT     all  --  any    lo      anywhere             anywhere
  699  117K OUTPUT_direct  all  --  any    any     anywhere             anywhere
Chain OUTPUT_direct (1 references)
 pkts bytes target     prot opt in     out     source               destination

For local loopback data pass directly, otherwise match the OUTPUT_direct link used for the rule configuration of firewalld, with an initial rule of null. If not matched, then use the policy ACCEPT for the OUTPUT chain to allow all data to pass.

firewalld add port policy

Add ports directly in firewalld via services or commands, and via Rich Language as well.

1
2
3
firewall-cmd --zone=public --add-port=8080/tcp --permanent
firewall-cmd --zone=public --add-rich-rule='rule family=ipv4 source address=10.1.1.0/24 port port=8081 protocol=tcp accept' --permanent
firewall-cmd --reload

It will be added directly to IN_public_allow:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
INPUT
  |
  | - INPUT_direct
  | - INPUT_ZONES_SOURCE
  | - INPUT_ZONES
         |
         | - IN_public (-A INPUT_ZONES -i ens192 -g IN_public)
               |
               | - IN_public_log
               | - IN_public_deny
               | - IN_public_allow
               |      |
               |      | -A IN_public_allow -p tcp -m tcp --dport 8080 -m conntrack --ctstate NEW,UNTRACKED -j ACCEPT
               |      | -A IN_public_allow -s 10.1.1.0/24 -p tcp -m tcp --dport 8081 -m conntrack --ctstate NEW,UNTRACKED -j ACCEPT
               | - ACCEPT

Alternatively, the table and chain of iptables can be specified using firewalld’s direct add rule command.

1
firewall-cmd --direct --add-rule ipv4 filter IN_public_allow 0 -m tcp -p tcp --dport 8080 -j ACCEPT

It can also be added directly to the INPUT_direct table, which will match rules before the network zone chain INPUT_ZONES.

nat table basic policy

PREROUTING and POSTROUTING chains

The PREROUTING / POSTROUTING chain in the nat table has a similar configuration policy to the INPUT chain in the filter table, and only PREROUTING is listed here.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
PREROUTING (policy ACCEPT)
  |
  | - PREROUTING_direct
  | - PREROUTING_ZONES_SOURCE
  | - PREROUTING_ZONES
      |
      | - PRE_public (-A PREROUTING_ZONES -i ens192 -g PRE_public)
            |
            | - PRE_public_log
            | - PRE_public_deny
            | - PRE_public_allow
      | - PRE_public (-A POSTROUTING_ZONES -g POST_public)

INPUT and OUTPUT chains

The default configuration provided by firewalld in the nat table is simple.

1
2
3
4
5
6
7
8
# iptables -t nat -L -v

Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 16 packets, 2377 bytes)
 pkts bytes target     prot opt in     out     source               destination
   16  2377 OUTPUT_direct  all  --  any    any     anywhere             anywhere

firewalld add port forwarding policy

The preceding port forwarding configuration is an example.

1
2
firewall-cmd --zone=public --add-forward-port=port=8080:proto=tcp:toport=8081 --permanent
firewall-cmd --reload

firewalld adds the following rules to each of the mangle and nat tables.

1
2
3
4
# mangle
-A PRE_public_allow -p tcp -m tcp --dport 8080 -j MARK --set-xmark 0x64/0xffffffff
# nat
-A PRE_public_allow -p tcp -m mark --mark 0x64 -j DNAT --to-destination :8081

The mange table marks all packets from port 8080/tcp in the PREROUTING phase with a 0x64/0xffffffff, the former being the value and the latter being the mask. The result is heterogeneously added to ctmark. ctmark is a 32-bit marker value provided by netfilter.

1
2
# set-xmark Pseudocode description
xmark = xmark | (0x64 & 0xffffffff)

Adds a packet to the nat table that matches the 0x64/0xffffffff ctmark value if successful and adds it to the DNAT destination. Specify the destination parameter as forwarding to local port 8081.

DNAT targets can only be used in the PREROUTING and OUPUT chains in the nat table and the associated call chains.

Alternatively, try configuring iptables directly for port forwarding without starting firewalld.

1
2
iptables -t mangle -A PREROUTING -p tcp -m tcp --dport 8080 -j MARK --set-xmark 0x64/0xffffffff
iptables -t nat -A PREROUTING -p tcp -m mark --mark 0x64 -j DNAT --to-destination :8081

Ending

This article briefly describes the basic operation and configuration of iptables and firewalld in CentOS as a test environment.s

firewalld can configure iptables at a high level of abstraction, for example by internally maintaining a ctmark value for port forwarding.

For port openings that do not specify an ip protocol version, firewalld maintains both ipv4 and ipv6 configurations, which can be viewed separately with the following command.

1
2
iptables -L -v
ip6tables -L -v

There are tools similar to firewalld in other Linux distributions, and iptables is present in most distributions.