Привет всем,

Сегодня будет быстрая заметка, относительно того как можно просто прикрутить GeoIP базы к уже существующему инстансу haproxy. И как пример, рассмотрим задачу в которой нужно заблокировать доступ к ресурсу для всех. И открыть только для определенных стран.

На просторах интернета можно найти схожите статейки, они обычно начинаются с получения доступа к бесплатным geoip - базам с ресурса maxmind.com. Там потребуется пройти регистрацию/верификацию аккаунта, далее получить токен для скачивая нужных файлов. Базы хоть и бесплатные, но не для коммерческого использования. Иначе извольте занести котлету =)

В процессе поиска альтернативного решения, наткнулся на github-репозиторий в котором уже нашел необходимые данные в csv-формате. Репозиторий не заброшен, судя по истории, с переодичностью один раз в неделю выкатывается новый релиз. Что касаемо возможных компроментаций, не думаю что есть опасения. Так как для решения нашей задачи, мы будем использовать уже заготовленные csv, остальное содержимое нам не интересно. Да и в целом проект имеет достаточное количество собранных звезд и форков: haproxy-geoip-git.png

Собственно с базами все ясно, теперь напишем скриптец, который будет скачивать архив. Далее разпаковывать его, и создавать файлы с сетями по странам.

#!/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

Собственно, на этом все. Только еще дополню, что на основе подобных правил мы можем выполнять выбор бекенда, тем самым реализовать роутинг клиентов на определенный сервер и так далее.