There are various guides on the web, regarding how to block a whole country, or a group of IP addresses using various firewall methods.
But, what if we need to do the opposite. Allow only one country, and block everyone else.
This may be particularly useful for personal services, like Nextcloud, that you know will only be used by yourself.
Of course, using the same method, it is also possible to allow only a few IP addresses, a city, or just one ISP. All that is needed are the group of IP blocks.
For countries, we can get them from the IPdeny lists. The aggregated files will be more efficient, since they will have less blocks and thus fewer rules.
Since this article uses firewalld, some quick notes on how it works.
By default, firewalld
will have one active zone, with some default
services attached.
# get all active zones
$ firewall-cmd --get-active-zones
public
interfaces: eth0
# list all rules on the public zone
$ firewall-cmd --zone=public --list-all
public (active)
target: default
icmp-block-inversion: no
interfaces: eth0
sources:
services: dhcpv6-client ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
The above rules mean, that everyone coming through the interface eth0 will be allowed on the services ssh and dhcpv6-client.
Now back to our goal. We will need to do the following:
- Get the IP address blocks.
- Create a list, and populate it with our addresses.
- Create a new zone, and set the list to it.
- Assign all the needed services to this zone, remove them from the other zones.
# download the country IP blocks
wget "https://www.ipdeny.com/ipblocks/data/aggregated/am-aggregated.zone"
Here we create an ipset
with the name allowlist
(could be
anything), and we populate it with the downloaded list.
# create list
firewall-cmd --permanent --new-ipset=allowlist \
--type=hash:net --option=family=inet \
--option=hashsize=4096 --option=maxelem=200000
# populate list
firewall-cmd --permanent --ipset=allowlist \
--add-entries-from-file=./am-aggregated.zone
# we can display the list like this
firewall-cmd --ipset=allowlist --get-entries
Then, we create a new zone allowzone
, assign the allowlist
to it,
and enable some services and ports.
# create zone
firewall-cmd --permanent --new-zone allowzone
# assign the list to the new zone
firewall-cmd --permanent --zone=allowzone --add-source=ipset:allowlist
# allow services
firewall-cmd --permanent --zone=allowzone --add-service ssh
firewall-cmd --permanent --zone=allowzone --add-service http
firewall-cmd --permanent --zone=allowzone --add-service https
Now, allowzone
will only allow the added services for the
allowlist
, and no one else.
# check all the rules on the new zone
$ firewall-cmd --permanent --zone=allowzone --list-all
allowzone (active)
target: default
icmp-block-inversion: no
interfaces:
sources: ipset:allowlist
services: http https ssh
ports:
protocols:
masquerade: no
forward-ports:
source-ports:
icmp-blocks:
rich rules:
We still need to remove any enabled services from the public
zone,
otherwise it will keep allowing everyone on eth0.
Up until this point, since we are using the --permanent
option,
nothing will be applied until we reload or restart firewalld
. And
since we added a new zone, it is not yet dangerous to do so
now. However, once we remove services like ssh from the active public
zone, we may lock ourselves out. So, some common sense care should be
applied here. Maybe start by removing a less dangerous port first.
# remove services from public zone
firewall-cmd --permanent --zone=public --remove-service ssh
Finally, reload or restart firewalld
.
firewall-cmd --reload