Setting up GeoIP blocking for web services using pfSense, HAProxy and free IPDeny databases

Today, many organizations that transfer their services to web platforms are forced to provide an entry point to the Internet for user authentication. Even with 2-factor authentication, a web service accessible via the Internet remains vulnerable to complex attacks, the result of which may be data theft, malicious code loading into the application or disruption of the service. It is not always possible to install a WAF (web application firewall) to restrict access to the service. One of the ways to protect a web service may be to restrict access to it by IP, including GeoIP, based on the user's location.

This will completely block access from the list of compromised, spammed IP addresses, as well as from entire countries and continents. Such a method will not help from an intentional hacker attack, but it will dramatically reduce the number of attacks by bots, which usually scan vulnerabilities automatically through proxy servers from IP addresses of third world countries.

NAT or HAProxy?

Even if you have a single web server on your network that looks "outside" with port 443, it still makes sense to abandon NAT in favor of HAProxy as a more flexible option, and transfer certificate updates to the security gateway. pfSense will allow you to use GeoIP blocking for normal Firewall rules (if you have NAT), but we will not consider this method.

What are GeoIP lists?

Regular GeoIP lists contain information about the geographical location of IP addresses. This type of list is used to determine the country, region, or city from which the request to the web service incomes.

Where to get databases (GeoIP lists)?

Usually, everyone recommends Maxmind services, but there is a simpler alternative, as they say, "without SMS and registration" - service , where GeoIP databases are publicly available, sorted by country.

Как добавить GeoIP в PFSense?

In pfSense, it is possible to create aliases for the rules of the built-in Firewall. As a source, you can specify the URL of the GeoIP list or enter the IP addresses of subnets manually. Usually one URL represents a range of IP addresses for a specific country or region and has the corresponding 2-letter code in the file name. Since there can be many regions, it makes sense not to ban each of them separately, but rather to add only the allowed region, and ban all the others. This will increase the performance of the gateway and reduce the amount of memory used.

For example, if we want to use Russia or the USA as the only regions that have access to our web services, we must create two aliases: Russia and USA, specifying for each of them in the form of content - the URL of the GEOIP feed. By default, feeds are updated once a day, which is quite enough to work with free databases, which are usually updated even less often – once a week, or even a month.

Configuring the Haproxy ACL

To configure the ACL (Access Control List) in Haproxy (version 2.8-d12 from the haproxy-devel 0.63_1 package was used), you open your Backend and go to the ACL settings. Here you can select a trigger that will be triggered under certain conditions - for example, if the IP source matches the alias Russia or USA.

Setting ACL in HAProxy

Depending on your needs and configuration, you can create separate triggers for each of your aliases. This can be especially useful if you have VPN connections to a corporate network or ranges of local subnets that should be excluded from access rules. Important: if the protected web services are accessed via the Intranet, you definitely need to create a separate alias with the IP addresses of local networks and create a condition (trigger) in the ACL list for each backend.

Now it remains only to select an action for triggers. Here it is necessary to understand that actions are triggered from top to bottom, before the first match, so first we specify permissive rules, and below the list – prohibitive ones. By checking the “Not” box, we can enable the inverse rule. In the simplest case, we will need to allow access from the local network and allow access from Russia. We specify the first permission explicitly, and the second – inversely, that is, we prohibit all any addresses except those from Russia and the USA.

The action itself is set by the server's response to the request, everything is clear with the permissive – the server must accept the header. But there may be several prohibitive ones, including a redirect. I do not see the point of being sly in choosing a rebound and I think that choosing http-request allow is necessary for permissive action and http-request deny to deny access. Users from the permitted regions will not even notice that there was some kind of filtering, and from the prohibited ones they will receive the 502nd error and go looking for other victims.


Restricting access to the web service only from permitted regions based on GeoIP significantly reduces the number of unwanted requests and bot attacks, which ultimately increases the stability of the service and the security of user data. However, it is worth remembering that this method of protection is ineffective against intentional attacks by intruders who can circumvent GeoIP restrictions using proxy servers in a permitted region.

Michael Degtjarev (aka LIKE OFF)

Read also: