Иногда возникает задача управления трафиком в зависимости от географического положения клиента. Возможные области применения — блокировка некоторых локаций либо перенаправление трафика на итоговый сервер в зависимости от локации клиента.

Традиционно подобные вещи реализуются при помощи библиотек GeoIP компании MaxMind — расскажу, как это сделать.

1. Настройка доступа к библиотекам MaxMind

Раньше не нужно было настраивать доступ, так как MaxMind предоставляли часть своих библиотек для прямого скачивания по ссылке. Однако сейчас функция прямого скачивания без регистрации недоступна. Поэтому сначала нужно зарегистрироваться здесь, заполнив форму:

После этого шага вам на почту придет письмо с дальнейшими инструкциями:

Перейдите по ссылке для создания пароля:

После завершения регистрации вы попадете в личный кабинет:

Чтобы скачивать файлы MaxMind напрямую, необходимо выписать лицензию на сайте. Для этого нужно перейти по ссылке My License Key:

Далее:

Далее:

На экране отобразится номер лицензии, который нужно сохранить — потом увидеть его будет нельзя, только выписывать новую лицензию:

Теперь, с номером лицензии, можно скачивать файлы баз данных GeoIP. Как это сделать написано тут.

Теперь переходим по ссылке: https://www.maxmind.com/en/accounts/current/geoip/downloads, и нажимаем линк Get permalinks напротив базы данных GeoLite2 Country: CSV Format:

В следующем окне указаны ссылки на скачивание:

Обратите внимание, что нужно заменить YOUR_LICENSE_KEY на вашу лицензию, которую вы выписали ранее.

На этом настройка получения базы GeoIP закончена, переходим непосредственно к настройке.

2. Настройка HAProxy

HAProxy не умеет работать напрямую с базами MaxMind, однако умеет строить ACL на основе списков сетей. Вот как это описывается в конфиге HAProxy:

frontend frontend-https
         bind *:80

# GeoIP ACL
          acl acl_SNG src -f /etc/haproxy/geoip/SNG.txt

Содержимое файла /etc/haproxy/geoip/SNG.txt:

...
188.215.252.0/22
188.237.0.0/16
188.240.70.0/24
188.244.16.0/20
191.96.60.0/23
192.121.87.0/24
193.8.167.0/24
193.16.111.0/24
193.17.78.0/24
...

То есть файл SNG.txt содержит в себе список сетей, которые попадут в ACL.

Перед нами стоит задача автоматизировать создание файла со списком сетей из баз данных MaxMind.

Сформулируем задачу, которую мы хотим решить: необходимо, чтобы трафик из России, Беларуси и Украины шел на один бэкенд, а трафик из других локаций уходил на другой бэкенд. Для решения этой задачи напишем shell-скрипт. Скрипт будет состоять из нескольких этапов.

Сначала получим свежую копию базы данных MaxMind в формате csv:

# Download geoip2 lite csv database
wget 
"https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Country-CSV&license_key=
${MAXMIND_LICENSE}&suffix=zip" -qOgeoip2lite.zip 
&& unzip -j geoip2lite.zip

Здесь ${MAXMIND_LICENSE} — это номер лицензии, который мы выписали ранее на сайте MaxMind.

После выполнения команды содержимое архива будет выглядеть так:

root@ash:/tmp/geo2lite# ls -1
COPYRIGHT.txt
GeoLite2-Country-Blocks-IPv4.csv
GeoLite2-Country-Blocks-IPv6.csv
GeoLite2-Country-Locations-de.csv
GeoLite2-Country-Locations-en.csv
GeoLite2-Country-Locations-es.csv
GeoLite2-Country-Locations-fr.csv
GeoLite2-Country-Locations-ja.csv
GeoLite2-Country-Locations-pt-BR.csv
GeoLite2-Country-Locations-ru.csv
GeoLite2-Country-Locations-zh-CN.csv
LICENSE.txt
README.txt

Нас интересуют два файла — GeoLite2-Country-Blocks-IPv4.csv, содержащий информацию о принадлежности сетей ipv4 странам, и GeoLite2-Country-Locations-en.csv, содержащий соответствия названий стран цифровым кодам.

Соответственно, если нам нужно получить все сети, принадлежащие России, мы для начала получаем код России:

root@ash:/tmp/geo2lite# cat GeoLite2-Country-Locations-en.csv | grep 
Russia
2017370,en,EU,Europe,RU,Russia,0

Здесь 2017370 — это искомый код.

Теперь по нему можно определить сети.

root@ash:/tmp/geo2lite# cat GeoLite2-Country-Blocks-IPv4.csv | grep 2017370
...
217.147.16.0/20,2017370,2017370,,0,0
217.148.48.0/20,2017370,2017370,,0,0
217.148.192.0/19,2017370,2017370,,0,0
217.149.16.0/20,2017370,2017370,,0,0
217.149.176.0/20,2017370,2017370,,0,0
217.150.0.0/18,2017370,2017370,,0,0
217.150.72.0/21,2017370,2017370,,0,0
217.150.192.0/20,2017370,2017370,,0,0

Обратите внимание, что число 2017370 встречается в каждой строке два раза. Обратимся к описанию формата файла:

root@ash:/tmp/geo2lite# cat GeoLite2-Country-Blocks-IPv4.csv | head -n 1
network,geoname_id,registered_country_geoname_id,represented_country_geo
name_id,is_anonymous_proxy,is_satellite_provider

Здесь registered_country_geoname_id — код страны, за которой зарегистрирована сеть, а represented_country_geoname_id — код страны, из которой происходит роутинг сети.

Возможна ситуация, когда сеть зарегистрирована, например за Россией, а роутинг происходит из Голландии. Как поступать в такой ситуации, решайте сами, я выбирал только сети, зарегистрированные и маршрутизируемые из запрашиваемой страны.

Соответственно, выбирая нужные сети из файла GeoLite2-Country-Blocks-IPv4.csv, можно сформировать файл для HAProxy ACL.

Сама работа с ACL в HAProxy:

frontend frontend
# GeoIP ACL
          acl acl_SNG src -f /etc/haproxy/geoip/SNG.txt

          use_backend backend1 if acl_SNG
          default_backend backend2

Соответственно, если IP-адрес источника будет из сети, описанной в файле SNG.txt, то запрос уйдет на backend1, в противном случае — на backend2.

Для автоматизации операций был написан скрипт:

root@ash:~# cat haproxy_geoip_list.sh
#!/bin/bash
#-----------------------------------------------------------------
------------------------------------------------------
# Variables
TMPDIR="/tmp/geo2lite"
MAXMIND_LICENSE="xxxxxxxxxxxxx"
LOCATIONS="Russia Ukraine Belarus"
HAPROXY_PATH="/etc/haproxy/geoip"
HAPROXY_FILE="SNG.txt"
#-----------------------------------------------------------------
------------------------------------------------------
mkdir -p ${TMPDIR}
pushd ${TMPDIR}
# Download geoip2 lite csv database
wget 
"https://download.maxmind.com/app/geoip_download?edition_id=GeoLite2-Cou
ntry-CSV&license_key=
${MAXMIND_LICENSE}&suffix=zip" -qOgeoip2lite.zip 
&& unzip -j geoip2lite.zip
if [ $? -gt 0 ]; then
   logger -t "haproxy_geoip" "Download not success."
   exit 1
fi

exit
for COUNTRY in ${LOCATIONS}; do
   echo "# ${COUNTRY}"
   COUNTRY_CODE=`cat GeoLite2-Country-Locations-en.csv | grep -i
   "${COUNTRY}" | awk -F "," '{print $1}'`
   grep ",${COUNTRY_CODE}," GeoLite2-Country-Blocks-IPv4.csv | awk      -F ","
   '{print $1}'
done > ${HAPROXY_FILE}

# Check file
if [ ! -s ${HAPROXY_FILE} ]; then
     logger -t "haproxy_geoip" "File is not exist"
     exit 1
fi

if [ -s ${HAPROXY_PATH}/${HAPROXY_FILE} ]; then
     MD5_FILE=`md5sum ${HAPROXY_PATH}/${HAPROXY_FILE} | awk '{print $1}'`
fi

MD5_TMPFILE=`md5sum ${HAPROXY_FILE} | awk '{print $1}'`

if [ "${MD5_TMPFILE}" != "${MD5_FILE}" ]; then
   logger -t "haproxy_geoip" "File ${HAPROXY_FILE} is changed"
   cp ${HAPROXY_PATH}/${HAPROXY_FILE}
${HAPROXY_PATH}/${HAPROXY_FILE}.backup
   mv ${HAPROXY_FILE} ${HAPROXY_PATH}/${HAPROXY_FILE}
   # Test new haprpoxy cfg
   /usr/sbin/haproxy -c -f /etc/haproxy/haproxy.cfg
   if [ $? -gt 0 ]; then
     logger -t "haproxy_geoip" "New file is corrupted, replace by older one from backup."
     mv ${HAPROXY_PATH}/${HAPROXY_FILE}.backup
${HAPROXY_PATH}/${HAPROXY_FILE}
  fi 
fi

popd
rm -rf "${TMPDIR}"

Перед запуском не забудьте заполнить секцию Variables под ваши нужды. Удачи!

Оригинал статьи на Habr.com