Привет всем,
Сегодня будет быстрая заметка, относительно того как можно просто прикрутить GeoIP базы к уже существующему инстансу haproxy. И как пример, рассмотрим задачу в которой нужно заблокировать доступ к ресурсу для всех. И открыть только для определенных стран.
На просторах интернета можно найти схожите статейки, они обычно начинаются с получения доступа к бесплатным geoip - базам с ресурса maxmind.com
. Там потребуется пройти регистрацию/верификацию аккаунта, далее получить токен для скачивая нужных файлов. Базы хоть и бесплатные, но не для коммерческого использования. Иначе извольте занести котлету =)
В процессе поиска альтернативного решения, наткнулся на github-репозиторий в котором уже нашел необходимые данные в csv-формате. Репозиторий не заброшен, судя по истории, с переодичностью один раз в неделю выкатывается новый релиз. Что касаемо возможных компроментаций, не думаю что есть опасения. Так как для решения нашей задачи, мы будем использовать уже заготовленные csv, остальное содержимое нам не интересно. Да и в целом проект имеет достаточное количество собранных звезд и форков:
Собственно с базами все ясно, теперь напишем скриптец, который будет скачивать архив. Далее разпаковывать его, и создавать файлы с сетями по странам.
#!/bin/bash
PROXY_SERVER=""
PROXY_PORT=""
GEOIP_ACL_PATH="/etc/haproxy/geoip"
GEOIP_TEMP_PATH="/tmp"
function DownloadDatabases () {
# Clean old downloads artifacts:
rm -rf $GEOIP_TEMP_PATH/{*.csv,*.zip}
# Get geoip database zip-file and unzip archive:
if [ $PROXY_SERVER ]; then
export https_proxy="http://$PROXY_SERVER:$PROXY_PORT/"
fi
curl -o $GEOIP_TEMP_PATH/geip_csv.zip https://raw.githubusercontent.com/Loyalsoldier/geoip/release/GeoLite2-Country-CSV.zip && \
unzip -j $GEOIP_TEMP_PATH/geip_csv.zip -d $GEOIP_TEMP_PATH "*IPv4.csv" "*en.csv"
# Rename csv-file:
find /tmp/ -depth -type f -name '*IPv4.csv' -exec mv {} /tmp/ips.csv \;
find /tmp/ -depth -type f -name '*en.csv' -exec mv {} /tmp/countres.csv \;
}
function CreateAclLists () {
if [ ! -d $GEOIP_ACL_PATH ]; then
mkdir -p $GEOIP_ACL_PATH
fi
while read line; do
COUNTRY_CODE=$(echo $line | cut -d, -f5)
COUNTRY_ID=$(echo $line | cut -d, -f1)
# Create new country list
cat $GEOIP_TEMP_PATH/ips.csv | grep $COUNTRY_ID | cut -d, -f1 > $GEOIP_ACL_PATH/$COUNTRY_CODE.list
done <$GEOIP_TEMP_PATH/countres.csv
}
DownloadDatabases
CreateAclLists
В скрипте мы определяем переменные:
PROXY_SERVER
,PROXY_PORT
- в этих переменных мы храним адрес и порт нашего прокси сервера. Если же сервера ходят в интернет напрямую, то значения можно оставить пустыми.GEOIP_ACL_PATH
- в значении этой переменной определяется путь, по которому будут созданы ALC-списки (файлы).GEOIP_TEMP_PATH
- тут просто указываем папку, в которой будет лежать архив, и распакованные файлы. Эти файлы важны только на момент исполнения скрипта, их потеря не критична.
Далее следуют две функции, c последовательностью команд:
- Функция
DownloadDatabases
- в теле этой функции представлена последовательность команд, с помошью которых мы скачиваем архив. Далее из архива распаковываем два файла - GeoLite2-Country-Locations-en.csv, GeoLite2-Country-Blocks-IPv4.csv. В табличке GeoLite2-Country-Locations-en.csv содержится списки со странами - iso-код страны, имя страны и id. Таблица GeoLite2-Country-Blocks-IPv4.csv содержит все ip-адреса. В конце при помощи утилитыfind
мы переименовываем файлы в более упрощенные названия. - Функция
CreateAclLists
- с помошью этой функции создаются acl-листы и заполняются подсетями. В начале через условиеif
проверяем наличие каталога в котором будут создаваться правила. И если этого каталога не существует, то он будет создан. Затем следует циклwhile
, с помошью которого мы проходится по файлу со странами -GeoLite2-Country-Locations-en.csv
. В теле цикла мы выдергиваем из каждой строки строки код и id страны. Далее за этими данными будем обращаться через переменные -COUNTRY_CODE
,COUNTRY_ID
. В конце цикла черезcat
вытягиваем все данные из файла -GeoLite2-Country-Blocks-IPv4.csv
. Затем через pipe грепаем всe по id-страны, в завершении черезcut
вырезаем подсеть и записаваем все в файл.
Делаем скрипт исполняемый и запускаем его:
[root@lb01 ~]# chmox +x /opt/scripts/geoipgen.sh
[root@lb01 ~]# bash /opt/scripts/geoipgen.sh
После выполнения скрипта у нас будет создано множество acl-файлов:
[root@lb01 ~]# ls /etc/haproxy/geoip/
AD.list AQ.list BA.list BJ.list BT.list CF.list CO.list CZ.list ....
Внутри каждого acl, будет свой набор подсетей принадлежайшей каждой стране.
[root@lb01 ~]# cat /etc/haproxy/geoip/AD.list
5.62.60.5/32
5.62.60.6/31
5.62.62.5/32
5.62.62.6/31
34.99.136.0/23
34.99.208.0/23
34.103.152.0/23
34.103.158.5/32
34.103.158.6/31
Ну вот и славненько, теперь возвращаясь к нашей задаче, мы можем заблокировать доступ для определенных стран. Для этого открываем конфигу haproxy. Далее в контексте frontend
, создаем новое acl-правило:
[root@lb01 ~]# vim /etc/haproxy/haproxy.cfg
---
frontend lb_internal
mode http
bind *:80
bind *:443 ssl crt /etc/ssl/certs/domain.pem
# Redirect from 80 -> 443
redirect scheme https code 301 if !{ ssl_fc }
# Добавленное правило
acl allow_countries src -f /etc/haproxy/geoip/RU.list
Тут мы определяем правило c именем allow_countries
, с помошью опции src -f
берем содержимое из файла /etc/haproxy/geoip/RU.list
. В файле определены подсети, которые закреплены за РФ.
Теперь что бы заблокировать доступ всем, и разрешить доступ только клиентам на территории РФ, в конфиге добавляем запрет на новые http-запросы:
http-request deny if !allow_countries
Этот параметр также добавляется в контекст frontend.
Условно говоря, теперь нам понадобилось открыть доступ для клиентов c ip Казахстана. Для этого в конфиг добавляем аналогичный acl:
[root@lb01 ~]# vim /etc/haproxy/haproxy.cfg
---
frontend lb_internal
mode http
bind *:80
bind *:443 ssl crt /etc/ssl/certs/domain.pem
# Redirect from 80 -> 443
redirect scheme https code 301 if !{ ssl_fc }
acl allow_countries src -f /etc/haproxy/geoip/RU.list
# Разрешаем доступ для KЗ.
acl allow_countries src -f /etc/haproxy/geoip/KZ.list
# Запрешающее действие
http-request deny if !allow_countries
Собственно, на этом все. Только еще дополню, что на основе подобных правил мы можем выполнять выбор бекенда, тем самым реализовать роутинг клиентов на определенный сервер и так далее.