Nginx: How to Restrict Access by Geographical Location on FreeBSD
In my setups this is mostly used to secure a backend login when a client doesn’t have access to a static IP, yet will only ever access the administration from within a certain geographical boundary, usually a country.
Start by either configuring the port to add the HTTP_GEOIP2
option
(lightweight) or install the www/nginx-full
package, which installs just
about everything and the kitchen sink: Java, ffmpeg, what have you.
# From ports
cd /usr/ports/www/nginx
make config
# Check HTTP_GEOIP2 and accept default options for all other ports
make install clean
# From packages
pkg install nginx-full
We’d like to automatically update the GeoIP database.
pkg install geoipupdate
The package info is a bit of a mess. Correct, however, is the fact that you need to register a free account with Maxmind, which you can do here:
https://www.maxmind.com/en/geolite2/signup
Afterwards, create a license key (also free) here, substituting YOUR_USER_ID
:
https://www.maxmind.com/en/accounts/YOUR_USER_ID/license-key
At the time of writing, the given location of the configuration file is wrong.
The correct location is /usr/local/etc/GeoIP.conf
. Add your user id and
license number there. Then just run geoipupdate
as root.
To finish up here, add a simple cronjob. Below is a sample crontab entry that
runs geoipupdate
on each Wednesday at noon:
0 12 * * 3 /usr/local/bin/geoipupdate
Now we’re equipped for the Nginx configuration. Add the following:
# /usr/local/etc/nginx/nginx.conf
load_module /usr/local/libexec/nginx/ngx_stream_module.so;
load_module /usr/local/libexec/nginx/ngx_stream_geoip2_module.so;
load_module /usr/local/libexec/nginx/ngx_http_geoip2_module.so;
[...]
http {
[...]
geoip2 /usr/local/share/GeoIP/GeoLite2-Country.mmdb {
$geoip2_data_country_iso_code country iso_code;
}
map $geoip2_data_country_iso_code $allowed_country {
default no;
AT yes;
CH yes;
DE yes;
}
[...]
}
This enables us to use an if-statement inside a location block, like so:
location /admin/ {
if ($allowed_country = no) {
return 403;
}
}
Check the configuration and restart.
nginx -t && service nginx restart
The Nginx documentation contains more information, including a rather interesting use case of choosing the nearest server based on geolocation.