Firewalld Rich and Direct Rules: Setting up RHEL 7 Server as a Router

We are going to configure RHEL server as a router. Masquerading, port forwarding, rich and direct rules will be covered. 

The Lab

We have three RHEL 7.0 servers available in our lab:

  1. ipa (10.10.1.79, 10.8.8.70) – will be configured as a router,
  2. srv1 (10.8.8.71) – a server on our DMZ network, will be used to test masquerading,
  3. pub (10.10.1.10) – a server on our public network, will be used to test port forwarding.

The ipa server has SELinux set to enforcing mode.

Network Interfaces on the Router Server

The ipa server has two network interfaces attached and configured:

# nmcli d
DEVICE   TYPE      STATE      CONNECTION 
enp0s17  ethernet  connected  enp0s17    
enp0s8   ethernet  connected  enp0s8     
lo       loopback  unmanaged  --

The enp0s8 is connected to our “public” network 10.10.1.0/24, where enp0s17 is connected to DMZ 10.8.8.0/24.

# ip -4 ad
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN 
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
2: enp0s8: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    inet 10.10.1.79/24 brd 10.10.1.255 scope global dynamic enp0s8
       valid_lft 86376sec preferred_lft 86376sec
3: enp0s17: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
    inet 10.8.8.70/24 brd 10.8.8.255 scope global dynamic enp0s17
       valid_lft 3583sec preferred_lft 3583sec

Routing table can be seen below:

# ip ro
default via 10.10.1.1 dev enp0s8  proto static  metric 1024 
10.8.8.0/24 dev enp0s17  proto kernel  scope link  src 10.8.8.70 
10.10.1.0/24 dev enp0s8  proto kernel  scope link  src 10.10.1.79

Exclude all iptables-based services from ever being started:

# systemctl mask iptables ip6tables ebtables
ln -s '/dev/null' '/etc/systemd/system/iptables.service'
ln -s '/dev/null' '/etc/systemd/system/ip6tables.service'
ln -s '/dev/null' '/etc/systemd/system/ebtables.service'

There is apparently a bug in RHEL 7.1 and RHEL 7.2 that prevents the iptables service from being masked if the package iptables-services is not installed:

[rhel7.1]# systemctl mask iptables
Failed to issue method call: Access denied
[rhel7.1]# cat /var/log/audit/audit.log
type=USER_AVC msg=audit(1468920066.358:447): pid=1 uid=0 auid=4294967295 ses=4294967295 subj=system_u:system_r:init_t:s0 msg='avc:  denied  { disable } for auid=0 uid=0 gid=0 cmdline="systemctl mask iptables ip6tables ebtables" scontext=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 tcontext=system_u:system_r:init_t:s0 tclass=service  exe="/usr/lib/systemd/systemd" sauid=0 hostname=? addr=? terminal=?'

The version of the policy that has a bug is this:

[rhel7.1]# rpm -q selinux-policy-targeted
selinux-policy-targeted-3.13.1-23.el7.noarch
[rhel7.2]# rpm -q selinux-policy-targeted
selinux-policy-targeted-3.13.1-60.el7.noarch

Installing the iptables-services package, or putting SELinux in to permissive mode, allows masking of iptables service on RHEL 7.1 and RHEL 7.2.

Firewalld Default Zone

Both network interfaces should be added to the zone public:

# firewall-cmd --get-active-zones
public
  interfaces: enp0s17 enp0s8

It should be the default zone:

# firewall-cmd --get-default-zone
public

We still want to set the default zone to public (so that we know how to change it), and assign the interface enp0s17 to the zone dmz.

Set the default firewall zone to public, remove the network interface enp0s17 from the zone public and add to the zone dmz.

# firewall-cmd --set-default-zone=public
# firewall-cmd --remove-interface=enp0s17 --zone=public
# firewall-cmd --permanent --add-interface=enp0s17 --zone=dmz

A permanent configuration requires a reload of the firewall configuration to work as expected:

# firewall-cmd --reload

Let us see the actives zones:

# firewall-cmd --get-active-zones
dmz
  interfaces: enp0s17
public
  interfaces: enp0s8

Make sure that zones are correctly set in the network scripts:

# nmcli con mod enp0s8 connection.zone public
# nmcli con mod enp0s17 connection.zone dmz
# nmcli c reload

Custom Firewalld Services

This part shows the way we can create custom firewalld services. We are going to create one for iSCSI target.

Copy one of the existing firewalld service configuration files, say for SSH:

# cp /usr/lib/firewalld/services/ssh.xml /etc/firewalld/services/iscsi-target.xml

Open the file /etc/firewalld/services/iscsi-target.xml for editing, and put the following:

<?xml version="1.0" encoding="utf-8"?>
<service>
 <short>iSCSI Target</short>
 <description>iSCSI target.</description>
 <port protocol="tcp" port="3260"/>
</service>

The newly created firewalld service can be added to the zone dmz this way:

# firewall-cmd --permanent --zone=dmz --add-service iscsi-target

More services depending on requirements can be also added, for example:

# firewall-cmd --permanent --zone=dmz --add-service={http,https,ldap,ldaps,kerberos,dns,kpasswd,ntp,ftp}
# firewall-cmd --reload

List all services that are added to the zone dmz:

# firewall-cmd --list-services --zone=dmz
dns ftp http https iscsi-target kerberos kpasswd ldap ldaps ntp ssh

List all services that are added to the zone public:

# firewall-cmd --list-services --zone=public
dhcpv6-client

Packet Forwarding

To configure routing, the server needs to forward incoming packets from one interface to another interface.

Create a new file /etc/sysctl.d/ip_forward.conf and add the following:

net.ipv4.ip_forward=1

The above makes the change permanent. Now change the runtime value:

# sysctl -w net.ipv4.ip_forward=1

Routing with Rich Rules

In general, all we have to do is to enable masquerading on the public interface. The command below masquerades packets coming from all hosts in the zone public:

# firewall-cmd --permanent --zone=public --add-masquerade
# firewall-cmd --reload

In other words, the above allows all traffic to leave from the zone dmz to the zone public.

The easy way to test this is to try to access some public website from any VM that resides in the dmz network 10.8.8.0/24. We have a server srv1 that’s on 10.8.8.71, and it has the default route going via 10.8.8.70:

[srv1]# ip ro
default via 10.8.8.70 dev mybond0  proto static  metric 1024 
10.8.8.0/24 dev mybond0  proto kernel  scope link  src 10.8.8.71

If we were to try to get access to example.com with masquerading disabled, we would have the following:

[srv1]# wget -O - http://example.com
--2016-06-18 17:47:30--  http://example.com/
Resolving example.com (example.com)... 93.184.216.34
Connecting to example.com (example.com)|93.184.216.34|:80... failed: No route to host.

When masquerading on the zone public is enabled, we can access the site:

[srv1]# wget http://example.com
Resolving example.com (example.com)... 93.184.216.34
Connecting to example.com (example.com)|93.184.216.34|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 1270 (1.2K) [text/html]
Saving to: 'index.html'

100%[================================>] 1,270  --.-K/s   in 0s

2016-06-18 17:50:19 (129 MB/s) - 'index.html' saved [1270/1270]

If we need some more control over which packets exactly are masqueraded, we can add a rich rule that matches packets coming from specific source addresses only, for example:

# firewall-cmd --permanent --zone=public --add-rich-rule='rule family=ipv4 source address=10.8.8.0/24 masquerade'
# firewall-cmd --reload

Let us check the rich rule:

# firewall-cmd --zone=public --list-all
public (default, active)
  interfaces: enp0s8
  sources:
  services: dhcpv6-client
  ports:
  masquerade: yes
  forward-ports:
  icmp-blocks:
  rich rules:
      rule family="ipv4" source address="10.8.8.0/24" masquerade

Routing with Direct Rules

Routing can also be achieved with direct rules. However, the firewalld man page says that direct options should be used only as a last resort when it’s not possible to use for example –add-rich-rule=’rule’.

The syntax is below:

[--permanent] --direct --add-rule { ipv4 | ipv6 | eb } table chain priority args
    Add a rule with the arguments args to chain chain in table table with priority priority.

    The priority is used to order rules. Priority 0 means add rule on top of the chain, with a higher priority
    the rule will be added further down. Rules with the same priority are on the same level and the order of
    these rules is not fixed and may change. If you want to make sure that a rule will be added after another
    one, use a low priority for the first and a higher for the following.

To allow our dmz (enp0s17) network VMs with private IP addresses to communicate with external networks, we have to configure firewall for IP masquerading:

# firewall-cmd --permanent --direct --add-rule ipv4 nat POSTROUTING 0 -o enp0s8 -j MASQUERADE

Forward all ICMP requests from the zone dmz (enp0s17) to the zone public (enp0s8):

# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i enp0s17 -o enp0s8 \
  -p icmp -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT

Do the same for all HTTP and HTTPS traffic:

# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i enp0s17 -o enp0s8 \
  -p tcp -m multiport --dport 80,443 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT

Also allow access to public SMTP and SMTPS servers:

# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i enp0s17 -o enp0s8 \
  -p tcp -m multiport --dport 25,465 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT

Allow to SSH into public servers:

# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i enp0s17 -o enp0s8 \
  -p tcp --dport 22 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT

Log everything else:

# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i enp0s17 -o enp0s8 \
  -j LOG --log-prefix "forward_fw "

Optional, but not really required:

# firewall-cmd --permanent --direct --add-rule ipv4 filter FORWARD 0 -i enp0s17 -o enp0s8 -j REJECT

Reload:

# firewall-cmd --reload

Check the direct rules:

# firewall-cmd --direct --get-all-rules
ipv4 nat POSTROUTING 0 -o enp0s8 -j MASQUERADE
ipv4 filter FORWARD 0 -i enp0s17 -o enp0s8 -p icmp -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
ipv4 filter FORWARD 0 -i enp0s17 -o enp0s8 -p tcp -m multiport --dport 80,443 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
ipv4 filter FORWARD 0 -i enp0s17 -o enp0s8 -p tcp -m multiport --dport 25,465 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
ipv4 filter FORWARD 0 -i enp0s17 -o enp0s8 -p tcp --dport 22 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
ipv4 filter FORWARD 0 -i enp0s17 -o enp0s8 -j LOG --log-prefix 'forward_fw '

We can use the same server srv1 to test the rules above. Anything that doesn’t match should be blocked and logged. For example, let us try to access example.com via FTP:

[srv1]# wget -O - ftp://example.com
--2016-06-18 18:13:49--  ftp://example.com/
           => ‘.listing’
Resolving example.com (example.com)... 93.184.216.34
Connecting to example.com (example.com)|93.184.216.34|:21... failed: No route to host.

And the log entry should be:

forward_fw IN=enp0s17 OUT=enp0s8 MAC=08:00:27:ff:70:00:08:00:27:ff:81:00:08:00 SRC=10.8.8.71 DST=93.184.216.34 LEN=60 TOS=0x00 PREC=0x00 TTL=63 ID=10304 DF PROTO=TCP SPT=34579 DPT=21 WINDOW=14600 RES=0x00 SYN URGP=0

Rich Rules and Port Forwarding

We may want to access servers that reside on the dmz network (10.8.8.0/24) from the public (10.10.1.0/24) over SSH. To do so, we need to put some port forwarding rules.

The rules below configure port forwarding so that connections to 10.10.1.79:2271 are forwarded to 10.8.8.71:22, and SSH logging.

# firewall-cmd --permanent --zone=public --add-forward-port='port=2271:proto=tcp:toport=22:toaddr=10.8.8.71'
# firewall-cmd --permanent --zone=public --add-rich-rule='rule service name=ssh log prefix="SSH_" level="debug" limit value=1/m reject'
# firewall-cmd --reload

Let us see the rules:

# firewall-cmd --list-all --zone=public
public (default, active)
  interfaces: enp0s8
  sources:
  services: dhcpv6-client
  ports:
  masquerade: yes
  forward-ports: port=2271:proto=tcp:toport=22:toaddr=10.8.8.71
  icmp-blocks:
  rich rules:
        rule service name="ssh" log prefix="SSH_" level="debug" limit value="1/m" reject

To test port forwarding, we need some server that’s in the public network (10.10.1.0/24):

[pub]$ ssh [email protected] -p2271
The authenticity of host '[10.10.1.79]:2271 ([10.10.1.79]:2271)' can't be established.
ECDSA key fingerprint is 32:e6:80:ec:ec:3e:d0:6a:7b:78:bf:6e:79:90:8f:2d.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '[10.10.1.79]:2271' (ECDSA) to the list of known hosts.
[email protected]'s password: 
Last login: Sat Jun 18 17:29:59 2016 from 10.8.8.1

Static Routes

If we need to create a static route to test something, you we can do so with a single bash command, for example:

# ip route add 10.8.9.0/24 via 10.8.8.70 dev enp0s17

69 thoughts on “Firewalld Rich and Direct Rules: Setting up RHEL 7 Server as a Router

  1. Hi Tomas, sorry to bother you again on another topic…
    Port forwarding – from the firewall man page –
    –add-forward-port=port=portid[-portid]:proto=protocol[:toport=portid[-portid]][:toaddr=address[/mask]]
    It seems to me the “toaddr=address/mask” is conflicting with line below in the same man page
    “The port can either be a single port number portid or a port range portid-portid. The protocol can
    either be tcp or udp. The destination address is a simple IP address.”
    :::::
    So for example, is this an invalid destination address “10.10.1.0/24” ?
    Should it be a specific simple IP address?

    • It shouldn’t even allow you to create a port forwarding rule if I use a mask of anything but /32.

      The man page is correct, it is a simple IP address.

  2. Hey Tomas, the last question: should I enable anything to make firewalld direct rules work? any module?
    The following one is not working, maybe its not correct:

    [root@server1 ~]# firewall-cmd –permanent –direct –add-rule ipv4 filter INPUT 0 -s 192.168.0.103 -p tcp –dport 2222 reject
    success
    [root@server1 ~]# firewall-cmd –direct –get-all-rules
    [root@server1 ~]#

    • I use direct rules for routing, but never for filtering incoming traffic (that’s what you seem to want to do) as this can be easily achieved with firewalld rich rules. Let me know if you figure it out.

  3. I tried using http with rich-rule and source address method for one ip in defferent zone.
    But I always get Failed connect – ; No route to host
    What I am doing wrong here ??
    ——————case1
    [root@csrv176 ~]# firewall-cmd –permanent –remove-rich-rule=’rule family=”ipv4″ source address=”192.168.1.175/32″ service name=”http” log prefix=”http_from_x” level=”info” limit value=”30/m” accept’ –zone=work
    success
    [root@csrv176 ~]# firewall-cmd –permanent –remove-rich-rule=’rule family=”ipv4″ source address=”192.168.1.175″ service name=”http” log prefix=”http_from_y” level=”info” limit value=”30/m” accept’ –zone=work
    success

    [root@csrv176 ~]# firewall-cmd –reload
    success

    [root@csrv176 ~]# firewall-cmd –list-all –zone=work
    work
    interfaces:
    sources:
    services: dhcpv6-client ipp-client ssh
    ports:
    masquerade: no
    forward-ports:
    icmp-blocks:
    rich rules:
    rule family=”ipv4″ source address=”192.168.1.175/32″ service name=”http” log prefix=”http_from_x” level=”info” limit value=”30/m” accept
    rule family=”ipv4″ source address=”192.168.1.175″ service name=”http” log prefix=”http_from_y” level=”info” limit value=”30/m” accept

    ——————-case2
    [root@csrv176 ~]# firewall-cmd –new-zone=sphttp –permanent
    [root@csrv176 ~]# firewall-cmd –permanent –add-source=192.168.1.175 –zone=sphttp
    success
    [root@csrv176 ~]# firewall-cmd –permanent –add-source=192.168.1.175/32 –zone=sphttp
    success
    [root@csrv176 ~]# firewall-cmd –reload
    success
    [root@csrv176 ~]# firewall-cmd –list-all –zone=sphttp
    sphttp
    interfaces:
    sources: 192.168.1.175 192.168.1.175/32
    services: http
    ports:
    masquerade: no
    forward-ports:
    icmp-blocks:
    rich rules:
    ———————–error

    [root@csrv175 ~]# curl http:// 192.168.1.176
    curl: (7) Failed connect to 192.168.1.176:80; No route to host

    • By default it is only assigned to default public zone. Can we assign the same interface to more then one zone ? If so then how can we use other zones like internal,work etc..
      I thought It is good idea not to keep interface into the same zone to make it more secure
      e.g. to make ssh to only certain ips I create new zone “exam” for ssh, add source ips, remove ssh from pubic. No interface assigned to this zone to not override source IPs.
      The result of this will be a “exam” zone which permits access to ssh, but only from given IP addresses. This works fine. but it didn’t work for http.

      Here is the info.
      [root@csrv176 ~]# firewall-cmd –get-active-zones
      sphttp
      sources: 192.168.1.175 192.168.1.175/32
      public
      interfaces: eno16777736 eno33554960 eno50332184 team1
      exam
      sources: 192.168.1.0/24
      [root@csrv176 ~]# firewall-cmd –list-all –zone=public
      public (default, active)
      interfaces: eno16777736 eno33554960 eno50332184 team1
      sources:
      services: dhcpv6-client
      ports:
      masquerade: no
      forward-ports:
      icmp-blocks:
      rich rules:

      root@csrv176 ~]# firewall-cmd –list-all –zone=exam
      exam
      interfaces:
      sources: 192.168.1.0/24
      services: ssh
      ports: 2022/tcp
      masquerade: no
      forward-ports: port=2022:proto=tcp:toport=22:toaddr=
      icmp-blocks:
      rich rules:
      rule family=”ipv4″ source address=”192.168.1.175″ service name=”ssh” log prefix=”ssh-reject” level=”info” limit value=”3/m” reject
      rule family=”ipv4″ source address=”192.168.1.170″ service name=”ssh” log prefix=”ssh-reject” level=”info” limit value=”3/m” reject

      ——————————————
      My understanding/reading says:
      ———————————————-
      Traffic sources can be designated in two ways: By interface, or by source IP address. Traffic that matches any source passes this check. A firewalld zone defined with only source addresses and without interfaces will apply to all traffic from those addresses, regardless of which interface it came in.

      – is the packet coming from a source already bound to a zone? (if yes, it is associated with this zone),
      – if not, is the packet coming from a network interface already bound to a zone? (if yes, it is associated with this zone),
      – if not, the packet is associated with the default zone.

  4. I only want to add my observation, but still looking for the answer

    -Zone become active if you add source, service to the zone. So my second case 2 should work
    as it appears in active zone list.

    [root@csrv176 ~]# firewall-cmd –get-active-zones
    sphttp
    sources: 192.168.1.175 192.168.1.175/32
    public
    interfaces: eno16777736 eno33554960 eno50332184 team1
    exam
    sources: 192.168.1.0/24

    [root@csrv176 ~]# firewall-cmd –list-all –zone=sphttp
    sphttp
    interfaces:
    sources: 192.168.1.175 192.168.1.175/32
    services: http
    ports:

    —If you just add rich rule, zone doesn’t become active, so I may say case 1 may not work as it doesn’t appear in the active zone list. But if you look zone xml file /etc/firewalld/zones/work.xml then it does clearly define source, service. This throws another question – why zone do not become active with rich rule only ? I don’t see anywhere in the document where it mentions this specific issue. I also wonder if I am using firewalld right way ;)

  5. oh I mentioned it from the beginning of my thread but may be not clear
    I tried to allow http service with -rich-rule- and -source address- method for -one ip- in different zone.
    Both method doesn’t allow http service and fails with error “Failed connect – ; No route to host”

    • Do you get any hits for the http rule? If you don’t, then it’s blocked some other place before you even reach it.

  6. Hello,
    Thanks for your post, very helpful!
    Do you now if there is a way to limit the masquerading destination ? for exemple disable all http calls or enable only port 587 has destination?
    Thank for your answer.
    Regards

  7. Hello Amigo, thanks for your post.

    Not sure what is wrong on my below config, but i do not get PC on internal zone reach public zones.
    Below my config:
    [root@ecs-fw01 ~]# firewall-cmd –direct –get-all-rules
    ipv4 nat POSTROUTING 0 -o eth0 -j MASQUERADE
    ipv4 filter FORWARD 0 -i eth1 -o eth0 -p icmp -m state –state NEW,RELATED,ESTABLISHED -j ACCEPT
    ipv4 filter FORWARD 0 -i eth1 -o eth0 -p tcp -m multiport –dport 80,443 -m state –state NEW,RELATED,ESTABLISHED -j ACCEPT
    ipv4 filter FORWARD 0 -i eth1 -o eth0 -j LOG –log-prefix ‘forward_fw ‘

    [root@ecs-fw01 ~]# firewall-cmd –list-all –zone=internal
    internal (active)
    target: default
    icmp-block-inversion: no
    interfaces: eth1
    sources:
    services: ssh mdns samba-client http https dns smtp
    ports: 53/udp 80/tcp 443/tcp
    protocols: IP icmp
    masquerade: no
    forward-ports:
    source-ports:
    icmp-blocks:
    rich rules:

    [root@ecs-fw01 ~]#
    [root@ecs-fw01 ~]#
    [root@ecs-fw01 ~]# firewall-cmd –list-all –zone=public
    public (active)
    target: default
    icmp-block-inversion: no
    interfaces: eth0
    sources:
    services: ssh dns http https
    ports:
    protocols: icmp ip
    masquerade: yes
    forward-ports:
    source-ports:
    icmp-blocks:
    rich rules:

    [root@ecs-fw01 ~]# firewall-cmd –get-active-zones
    internal
    interfaces: eth1
    public
    interfaces: eth0

    • Please post the output of the following commands:

      # ip ro
      # iptables -nL
      # iptables -nL -t nat
      # cat /proc/sys/net/ipv4/ip_forward
  8. I am bit confused with port forwarding thing.

    This is what i am trying to acheive.
    When i ssh on server1 ( 192.168.1.244 ) on port 9090 , it should take me to server2 on port 22 (192.168.1.252)

    on Server 1: ( 192.168.1.244/24 )
    firewall-cmd –permanent –add-forward-port=’port=9090:proto=tcp:toport=22:toaddr=192.168.1.252′
    firewall-cmd –reload

    Server1: 192.168.1.244/24
    Server2: 192.168.1.252/24
    ssh runs on port 22 on both servers,
    firewall enabled on both servers

    After the above rule. ( from Server1: 192.168.1.244/24 itself)
    #ssh 192.168.1.244 -p9090
    ssh: connect to host 192.168.1.244 port 9090: Connection refused

    What the best way to test port forwarding in lab.
    1: do i need to enable masq ?

    Thanks for a detailed answer

    • It does work for me. Try the following on the server2:

      [server2]# ssh 192.168.1.244 -p9090

      The above will match the port forwarding rule that is set on the server1 and will forward the request to the server2. You do need to enable masquerading on the server1 as well as net.ipv4.ip_forward=1. Hope this explains it.

  9. Tomas, please help.

    I have server with external IP (I1) and I forward traffic through this server to other external ip (I2) and port. It works, but I want that I connect to I1 from certain externel ip.
    For example: 1.1.1.1:444 forward to 2.2.2.2:444 and I must connect to 1.1.1.1:444 only 3.3.3.3 other traffic must drop. Sorry for my English.

    I have this configuration, but I can connect from different ip to port 444.
    firewall-cmd –permanent –zone=public –add-forward-port=port=444:proto=tcp:toport=444:toaddr=94.22.11.11
    firewall-cmd –permanent –add-service=PRDP
    firewall-cmd –permanent –service=PRDP –add-port=444/tcp
    firewall-cmd –permanent –zone=public –set-target=DROP
    firewall-cmd –permanent –zone=public –add-rich-rule ‘rule family=”ipv4″ service name=”PRDP” source address=”My_IP” accept’

    How can I do it? Thank you.

  10. [root@portmap ~]# iptables -nL
    Chain INPUT (policy ACCEPT)
    target     prot opt source               destination
    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
    INPUT_direct  all  --  0.0.0.0/0            0.0.0.0/0
    INPUT_ZONES_SOURCE  all  --  0.0.0.0/0            0.0.0.0/0
    INPUT_ZONES  all  --  0.0.0.0/0            0.0.0.0/0
    DROP       all  --  0.0.0.0/0            0.0.0.0/0            ctstate INVALID
    REJECT     all  --  0.0.0.0/0            0.0.0.0/0            reject-with icmp-host-prohibited
    
    Chain FORWARD (policy ACCEPT)
    target     prot opt source               destination
    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
    FORWARD_direct  all  --  0.0.0.0/0            0.0.0.0/0
    FORWARD_IN_ZONES_SOURCE  all  --  0.0.0.0/0            0.0.0.0/0
    FORWARD_IN_ZONES  all  --  0.0.0.0/0            0.0.0.0/0
    FORWARD_OUT_ZONES_SOURCE  all  --  0.0.0.0/0            0.0.0.0/0
    FORWARD_OUT_ZONES  all  --  0.0.0.0/0            0.0.0.0/0
    DROP       all  --  0.0.0.0/0            0.0.0.0/0            ctstate INVALID
    REJECT     all  --  0.0.0.0/0            0.0.0.0/0            reject-with icmp-host-prohibited
    
    Chain OUTPUT (policy ACCEPT)
    target     prot opt source               destination
    OUTPUT_direct  all  --  0.0.0.0/0            0.0.0.0/0
    
    Chain FORWARD_IN_ZONES (1 references)
    target     prot opt source               destination
    FWDI_public  all  --  0.0.0.0/0            0.0.0.0/0
    FWDI_public  all  --  0.0.0.0/0            0.0.0.0/0
    
    Chain FORWARD_IN_ZONES_SOURCE (1 references)
    target     prot opt source               destination
    
    Chain FORWARD_OUT_ZONES (1 references)
    target     prot opt source               destination
    FWDO_public  all  --  0.0.0.0/0            0.0.0.0/0
    FWDO_public  all  --  0.0.0.0/0            0.0.0.0/0
    
    Chain FORWARD_OUT_ZONES_SOURCE (1 references)
    target     prot opt source               destination
    
    Chain FORWARD_direct (1 references)
    target     prot opt source               destination
    
    Chain FWDI_public (2 references)
    target     prot opt source               destination
    FWDI_public_log  all  --  0.0.0.0/0            0.0.0.0/0
    FWDI_public_deny  all  --  0.0.0.0/0            0.0.0.0/0
    FWDI_public_allow  all  --  0.0.0.0/0            0.0.0.0/0
    DROP       all  --  0.0.0.0/0            0.0.0.0/0
    
    Chain FWDI_public_allow (1 references)
    target     prot opt source               destination
    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            ctstate NEW mark match 0x64
    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            ctstate NEW mark match 0x65
    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0            ctstate NEW mark match 0x66
    
    Chain FWDI_public_deny (1 references)
    target     prot opt source               destination
    
    Chain FWDI_public_log (1 references)
    target     prot opt source               destination
    
    Chain FWDO_public (2 references)
    target     prot opt source               destination
    FWDO_public_log  all  --  0.0.0.0/0            0.0.0.0/0
    FWDO_public_deny  all  --  0.0.0.0/0            0.0.0.0/0
    FWDO_public_allow  all  --  0.0.0.0/0            0.0.0.0/0
    DROP       all  --  0.0.0.0/0            0.0.0.0/0
    
    Chain FWDO_public_allow (1 references)
    target     prot opt source               destination
    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
    
    Chain FWDO_public_deny (1 references)
    target     prot opt source               destination
    
    Chain FWDO_public_log (1 references)
    target     prot opt source               destination
    
    Chain INPUT_ZONES (1 references)
    target     prot opt source               destination
    IN_public  all  --  0.0.0.0/0            0.0.0.0/0
    IN_public  all  --  0.0.0.0/0            0.0.0.0/0
    
    Chain INPUT_ZONES_SOURCE (1 references)
    target     prot opt source               destination
    
    Chain INPUT_direct (1 references)
    target     prot opt source               destination
    REJECT     tcp  --  0.0.0.0/0            0.0.0.0/0            multiport dports 22 match-set fail2ban-sshd src reject-with icmp-port-unreachable
    REJECT     tcp  --  0.0.0.0/0            0.0.0.0/0            multiport dports 22 match-set fail2ban-sshd-ddos src reject-with icmp-port-unreachable
    REJECT     tcp  --  0.0.0.0/0            0.0.0.0/0            multiport dports 22 match-set fail2ban-dropbear src reject-with icmp-port-unreachable
    REJECT     tcp  --  0.0.0.0/0            0.0.0.0/0            multiport dports 22 match-set fail2ban-selinux-ssh src reject-with icmp-port-unreachable
    REJECT     tcp  --  0.0.0.0/0            0.0.0.0/0            multiport dports 80,443 match-set fail2ban-nginx-http-auth src reject-with icmp-port-unreachable
    REJECT     tcp  --  0.0.0.0/0            0.0.0.0/0            multiport dports 80,443 match-set fail2ban-nginx-limit-req src reject-with icmp-port-unreachable
    REJECT     tcp  --  0.0.0.0/0            0.0.0.0/0            multiport dports 80,443 match-set fail2ban-nginx-botsearch src reject-with icmp-port-unreachable
    REJECT     tcp  --  0.0.0.0/0            0.0.0.0/0            multiport dports 80,443 match-set fail2ban-php-url-fopen src reject-with icmp-port-unreachable
    
    Chain IN_public (2 references)
    target     prot opt source               destination
    IN_public_log  all  --  0.0.0.0/0            0.0.0.0/0
    IN_public_deny  all  --  0.0.0.0/0            0.0.0.0/0
    IN_public_allow  all  --  0.0.0.0/0            0.0.0.0/0
    DROP       all  --  0.0.0.0/0            0.0.0.0/0
    
    Chain IN_public_allow (1 references)
    target     prot opt source               destination
    ACCEPT     tcp  --  93.Y.184.74         0.0.0.0/0            tcp dpt:444 ctstate NEW
    
    
    Chain IN_public_deny (1 references)
    target     prot opt source               destination
    
    Chain IN_public_log (1 references)
    target     prot opt source               destination
    
    Chain OUTPUT_direct (1 references)
    target     prot opt source               destination
    • Thanks. Even though firewalld uses netfilter under the hood, you sometimes need to get into direct rules to get advanced stuff working.

      Try the following (I use IPs and ports to match your example). Create a destination NAT rule to forward all (source 3.3.3.3/32) traffic from TCP port 444 to 2.2.2.2:444.

      # iptables -t nat -A PREROUTING -s 3.3.3.3/32 -p tcp --dport 444 -j DNAT --to-destination 2.2.2.2:444

      Then create a forwarding rule as well as allow all related and established traffic:

      # iptables -A FORWARD -d 2.2.2.2/32 -p tcp --dport 444 -j ACCEPT
      # iptables -A FORWARD -p tcp -m state --state RELATED,ESTABLISHED -j ACCEPT

      Finally, enable IP masquerading:

      # iptables -t nat -A POSTROUTING -p tcp -j MASQUERADE

      The above will do the trick. If you require a firewalld solution then you’ll have to figure it out yourself I’m afraid.

    • I’ve found solution with Firewalld -https://fedoraproject.org/wiki/Features/FirewalldRichLanguage

      firewall-cmd —zone=public —permanent —add-rich-rule=’rule family=”ipv4″ source address=”MY_IP” forward-port to-addr=”95.X.11.15″ to-port=”443″ port=”443″ protocol=”tcp”‘

      And this rule is being deleted -forward-ports: port=444:proto=tcp:toport=444:toaddr=95.X.11.15

  11. [root@portmap ~]# firewall-cmd --list-all --zone=public
    public (active)
      target: DROP
      icmp-block-inversion: no
      interfaces: eth0
      sources:
      services:
      ports:
      protocols:
      masquerade: yes
      forward-ports: port=444:proto=tcp:toport=444:toaddr=95.X.11.15
      sourceports:
      icmp-blocks:
      rich rules:
                rule family="ipv4" source address="93.Y.184.74" service name="PRDP" accept
  12. Hi Tomas, my server ip is 192.168.10.40. I just want forward the local port 22 to 80 on this server for systems in the 192.168.10.0/24 network. I just did: # firewall-cmd –permanent –add-forward-port=port=22:proto=tcp:toport=80 which is successful but I didn’t put any source addresses and destination address.Am I right here? If I need to put,Should I put network address(192.168.10.0) as source addresses or my server ip address 192.168.10.40? Or not needed at all?Please respond to me.

    • The last time I checked firewalld did not allow port redirection from localhost. I would suggest to use iptables to create a NAT rule to achieve port redirection.

    • Can do, but keep in mind that being able to find information is somewhat an exam requirement :)

      # iptables -t nat -A PREROUTING -p tcp -m tcp --dport 22 -j REDIRECT --to-ports 80
  13. Hi Tomas,
    Is your examples almost as the same as the exam? Am i correct, that if i have a 2 vm, they should be able to connect to internet? I am planning to do my testing offline. without connecting to internet. BUt your setup is should be able to coccent to internet..that is based from the exam setup you gave

    • These are examples from my homelab. You do not need internet connection to use the examples, as these are based on two private subnets 10.10.1.0/24 and 10.8.8.0/24 (see the blog post).

    • Firewalld uses iptables under the hood, so I find iptables quite helpful when I have to troubleshoot firewalld configuration. However, you don’t need to learn iptables to configure your server as a router, you can use firewalld.

  14. Ok thank you for your time. Can you please tell me, did you turn off your Netwokrmanager during exam? Is nmcli is really needed? Or modifying the config file is enough? I have mastered the iscsi,smtp mariadb, but im lacking with kerberos and this nmcli as well as the rich rules.. Im trying to squeeze my time, eliminate what i can eliminate. muy exam is in 15 days.. i Hvve 3 days to decide if i will resched it in May or not.

    • I cannot tell you what I did during the exam, but if you don’t feel confident, then it makes sense to reschedule it.

  15. Thomas, good afternoon.

    I am studying for the EX300 exam, but I have doubts about Rich Rules and Port Forwarding

    The Port Forwarding I understood

    to redirect ssh to port 6000
    $ firewall-cmd –add-forward-port=port=6000:proto=tcp:toport=22:toaddr=192.168.122.197

    # ssh -p 6000 192.168.122.197 – it’s ok

    Now to do the same using the rich rules I’m not getting

    $ firewall-cmd –add-rich-rule ‘rule family=ipv4 source address=192.168.122.197 forward-port port=80 protocol=tcp to-port=8080’

    $ elinks http:// 192.168.122.197:8080

    The “conection refused” error occurs

    # firewall-cmd –list-all
    public (default, active)
    interfaces: eth0 eth1 team0
    sources:
    services: dhcpv6-client http https ssh
    ports: 3260/tcp
    masquerade: yes
    forward-ports:
    icmp-blocks:
    rich rules:
    rule family=”ipv4″ source address=”192.168.122.197″ forward-port port=”80″ protocol=”tcp” to-port=”8080″

    what am I doing wrong?

    thank you

    • In the second example you seem to use IP 192.168.122.197 as the source address, however, in the first example the same IP address is used as the destination address.

    • It was just an example. I meant that with port-forward I was able to redirect ssh.

      But using the rich rules technique I can not redirect

      firewall-cmd –add-rich-rule “rule family=ipv4 source address=192.168.122.197 forward-port port=6000 protocol=tcp to-port=22”

  16. Hello Tomas

    Last week i gave RHCSA exam and got 300/300 now I’m preparing for RHCE exam

    I installed IPA server on centos 7.2 using VirtualBoz and on IPA server i have two interfaces enp0s3 and enp0s8. I added enp0s3 (interface facing Internet) part of Public Zone and enp0s8 is part of DMZ zone with IP Addr = 10.8.8.2

    And on Second server i have one interface enp0s3(Host Only Network) with IP Addr = 10.8.8.50 part of DMZ Zone and made 10.8.8.2 gw of this second server

    I enabled masquerading on public zone of IPA Server . But when i try to ping ipa server i get “ping: unknown host ipa” same case if i try to ping http://www.google.com “ping: unknown host http://www.google.com

    If i revert back all changes and make enp0s3 and enp0s8 part of public zone on IPA server it start working but stops working as soon i make enp0s8 part of DMZ zone. Any suggestions

    • The error message suggests that your DNS resolution may not be working properly.

      Try accessing the IPA server on the IP address and see if that works.

  17. From firewall-cmd on IPA server i added following services and ports in both PUBLIC and DMZ Zones
    dmz (active)
    interfaces: enp0s8
    sources:
    services: ssh
    ports: 443/tcp 80/tcp 88/udp 464/udp 88/tcp 123/udp 389/tcp 53/tcp 53/udp 636/tcp
    masquerade: no ——————————————— (and yes on Public Zone)
    forward-ports:
    icmp-blocks:
    rich rules:

    It started working, i was able to ping ipa again using name, but when i added the gw on Serv1(10.8.8.50) IP addr of IPA server 10.8.8.2. it stopped working

    On Serv1(10.8.8.50) default zone is still public ,

    • It seems the issue was on Serv1(10.8.8.50) Firewall Zone. Previously default zone was set to Public then i change the default to DMZ it started working. My guess is both zone on IPA and Client should be same zone depending on port and gateway ,especially gateway because 10.8.8.2 enp0s8 is part of dmz zone and not public

  18. Thanks for the good information. Do you have any experience with configuring this same setup in an AWS cloud environment where only 1 interface is required? I’ve tried to sort it out but most of the rules you created in the final steps refer to passing the protocol from 1 interface to the other.

    • My AWS setup is a bit different thanks to NAT gateways. I used instances with 2 interfaces some years ago, nowadays I create private and public subnets, and use a NAT gateway for instances in the private one.

  19. Hi, having some issues when I try to add forward-port to a network address,
    –add-forward-port=port=[-]:proto=[:toport=[-]][:toaddr=[/]]
    In the manual, is possible to add network mask, but when I try to add they give an error saying is invalid
    firewall-cmd –add-forward-port=port=23:proto=tcp:toport=80:toaddr=192.168.1.0/24
    Error: INVALID_ADDR: 192.168.1.0/24

    Do you have any idea? the forward port just work when is a host(192.168.1.10)?

    • You usually forward a port from one host to a port on another host. If you want to specify a network mask, you can add /32.

  20. Hi all,

    Will you please help me out for the below issue,
    I want to the run the below command in centos 7 firewall

    iptables command
    -A INPUT -i eth0 -p udp -m state –state ESTABLISH,RELATED -m udp -d 1.2.3.4 -j ACCEPT

  21. Hello, I have some confusions on the “reject” part of the following rich rules:

    rule service name=ssh log prefix=”SSH_” level=”debug” limit value=1/m reject

    1. What does above rich rules means?
    2. What is the purpose of specifying reject here?
    3. Is it rejecting the ssh services debug messages that started with “SSH_” prefix?
    4. Can you please clarify when to use reject or accept?

    Thanks

    • This rich rule means that new connections to service SSH are all rejected and logged at a rate of 1 per minute. The log prefix is set to SSH_ and the log level is debug.

      Use reject when you want to deny connections, and use accept when you want to allow connections.

  22. hi i have the same confusion as the above comments from Kamrul Ahsan:
    rule service name=ssh log prefix=”SSH_” level=”debug” limit value=1/m reject

    my confusion is because in the questions it doesn’t say accept or reject, so why we chose the reject ?
    the question says only “configure server2 for firewalld SSH logging with a prefix of “SSH_” and a debug level, limit to 2 log entries per minute”

    could you please advise ?
    thanks in advance

    • In my example I use a forwarding rule for SSH and then reject all other SSH traffic while also logging it. If the question doesn’t ask you to reject traffic then you don’t have to reject it. I hope this clarifies things for you.

  23. Hi
    I have a server with two interfaces. One to the internal network, the other to the external network. Internal and external zones are defined on the interfaces, respectively. in this configuration, traffic from the internal network goes to the external network.

    external (active)
    target: default
    icmp-block-inversion: no
    interfaces: ens192
    sources:
    services: ssh
    ports:
    protocols:
    masquerade: yes
    forward-ports:
    source-ports:
    icmp-blocks:
    rich rules:

    internal (active)
    target: default
    icmp-block-inversion: no
    interfaces: ens224
    sources:
    services: dhcpv6-client mdns samba-client ssh
    ports:
    protocols:
    masquerade: no
    forward-ports:
    source-ports:
    icmp-blocks:
    rich rules:

    If I change the zone on the internal interface to drop, and prescribe rich rules for individual hosts. As a result, the traffic from host 192.168.100.50 does not go away.

    rop (active)
    target: DROP
    icmp-block-inversion: no
    interfaces: ens224
    sources:
    services: http https
    ports:
    protocols:
    masquerade: no
    forward-ports:
    source-ports:
    icmp-blocks:
    rich rules:
    rule family=”ipv4″ source address=”192.168.100.50″ accept

    Chain INPUT (policy ACCEPT)
    target prot opt source destination
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0
    INPUT_direct all — 0.0.0.0/0 0.0.0.0/0
    INPUT_ZONES_SOURCE all — 0.0.0.0/0 0.0.0.0/0
    INPUT_ZONES all — 0.0.0.0/0 0.0.0.0/0
    LOG all — 0.0.0.0/0 0.0.0.0/0 ctstate INVALID LOG flags 0 level 4 prefix “STATE_INVALID_DROP: ”
    DROP all — 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
    LOG all — 0.0.0.0/0 0.0.0.0/0 LOG flags 0 level 4 prefix “FINAL_REJECT: ”
    REJECT all — 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited

    Chain FORWARD (policy ACCEPT)
    target prot opt source destination
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0
    FORWARD_direct all — 0.0.0.0/0 0.0.0.0/0
    FORWARD_IN_ZONES_SOURCE all — 0.0.0.0/0 0.0.0.0/0
    FORWARD_IN_ZONES all — 0.0.0.0/0 0.0.0.0/0
    FORWARD_OUT_ZONES_SOURCE all — 0.0.0.0/0 0.0.0.0/0
    FORWARD_OUT_ZONES all — 0.0.0.0/0 0.0.0.0/0
    LOG all — 0.0.0.0/0 0.0.0.0/0 ctstate INVALID LOG flags 0 level 4 prefix “STATE_INVALID_DROP: ”
    DROP all — 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
    LOG all — 0.0.0.0/0 0.0.0.0/0 LOG flags 0 level 4 prefix “FINAL_REJECT: ”
    REJECT all — 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited

    Chain OUTPUT (policy ACCEPT)
    target prot opt source destination
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0
    OUTPUT_direct all — 0.0.0.0/0 0.0.0.0/0

    Chain FORWARD_IN_ZONES (1 references)
    target prot opt source destination
    FWDI_external all — 0.0.0.0/0 0.0.0.0/0 [goto]
    FWDI_drop all — 0.0.0.0/0 0.0.0.0/0
    FWDI_public all — 0.0.0.0/0 0.0.0.0/0 [goto]

    Chain FORWARD_IN_ZONES_SOURCE (1 references)
    target prot opt source destination

    Chain FORWARD_OUT_ZONES (1 references)
    target prot opt source destination
    FWDO_external all — 0.0.0.0/0 0.0.0.0/0 [goto]
    FWDO_drop all — 0.0.0.0/0 0.0.0.0/0
    FWDO_public all — 0.0.0.0/0 0.0.0.0/0 [goto]

    Chain FORWARD_OUT_ZONES_SOURCE (1 references)
    target prot opt source destination

    Chain FORWARD_direct (1 references)
    target prot opt source destination

    Chain FWDI_drop (1 references)
    target prot opt source destination
    FWDI_drop_log all — 0.0.0.0/0 0.0.0.0/0
    FWDI_drop_deny all — 0.0.0.0/0 0.0.0.0/0
    FWDI_drop_allow all — 0.0.0.0/0 0.0.0.0/0
    LOG all — 0.0.0.0/0 0.0.0.0/0 LOG flags 0 level 4 prefix “FWDI_drop_DROP: ”
    DROP all — 0.0.0.0/0 0.0.0.0/0

    Chain FWDI_drop_allow (1 references)
    target prot opt source destination

    Chain FWDI_drop_deny (1 references)
    target prot opt source destination

    Chain FWDI_drop_log (1 references)
    target prot opt source destination

    Chain FWDI_external (1 references)
    target prot opt source destination
    FWDI_external_log all — 0.0.0.0/0 0.0.0.0/0
    FWDI_external_deny all — 0.0.0.0/0 0.0.0.0/0
    FWDI_external_allow all — 0.0.0.0/0 0.0.0.0/0
    ACCEPT icmp — 0.0.0.0/0 0.0.0.0/0

    Chain FWDI_external_allow (1 references)
    target prot opt source destination

    Chain FWDI_external_deny (1 references)
    target prot opt source destination

    Chain FWDI_external_log (1 references)
    target prot opt source destination

    Chain FWDI_public (1 references)
    target prot opt source destination
    FWDI_public_log all — 0.0.0.0/0 0.0.0.0/0
    FWDI_public_deny all — 0.0.0.0/0 0.0.0.0/0
    FWDI_public_allow all — 0.0.0.0/0 0.0.0.0/0
    ACCEPT icmp — 0.0.0.0/0 0.0.0.0/0

    Chain FWDI_public_allow (1 references)
    target prot opt source destination

    Chain FWDI_public_deny (1 references)
    target prot opt source destination

    Chain FWDI_public_log (1 references)
    target prot opt source destination

    Chain FWDO_drop (1 references)
    target prot opt source destination
    FWDO_drop_log all — 0.0.0.0/0 0.0.0.0/0
    FWDO_drop_deny all — 0.0.0.0/0 0.0.0.0/0
    FWDO_drop_allow all — 0.0.0.0/0 0.0.0.0/0
    LOG all — 0.0.0.0/0 0.0.0.0/0 LOG flags 0 level 4 prefix “FWDO_drop_DROP: ”
    DROP all — 0.0.0.0/0 0.0.0.0/0

    Chain FWDO_drop_allow (1 references)
    target prot opt source destination

    Chain FWDO_drop_deny (1 references)
    target prot opt source destination

    Chain FWDO_drop_log (1 references)
    target prot opt source destination

    Chain FWDO_external (1 references)
    target prot opt source destination
    FWDO_external_log all — 0.0.0.0/0 0.0.0.0/0
    FWDO_external_deny all — 0.0.0.0/0 0.0.0.0/0
    FWDO_external_allow all — 0.0.0.0/0 0.0.0.0/0

    Chain FWDO_external_allow (1 references)
    target prot opt source destination
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0 ctstate NEW,UNTRACKED
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0 ctstate NEW,UNTRACKED

    Chain FWDO_external_deny (1 references)
    target prot opt source destination

    Chain FWDO_external_log (1 references)
    target prot opt source destination

    Chain FWDO_public (1 references)
    target prot opt source destination
    FWDO_public_log all — 0.0.0.0/0 0.0.0.0/0
    FWDO_public_deny all — 0.0.0.0/0 0.0.0.0/0
    FWDO_public_allow all — 0.0.0.0/0 0.0.0.0/0

    Chain FWDO_public_allow (1 references)
    target prot opt source destination

    Chain FWDO_public_deny (1 references)
    target prot opt source destination

    Chain FWDO_public_log (1 references)
    target prot opt source destination

    Chain INPUT_ZONES (1 references)
    target prot opt source destination
    IN_external all — 0.0.0.0/0 0.0.0.0/0 [goto]
    IN_drop all — 0.0.0.0/0 0.0.0.0/0
    IN_public all — 0.0.0.0/0 0.0.0.0/0 [goto]

    Chain INPUT_ZONES_SOURCE (1 references)
    target prot opt source destination

    Chain INPUT_direct (1 references)
    target prot opt source destination

    Chain IN_drop (1 references)
    target prot opt source destination
    IN_drop_log all — 0.0.0.0/0 0.0.0.0/0
    IN_drop_deny all — 0.0.0.0/0 0.0.0.0/0
    IN_drop_allow all — 0.0.0.0/0 0.0.0.0/0
    LOG all — 0.0.0.0/0 0.0.0.0/0 LOG flags 0 level 4 prefix “IN_drop_DROP: ”
    DROP all — 0.0.0.0/0 0.0.0.0/0

    Chain IN_drop_allow (1 references)
    target prot opt source destination
    ACCEPT all — 192.168.100.50 0.0.0.0/0
    ACCEPT tcp — 0.0.0.0/0 0.0.0.0/0 tcp dpt:80 ctstate NEW,UNTRACKED
    ACCEPT tcp — 0.0.0.0/0 0.0.0.0/0 tcp dpt:443 ctstate NEW,UNTRACKED

    Chain IN_drop_deny (1 references)
    target prot opt source destination

    Chain IN_drop_log (1 references)
    target prot opt source destination

    Chain IN_external (1 references)
    target prot opt source destination
    IN_external_log all — 0.0.0.0/0 0.0.0.0/0
    IN_external_deny all — 0.0.0.0/0 0.0.0.0/0
    IN_external_allow all — 0.0.0.0/0 0.0.0.0/0
    ACCEPT icmp — 0.0.0.0/0 0.0.0.0/0

    Chain IN_external_allow (1 references)
    target prot opt source destination
    ACCEPT tcp — 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 ctstate NEW,UNTRACKED

    Chain IN_external_deny (1 references)
    target prot opt source destination

    Chain IN_external_log (1 references)
    target prot opt source destination

    Chain IN_public (1 references)
    target prot opt source destination
    IN_public_log all — 0.0.0.0/0 0.0.0.0/0
    IN_public_deny all — 0.0.0.0/0 0.0.0.0/0
    IN_public_allow all — 0.0.0.0/0 0.0.0.0/0
    ACCEPT icmp — 0.0.0.0/0 0.0.0.0/0

    Chain IN_public_allow (1 references)
    target prot opt source destination
    ACCEPT tcp — 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 ctstate NEW,UNTRACKED

    Chain IN_public_deny (1 references)
    target prot opt source destination

    Chain IN_public_log (1 references)
    target prot opt source destination

    Chain OUTPUT_direct (1 references)
    target prot opt source destination

    Works “FWDO_drop_DROP: ”
    Tell me what the problem is?

    • Could that be because you’re dropping them? When you check your iptables configuration, can you trace the rule that drops traffic? You can use the hit counter.

  24. One part of your tutorial seems contradictory and it’s confusing. I’m with you up until the point where you say:
    “When masquerading on the zone public is enabled, we can access the site:”

    I started with a fresh firewalld install with no rules and I run a script to execute the following:

    # assign interfaces to zones
    firewall-cmd –add-interface=${DMZ_INTERFACE} –zone=dmz
    firewall-cmd –add-interface=${PUBLIC_INTERFACE} –zone=public

    # I have a DHCP & DNS server running on this box listening on ${DMZ_INTERFACE}
    firewall-cmd –add-service=dhcp –zone=dmz
    firewall-cmd –add-service=dns –zone=dmz

    Now at this point if I try to ping http://www.google.com from a box in my dmz I get “no route to host” (just as you said). But if I then add 1 more rule to my firewall:

    firewall-cmd –zone=public –add-masquerade

    Now at this point in your tutorial you say “When masquerading on the zone public is enabled, we can access the site:” showing your server in your dmz successfully downloading a file. BUT when I try and ping http://www.google.com from inside my dmz I get “request timed out”. I cannot get anything through to external servers until I add a direct rule:

    firewall-cmd –direct –add-rule ipv4 nat POSTROUTING 0 -o ${PUBLIC_INTERFACE} -j MASQUERADE

    And indeed (later on in the tutorial) where you introduce that rule you say “To allow our dmz (enp0s17) network VMs with private IP addresses to communicate with external networks, we have to configure firewall for IP masquerading:”

    BUT that contradicts your earlier example where you downloaded a file from within your DMZ without adding this rule!

    Also, right after you introduce that last direct rule for masquerading you introduce a bunch of direct rules to enable forwarding of dmz traffic to public hosts for http, dns, ssh, etc. But once I added that masquerading direct rule I can do all those things without adding all those protocol-specific rules… so what are they used for?

    FINAL QUESTION:

    As you pointed out, the firewalld man page says: “Direct configuration should be used only as a last resort when it’s not possible to use firewalld.zone”

    So is there a way to enable masquerading without adding a direct rule?

    Thanks for your help!

    • Hi Mark, thanks for your detailed comment, I appreaciate it.

      To be honest with you, I wrote this article several years ago, there is a chance that I made a mistake at the time. There is obviously a way of enabling masquerading without adding a direct rule, it should work as per instructions. I’m sure I’ve done that many times, unfortunatelly I don’t really use firewalld nowadays.

  25. Hi,
    In my setup i have this openvpn server:
    etho (public) 10.1.1.230
    tun0 (trusted) 10.8.0.1

    clients connected to openvpn with 10.8.0.X can connect to public network 10.1.1.X normally, but clients connected to 10.1.1.X can’t.

    How to enable this nat/forwarding?

    firewall-cmd –get-active-zones
    public
    interfaces: eth0
    trusted
    sources: 10.8.0.0/24

    iptables -nL
    Chain INPUT (policy ACCEPT)
    target prot opt source destination
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0
    INPUT_direct all — 0.0.0.0/0 0.0.0.0/0
    INPUT_ZONES_SOURCE all — 0.0.0.0/0 0.0.0.0/0
    INPUT_ZONES all — 0.0.0.0/0 0.0.0.0/0
    DROP all — 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
    REJECT all — 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited

    Chain FORWARD (policy ACCEPT)
    target prot opt source destination
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0
    FORWARD_direct all — 0.0.0.0/0 0.0.0.0/0
    FORWARD_IN_ZONES_SOURCE all — 0.0.0.0/0 0.0.0.0/0
    FORWARD_IN_ZONES all — 0.0.0.0/0 0.0.0.0/0
    FORWARD_OUT_ZONES_SOURCE all — 0.0.0.0/0 0.0.0.0/0
    FORWARD_OUT_ZONES all — 0.0.0.0/0 0.0.0.0/0
    DROP all — 0.0.0.0/0 0.0.0.0/0 ctstate INVALID
    REJECT all — 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited

    Chain OUTPUT (policy ACCEPT)
    target prot opt source destination
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0
    OUTPUT_direct all — 0.0.0.0/0 0.0.0.0/0

    Chain FORWARD_IN_ZONES (1 references)
    target prot opt source destination
    FWDI_public all — 0.0.0.0/0 0.0.0.0/0 [goto]
    FWDI_public all — 0.0.0.0/0 0.0.0.0/0 [goto]

    Chain FORWARD_IN_ZONES_SOURCE (1 references)
    target prot opt source destination
    FWDI_trusted all — 10.8.0.0/24 0.0.0.0/0

    Chain FORWARD_OUT_ZONES (1 references)
    target prot opt source destination
    FWDO_public all — 0.0.0.0/0 0.0.0.0/0 [goto]
    FWDO_public all — 0.0.0.0/0 0.0.0.0/0 [goto]

    Chain FORWARD_OUT_ZONES_SOURCE (1 references)
    target prot opt source destination
    FWDO_trusted all — 0.0.0.0/0 10.8.0.0/24

    Chain FORWARD_direct (1 references)
    target prot opt source destination

    Chain FWDI_public (2 references)
    target prot opt source destination
    FWDI_public_log all — 0.0.0.0/0 0.0.0.0/0
    FWDI_public_deny all — 0.0.0.0/0 0.0.0.0/0
    FWDI_public_allow all — 0.0.0.0/0 0.0.0.0/0
    ACCEPT icmp — 0.0.0.0/0 0.0.0.0/0

    Chain FWDI_public_allow (1 references)
    target prot opt source destination

    Chain FWDI_public_deny (1 references)
    target prot opt source destination

    Chain FWDI_public_log (1 references)
    target prot opt source destination

    Chain FWDI_trusted (1 references)
    target prot opt source destination
    FWDI_trusted_log all — 0.0.0.0/0 0.0.0.0/0
    FWDI_trusted_deny all — 0.0.0.0/0 0.0.0.0/0
    FWDI_trusted_allow all — 0.0.0.0/0 0.0.0.0/0
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0

    Chain FWDI_trusted_allow (1 references)
    target prot opt source destination

    Chain FWDI_trusted_deny (1 references)
    target prot opt source destination

    Chain FWDI_trusted_log (1 references)
    target prot opt source destination

    Chain FWDO_public (2 references)
    target prot opt source destination
    FWDO_public_log all — 0.0.0.0/0 0.0.0.0/0
    FWDO_public_deny all — 0.0.0.0/0 0.0.0.0/0
    FWDO_public_allow all — 0.0.0.0/0 0.0.0.0/0

    Chain FWDO_public_allow (1 references)
    target prot opt source destination

    Chain FWDO_public_deny (1 references)
    target prot opt source destination

    Chain FWDO_public_log (1 references)
    target prot opt source destination

    Chain FWDO_trusted (1 references)
    target prot opt source destination
    FWDO_trusted_log all — 0.0.0.0/0 0.0.0.0/0
    FWDO_trusted_deny all — 0.0.0.0/0 0.0.0.0/0
    FWDO_trusted_allow all — 0.0.0.0/0 0.0.0.0/0
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0

    Chain FWDO_trusted_allow (1 references)
    target prot opt source destination

    Chain FWDO_trusted_deny (1 references)
    target prot opt source destination

    Chain FWDO_trusted_log (1 references)
    target prot opt source destination

    Chain INPUT_ZONES (1 references)
    target prot opt source destination
    IN_public all — 0.0.0.0/0 0.0.0.0/0 [goto]
    IN_public all — 0.0.0.0/0 0.0.0.0/0 [goto]

    Chain INPUT_ZONES_SOURCE (1 references)
    target prot opt source destination
    IN_trusted all — 10.8.0.0/24 0.0.0.0/0

    Chain INPUT_direct (1 references)
    target prot opt source destination

    Chain IN_public (2 references)
    target prot opt source destination
    IN_public_log all — 0.0.0.0/0 0.0.0.0/0
    IN_public_deny all — 0.0.0.0/0 0.0.0.0/0
    IN_public_allow all — 0.0.0.0/0 0.0.0.0/0
    ACCEPT icmp — 0.0.0.0/0 0.0.0.0/0

    Chain IN_public_allow (1 references)
    target prot opt source destination
    ACCEPT tcp — 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 ctstate NEW,UNTRACKED
    ACCEPT udp — 0.0.0.0/0 0.0.0.0/0 udp dpt:1150 ctstate NEW,UNTRACKED

    Chain IN_public_deny (1 references)
    target prot opt source destination

    Chain IN_public_log (1 references)
    target prot opt source destination

    Chain IN_trusted (1 references)
    target prot opt source destination
    IN_trusted_log all — 0.0.0.0/0 0.0.0.0/0
    IN_trusted_deny all — 0.0.0.0/0 0.0.0.0/0
    IN_trusted_allow all — 0.0.0.0/0 0.0.0.0/0
    ACCEPT all — 0.0.0.0/0 0.0.0.0/0

    Chain IN_trusted_allow (1 references)
    target prot opt source destination

    Chain IN_trusted_deny (1 references)
    target prot opt source destination

    Chain IN_trusted_log (1 references)
    target prot opt source destination

    Chain OUTPUT_direct (1 references)
    target prot opt source destination

    Thank you,
    Wilson

Leave a Reply to Vinicius S Cancel reply

Your email address will not be published. Required fields are marked *