Linux Firewall Management

Cloud Journey
9 min readNov 28, 2021

Overview

In this blog post, we will do a lab in a RedHat Linux environment, including managing inbound and outbound traffic using Linux firewall and learning about firewalld, zones, services, rich rules, rich rule priority and direct rule.

Lab Environment

Setup

  • Create one RedHat EC2 instance
  • Use the following user data to install SSM agent in RedHat.
#!/bin/bash
sudo dnf install -y https://s3.us-east-2.amazonaws.com/amazon-ssm-us-east-2/latest/linux_amd64/amazon-ssm-agent.rpm
sudo systemctl enable amazon-ssm-agent
sudo systemctl start amazon-ssm-agent
  • Create one Ubuntu EC2 instance
  • Install firewalld in RedHat
sudo yum install firewalld -y
sudo systemctl start firewalld
sudo systemctl status firewalld
Loaded: loaded (/usr/lib/systemd/system/firewalld.service; enabled; vendor preset: enabled)
#once show enable, it will automatically start when reboot, disable it when do experiment to avoid lockout

Validation

We will validate connectivity between these two EC2 instances first.

Logon to EC2 via AWS session manager, from RedHat EC2 ping Ubuntu EC2:

[root@ip-172-31-18-61 zones]# ping ip-172-31-23-98 -c 4
PING ip-172-31-23-98.us-east-2.compute.internal (172.31.23.98) 56(84) bytes of data.
64 bytes from ip-172-31-23-98.us-east-2.compute.internal (172.31.23.98): icmp_seq=1 ttl=64 time=0.410 ms

From Ubuntu ping RedHat:

$ ping ip-172-31-18-61
PING ip-172-31-18-61.us-east-2.compute.internal (172.31.18.61) 56(84) bytes of data.
64 bytes from ip-172-31-18-61.us-east-2.compute.internal (172.31.18.61): icmp_seq=1 ttl=64 time=0.426 ms

Manage Inbound Traffic

firewalld uses the concepts of zones and services, that simplify the traffic management. Zones are predefined sets of rules. Network interfaces and sources can be assigned to a zone. Firewall services are predefined rules that cover all necessary settings to allow incoming traffic for a specific service and they apply within a zone.

Use Zone Target to Block Traffic

Configure default zone to work:

[root@ip-172-31-18-61 zones]# firewall-cmd --set-default-zone work
success

[root@ip-172-31-18-61 zones]# firewall-cmd --get-default-zone
work
[root@ip-172-31-18-61 zones]# firewall-cmd --get-active-zone
work
interfaces: eth0
<# Active zones are zones, that have a binding to an interface or source, assign active zone to an interface #>
~]# firewall-cmd --zone=zone-name --change-interface=<interface-name>
~]# firewall-cmd --get-active-zones

Update target of the zone to REJECT: (if you need to block all)

firewall-cmd --permanent --zone=work --set-target=REJECT
firewall-cmd --reload

From Ubuntu EC2 ping RedHat EC2, the result is 100% packet loss:

root@ip-172–31–23–98:~# ping ip-172–31–18–61
PING ip-172–31–18–61.us-east-2.compute.internal (172.31.18.61) 56(84) bytes of data.
From ip-172–31–18–61.us-east-2.compute.internal (172.31.18.61) icmp_seq=1 Packet filtered
....
9 packets transmitted, 0 received, +9 errors, 100% packet loss, time 8172ms

Use Zone Services to Allow Http Traffic

(if you need to deny anything else, but allow certain services, add the service to the allow list of firewalld)

By default http is denied by local firewall, from Ubuntu EC2, we are not able to open Apache home page which is hosted in RedHat EC2.

$ curl http://ip-172-31-18-61.us-east-2.compute.internal
curl: (7) Failed to connect to ip-172-31-18-61.us-east-2.compute.internal port 80: No route to host

http service is one of the predefined services, configuration can be found from /usr/lib/firewalld/services/http.xml.

Let’s add http service to allowed list.

#permanent means the rule stays even after reboot
$firewall-cmd --add-service=http --zone=work --permanent
$firewall-cmd --add-service=https --zone=work --permanent
$firewall-cmd --reload$firewall-cmd --list-servicescockpit dhcpv6-client http ssh

Now we are able to open Apache home page from Ubuntu EC2:

$ curl http://172.31.18.61
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
<head>
<title>Test Page for the HTTP Server on Red Hat Enterprise Linux</title>

Use Zone Rich Rule to Deny Traffic

#firewall-cmd --permanent --add-rich-rule="rule family='ipv4' priority=-2000 source address='172.31.23.98' reject"#firewall-cmd --reload# firewall-cmd --list-rich-rules
rule family="ipv4" source address="172.31.23.98" reject

We are not able to open Apache home page and ping is also not working.

$ ping 172.31.18.61
PING 172.31.18.61 (172.31.18.61) 56(84) bytes of data.
From 172.31.18.61 icmp_seq=1 Destination Port Unreachable
$ curl http://172.31.18.61
curl: (7) Failed to connect to 172.31.18.61 port 80: Connection refused

Zone Rich Rule Priority

Let’s deny all traffic from Ubuntu EC2, but allow ping and ssh.

#firewall-cmd  --add-rich-rule="rule family='ipv4' priority='-2010' source address='172.31.23.98' protocol value='icmp' accept"  --permanent#firewall-cmd --add-rich-rule="rule family='ipv4' priority=-2020 source address='172.31.23.98' port  port='22' protocol='tcp' accept" --permanent#firewall-cmd --reload[root@ip-172-31-18-61 services]# firewall-cmd  --list-all
work (active)
target: %%REJECT%%
icmp-block-inversion: no
interfaces: eth0
sources:
services: cockpit dhcpv6-client http ssh
ports:
protocols:
forward: no
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
rule priority="-2020" family="ipv4" source address="172.31.23.98" port port="22" protocol="tcp" accept
rule priority="-2010" family="ipv4" source address="172.31.23.98" protocol value="icmp" accept
rule priority="-2000" family="ipv4" source address="172.31.23.98" reject

We are able to ping the RedHat EC2 from Ubuntu EC2, and http traffic is still blocked.

root@ip-172-31-23-98:~# ping 172.31.18.61 -c 4
PING 172.31.18.61 (172.31.18.61) 56(84) bytes of data.
64 bytes from 172.31.18.61: icmp_seq=1 ttl=64 time=0.415 ms
64 bytes from 172.31.18.61: icmp_seq=2 ttl=64 time=0.511 ms
64 bytes from 172.31.18.61: icmp_seq=3 ttl=64 time=0.494 ms
64 bytes from 172.31.18.61: icmp_seq=4 ttl=64 time=0.472 ms
--- 172.31.18.61 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3073ms
rtt min/avg/max/mdev = 0.415/0.473/0.511/0.036 ms
root@ip-172-31-23-98:~# curl http://172.31.18.61
curl: (7) Failed to connect to 172.31.18.61 port 80: Connection refused

Understanding Priority

This can be any number between -32768 and 32767, where lower numbers have higher precedence. Based on the priority rules are organized into different chains. If they have the same priority value, then it’s undefined in what order they will be executed.

  • If priority < 0, the rule goes into a chain with the suffix _pre.
  • If priority > 0, the rule goes into a chain with the suffix _post.
  • _pre can occur before normal log rules.
  • _post execution always occurs after firewalld’s other primitives (services, ports, etc). This makes it a good place for catch-all type rules.

Manage Outbound Traffic

firewalld provides zone-based rules, controlling the firewalld Services entering a Zone. When outgoing filtering is required, use of direct rules is required, as firewalld is only concerned with filtering traffic coming into zones.

firewalld provides "direct rules" for administrators who understand the iptables syntax and wish to directly manipulate the underlying iptables interface.

Use firewalld direct rules in the firewalld OUTPUT chain. Direct rules use the iptables syntax. (see also: man firewalld, man firewall-cmd, man firewalld.direct)

Outbound to Same Subnet Only

The idea is to reject all except intra-subnet traffic. We will add two direct rules, one is to allow outbound to subnet 172.31.16.0/20, one is to drop all.

Tips: to avoid lock out, add the deny rule as runtime first and reboot EC2 instance to restore the permanent rule when required. After test when everything works as expected, make the rule as permanent.

firewall-cmd direct rule general syntax:

[ — permanent] — direct — add-rule { ipv4 | ipv6 | eb } table chain priority args

Run following specific commands in Redhat EC2.

# firewall-cmd --direct --add-rule ipv4 filter OUTPUT 1 -d 172.31.16.0/20 -j ACCEPT# firewall-cmd --direct --add-rule ipv4 filter OUTPUT 9 -j DROP

Validation

We lost the connectivity to Redhat EC2 through AWS SSM, that’s expected, since SSM agent requires outbound connectivity to couple of SSM endpoints.

But we are able to ssh to Redhat EC2 from Ubuntu EC2. This is expected as well, since we allow inbound ssh service, and allow all outbound traffic to the subnet 172.31.16.0/20.

root@ip-172-31-23-98:~# ssh ec2-user@172.31.18.61 -i webserverkey.pem
Last login: Sat Nov 27 22:33:54 2021 from 172.31.23.98
[ec2-user@ip-172-31-18-61 ~]$

Add Permanent Rules

# firewall-cmd --reload# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 1 -d 172.31.16.0/20 -j ACCEPT# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 9 -j DROP# firewall-cmd --permanent --direct --get-all-rules
ipv4 filter OUTPUT 1 -d 172.31.16.0/20 -j ACCEPT
ipv4 filter OUTPUT 9 -j DROP
# firewall-cmd --direct --get-all-rules
#
# firewall-cmd --reload

# firewall-cmd --direct --get-all-rules
ipv4 filter OUTPUT 1 -d 172.31.16.0/20 -j ACCEPT
ipv4 filter OUTPUT 9 -j DROP

More About Rich Language Rule Syntax

firewall-cmd [--permanent] [--zone=zone] [--permanent] [--policy=policy] --add-rich-rule='rule' [--timeout=timeval]

General rule structure

rule
[source]
[destination]
service|port|protocol|icmp-block|icmp-type|masquerade|forward-port|source-port
[log]
[audit]
[accept|reject|drop|mark]

What Sources?

source [not] address="address[/mask]"|mac="mac-address"|ipset="ipset"

With the source address the origin of a connection attempt can be limited to the source address. An address is either a single IP address, or a network IP address, a MAC address or an IPSet.

It is possible to invert the sense of an address by adding not before address. All but the specified address will match then.

IPSET

IP sets can be used in firewalld zones as sources and also as sources in rich rules. In Red Hat Enterprise Linux, the preferred method is to use the IP sets created with firewalld in a direct rule.

Create ipset and add ip address to it:

~]# firewall-cmd --permanent --new-ipset=test --type=hash:net
success
~]# firewall-cmd --permanent --ipset=test --add-entry=192.168.0.1
success
# adding list of IPs, including CIDR
# firewall-cmd --permanent --ipset=test --add-entries-from-file=iplist.txt
# list entries from ipset
# firewall-cmd --permanent --ipset=test --get-entries
# list all ipsets
#firewall-cmd --permanent --get-ipsets

References

https://firewalld.org/2018/12/rich-rule-priorities

https://firewalld.org/documentation/man-pages/firewalld.richlanguage.html

https://forums.aws.amazon.com/thread.jspa?threadID=270976

How to filter outbound or outgoing network traffic using firewall-cmd? — Red Hat Customer Portal

iptables command in Linux with Examples — GeeksforGeeks

Cheat Sheet: (-m means match rule, -j means target)

# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 0 -p tcp -m tcp --dport=22 -j ACCEPT
success
# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 1 -p tcp -m tcp --sport=22 -j ACCEPT
success
# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 9 -j DROP
success
# firewall-cmd --permanent --direct --get-all-rules
ipv4 filter OUTPUT 0 -p tcp -m tcp --dport=22 -j ACCEPT
ipv4 filter OUTPUT 1 -p tcp -m tcp --sport=22 -j ACCEPT
ipv4 filter OUTPUT 9 -j DROP
# firewall-cmd --direct --get-all-rules
#
# firewall-cmd --reload
success

# firewall-cmd --direct --get-all-rules
ipv4 filter OUTPUT 0 -p tcp -m tcp --dport=22 -j ACCEPT
ipv4 filter OUTPUT 1 -p tcp -m tcp --sport=22 -j ACCEPT
ipv4 filter OUTPUT 9 -j DROP

ipset:
https://access.redhat.com/discussions/5784261
https://ipset.netfilter.org/features.html
https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html-single/securing_networks#setting-and-controlling-ip-sets-using-firewalld_using-and-configuring-firewalld

--

--

Cloud Journey

All blogs are strictly personal and do not reflect the views of my employer. https://github.com/Ronnie-personal