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 with ipset test.
- 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.