Актуальной задачей для веб-ресурса в нынешних реалиях является установка ограничений доступа по регионам, либо географическая привязка выдаваемой информации с учетом региона. Одним из решений, которые можно использовать для этих целей, является база данных и американский сервис MaxMind GeoIP2. Сервис привлекает огромным объемом и детальностью информации, но имеет свои особенности. Так, Крым, ДНР, ЛНР, Запорожская и Херсонская области а также Севастополь указаны в базе как регионы Украины. Соответственно, если, скажем, разрешить доступ к ресурсу только с территории РФ, то эти регионы останутся не у дел.
Так я, первоначально настроив свой ресурс только на доступ с территории РФ, обнаружил, что мои друзья из Крыма и ДНР не могут получить доступ (получают специальную версию страницы, с ограниченным объемом информации). Проблема эта легко решается, и в этой статье я покажу, как именно можно простым спососбом ее решить, а также покажу действующий пример конфигурации сервера.
Мой пример основан на nginx и модуле ngx_http_geoip2_module, предназначенном для работы с базой MaxMind, под Ubuntu 20.04. Следует учесть, что в настоящее время актуальной является версия GeoIP2, имеющая новый формат БД. Пример приводится на основе бесплатной версии базы GeoLite2. Она содержит три таблицы. Первая, с данными IP, содержащими только информацию о стране (GeoLite2-Country), вторая - только о провайдере (GeoLite2-ASN), и третья содержит подробные данные по каждой местности (GeoLite2-City). Для фильтрации отдельных регионов нам нужна именно третья таблица.
Для получения базы GeoLite2 требуется учетная запись MaxMind. Ее можно завести тут: https://www.maxmind.com/en/geolite2/signup
Если из-за санкций не удается официально зарегистрироваться и получить базу, то это спокойно можно сделать из альтернативных источников. Главное, загрузить на сервер любым способом свежую версию файла GeoLite2-City.mmdb, и поместить его (для Ubuntu) в папку "/usr/share/GeoIP/
".
Если зарегистрироваться удалось (например, имея двойное гражданство с другой страной, против которой санкции не введены?), тогда для загрузки и обновления базы есть удобная утилита от самого сервиса.
Подключаем репозиторий:
$ sudo add-apt-repository ppa:maxmind/ppa
Устанавливаем утилиту:
$ sudo apt update
$ sudo apt install geoipupdate
Конфигурация утилиты осуществляется в файле /etc/GeoIP.conf
Здесь нужно указать AccountID - берется из данных вашего аккаунта,
LicenseKey - нужно получить на сайте, в вашем кабинете.
EditionIDs - какие таблицы нужно загрузить. В нашем случае:
EditionIDs GeoLite2-City
Дальше переходим к настройке nginx / ngx_http_geoip2_module. Самым простым вариантом является установка из штатного репозитория Ubuntu бинарной версии пакетов:
$ sudo apt-get install nginx-full
Сюда входит как сам сервер nginx, так и модуль ngx_http_geoip2_module.
Обращу внимание, что нужен именно модуль geoip2. Модуль первой версии не сможет считать таблицу в новом формате.
Далее настраиваем nginx. Для работы модуля нужно добавить следующие строки в файл /etc/nginx/nginx.conf
:
load_module modules/ngx_http_geoip2_module.so;
В первой версии модуля для загрузки таблиц использовались параметры geoip_country и geoip_city. Для второй версии они не актуальны.
Настройка модуля второй версии проводится в секции http, и выглядит следующим образом:
geoip2 /usr/share/GeoIP/GeoLite2-City.mmdb {
[параметры]
[...]
}
Параметры позволяют вам, помимо настройки модуля, определить глобальные переменные nginx, которые будут содержать нужные вам данные из базы по геолокации текущего запроса. Какие именно данные присваиваются переменной, определяется указанием соответствующих полей таблицы, имеющей древовидную структуру. Структура описана на сайте MaxMind.
Для нашей задачи подойдет такая конфигурация:
geoip2 /usr/share/GeoIP/GeoLite2-City.mmdb {
auto_reload 60m;
$geoip2_metadata_city_build metadata build_epoch;
$geoip2_data_country_name country names en;
$geoip2_data_country_code country iso_code;
$geoip2_data_city_name city names en;
$geoip2_data_region_name subdivisions 0 names en;
$geoip2_data_state_code subdivisions 0 iso_code;
}
Параметр auto_reload определяет, насколько часто модуль будет считывать заново данные из файла таблицы. А файл таблицы, в свою очередь, должен обновляться через crontab. Таким образом можно поддерживать актуальность данных.
$geoip2_metadata_city_build - временная метка сборки базы
$geoip2_data_country_name - название страны на английском
$geoip2_data_country_code - двухбуквенный код страны
$geoip2_data_city_name - имя города на английском
$geoip2_data_region_name - имя региона (Крым, Севастополь, Московская область и т. д.)
$geoip2_data_state_code - код региона. Это именно то, что нам надо для фильтрации. Бывают двухбуквенные коды, трехбуквенные, цифровые коды. И даже однобуквенные. Иногда этот параметр вовсе отсутствует в базе.
Стоит отметить, что доступны данные и на русском языке. Полную структуру данных таблицы можно узнать в документации MaxMind, доступной на их сайте.
Получать имеет смысл только те данные, которые вам действительно нужны для настройки проекта. Нам нужны в первую очередь два параметра:
$geoip2_data_country_code - двухбуквенный код страны
$geoip2_data_state_code - код региона
Для простоты решения задачи мы можем тут же, в секции http файла конфигурации nginx, отфильтровать нужные нам регионы и создать логическую переменную, которую можно использовать для дальнейшего ограничения доступа. Например вот так:
map "$geoip2_data_country_code:$geoip2_data_state_code" $allowed_reg {
default no;
~^RU: yes; #Россия
~^BY: yes; #Белоруссия
~^AM: yes; #Армения
~^KZ: yes; #Казахстан
~^KG: yes; #Кыргызстан
UA:40 yes; #Севастополь
UA:43 yes; #Крым
UA:14 yes; #Донецкая область
UA:09 yes; #Луганская область
UA:23 yes; #Запорожская область
UA:65 yes; #Херсонская область
GE:AB yes; #Абхазия
}
Теперь переменная $allowed_reg содержит логическое значение, которое можно использовать либо для ограничения доступа на уровне конфигурации сервера, либо для формирования содержимого на уровне SSI.
Здесь использованы две переменные, разделенные двоеточием, в качестве исходного значения MAP. Для определения стран без учета региона используются регулярные выражения. Для определения регионов, имеющих ложную привязку, использованы простые значения, указывающие на конкретный регион по двухбуквенному обозначению страны и коду региона.
Для ограничения доступа на уровне конфигурации можно использовать такую конструкцию:
server {
#Block forbidden country
if ($allowed_reg = no) {
return 444;
}
Для использования переменной на уровне SSI можно использовать вот такую конструкцию:
<!--# if expr="$allowed_reg = yes" -->
[...]
<!--# else -->
[...]
<!--# endif -->
Варианты использования этого механизма зависят только от ваших потребностей. Это может быть ограничение доступа или выдача контента, имеющего региональные особенности.