Firewalld - Allowing only the HTTP Traffic from a subnet such as the servers from Cloudflare

About

This article is a set by step that shows how to allow only network packet from a certain subnet to reach your web server with firewalld.

The subnet taken in this example is the subnet of Cloudflare

firewalld is based on iptable and therefore the same concept such as zone, a service and rule applied also for firewalld.

To filter by subnet, you need to:

  • create an ipset which is a set of ip.
  • and apply your rule on this ip set.

This article gives your a detailed step by step on how to do it.

With the same method, you can also filter by country

Steps

Create your file

Each set of ip has a storage format called the type that defines :

  • the type of input file used to define the set.
  • the storage type and therefore the lookup time

You can't change this format once the ip set is created.

The file format for a hash:net type is:

ip
ip[/cidr]
fromaddr-toaddr

Luckily, Cloudflare is providing this files at Cloudflare IPS:

Create a set of ip

The below commands create two ipset name of the type hash:net named:

  • cloudflare-ipv4 for ipv4
  • cloudflare-ipv6 for ipv6

Create the set for ipv4 address:

firewall-cmd \
    --new-ipset=cloudflare-ipv4 \
    --type=hash:net \
    --permanent \
    --option=family=inet  

where:

  • the option can be found in ipset documentation section of the type (in our case hash:ip)
    • family is the protocol family of the IP addresses to be stored in the set (default: inet = ipv4)
    • hashsize is the initial hash size for the set (default to 1024)
    • maxelem is the maximal number of element (default to 65536)

This command creates the set for Ipv6 address.

firewall-cmd \
    --new-ipset=cloudflare-ipv6 \
    --type=hash:net \
    --permanent \
    --option=family=inet6

The command should return:

success

Load the file into the ipset

Load Ipv4

  • For IPV4, we get first the ipv4 file
cd /tmp
wget https://www.cloudflare.com/ips-v4
  • Then load it into the ipset
firewall-cmd \
    --ipset=cloudflare-ipv4 \
    --permanent \
    --add-entries-from-file=ips-v4

Load Ipv6

  • For IPV6, we get first the ipv6 file
wget https://www.cloudflare.com/ips-v6
  • Then load it into the ipset
firewall-cmd \
    --ipset=cloudflare-ipv6 \
    --permanent \
    --add-entries-from-file=ips-v6

Check the load

Reload to load the ipset in memory and therefore make it visible to the ipset command

systemctl reload firewalld

Check that the ipset is not full

ipset -t list cloudflare-ipv4
Name: cloudflare-ipv4
Type: hash:net
Revision: 6
Header: family inet hashsize 1024 maxelem 65536
Size in memory: 1272
References: 0
Number of entries: 14

Their is one line by entry (ie 14)

While with a hash:ip, you're list would be much bigger because the record is not at the net level but at the ip. It creates therefore all IP for each subnet.

Test the IP Set

You can test your IP.

  • A Canada IP should not be in the set.
ipset test cloudflare-ipv4 167.114.98.233
167.114.98.233 is NOT in set cloudflare-ipv4.
  • A Cloudflare IP should be in the set.
ipset test cloudflare-ipv4 173.245.48.0
Warning: 173.245.48.0 is in set cloudflare-ipv4.

Add a rule to drop all packets that does not come from the IPSet

Drop non Cloudflare HTTPS

Adding a rule that drops all packet for the https Firewalld (port 443) if they are not from Cloudflare

  • Ipv4 - Https
firewall-cmd \
    --zone=public \
    --permanent \
    --add-rich-rule='rule family="ipv4" source not ipset="cloudflare-ipv4" service name="https" drop'
  • Ipv6 - https
firewall-cmd \
    --zone=public \
    --permanent \
    --add-rich-rule='rule family="ipv6" source not ipset="cloudflare-ipv6" service name="https" drop'

Drop non Cloudflare HTTP

  • Ipv4 - Http
firewall-cmd \
    --zone=public \
    --permanent \
    --add-rich-rule='rule family="ipv4" source not ipset="cloudflare-ipv4" service name="http" drop'
  • Ipv6 - http
firewall-cmd \
    --zone=public \
    --permanent \
    --add-rich-rule='rule family="ipv6" source not ipset="cloudflare-ipv6" service name="http" drop'

Reload and Test

Reload

When your restart you are making the rules active. If you are locked out because of any manipulation, check this article that will help you recover your system.

Made the rule effective.

systemctl reload firewalld
  • equivalent to
systemctl stop firewalld
systemctl start firewalld

Checks

Check the log;

systemctl status firewalld
# or tail -20 /var/log/firewalld

Check that the rule is still present

firewall-cmd --list-rich-rule

Test

To test that you can't connect, you can use your browser.

Just start it and set its DNS resolution for your website to your server IP.

For instance for chrome, you would start it with the below command that points the name datacadamia.com to the IP 212.186.33.26

chrome.exe --host-resolver-rules="MAP datacadamia.com 212.186.33.26"

Be sure to have no background chrome process running otherwise it will not work

Then if your try to see a page, you should get a Server could not be reached error.


Powered by ComboStrap