[exim-conf] фильтрация по континенту хоста отправителя и интеграция с GeoIP2 через libmaxmind

Victor Ustugov victor на corvax.kiev.ua
Вт Сен 18 01:11:59 EEST 2018


Victor Ustugov wrote on 12.09.2018 21:05:

> также реализовано определение страны и континента хоста отправителя по
> базе GeoIP2 с использованием библиотеке libmaxminddb.
> 
> интеграция произведена с помощью новой dlfunc:
> 
> https://mta.org.ua/exim-4.88-conf/dlfunc/maxminddb/
> 
> механизмы, используемые для определения страны и континента хоста
> отправителя, можно указать в соответствующих переменных
> confIP2COUNTRY_BACKEND и confIP2CONTINENT_BACKEND.
> 
> механизм определения страны по IP адресу:
> 
> DLFUNC		- определение страны по IP адресу с помощью dlfunc
> 		ip2country при включенной поддержке IPv6 используется
> 		dlfunc geoipv6
> 		для использования dlfunc необходимо:
> 		собрать exim с EXPAND_DLFUNC
> 		собрать и установить GeoIP library
> DLFUNC_GEOIP	- синоним DLFUNC
> DLFUNC_GEOIPV6	- определение страны по IP адресу с помощью dlfunc
> 		geoipv6
> 		для использования dlfunc необходимо:
> 		собрать exim с EXPAND_DLFUNC
> 		собрать и установить GeoIP library
> DLFUNC_GEOIP2	- определение страны по IP адресу с помощью dlfunc
> 		maxminddb
> 		для использования dlfunc необходимо:
> 		собрать exim с EXPAND_DLFUNC
> 		собрать и установить libmaminddb library
> DNSBL		- определение страны по IP адресу с помощю DNSBL
> 		zz.countries.nerd.dk
> WIP-API		- определение страны по WIPMANIA API
> 		(http://www.wipmania.com/ru/api/)
> PTRTLD		- определение страны по IP адресу с на основании TLD из
> 		PTR записи
> define(`confIP2COUNTRY_BACKEND', `DNSBL PTRTLD')dnl
> 
> в качестве значения confIP2COUNTRY_BACKEND можно указывать несколько
> механизмов.
> 
> т. к. dlfunc maxminddb является прототипом (сейчас обрабатываются только
> результаты с type MMDB_DATA_TYPE_UTF8_STRING), вполне разумно
> использовать сразу две dlfunc. т. е. указать в confIP2COUNTRY_BACKEND и
> DLFUNC_GEOIP2 и DLFUNC_GEOIP. при этом первой будет использовать dlfunc
> maxminddb, и только потом (в случае, если результат будет
> неопределённым) dlfunc ip2country или geoip6 (в зависимости от того,
> включена ли поддержка IPv6).

как оказалось, ещё рано отказываться от работы с GeoIP в пользу GeoIP2
(GeoLite2). вернее, единственно верного варианта вообще не существует.


сегодня были обнаружены два проблемных IP адреса - 163.157.254.161 и
62.180.229.48.


с определением страны для 163.157.254.161 по базе GeoIP есть некие проблемы:

# geoiplookup 163.157.254.161
GeoIP Country Edition: EU, Europe


аналог этого запроса средствами dlfunc ip2country:

# exim -be
'${dlfunc{/usr/local/libexec/exim/exim-dlfunc.so}{ip2country}{163.157.254.161}}'
EU


тот же результат получается при проверке страны по базе GeoIPv6:

# geoiplookup6 0:0:0:0:0:ffff:a39d:fea1
GeoIP Country V6 Edition: EU, Europe


# geoiplookup6 ::ffff:163.157.254.161
GeoIP Country V6 Edition: EU, Europe


аналог этого запроса средствами dlfunc geoipv6:

# exim -be
'${dlfunc{/usr/local/libexec/exim/exim-dlfunc.so}{geoipv6}{163.157.254.161}}'
EU


а вот результат аналогичного запроса к базе GeoLite2:

# mmdblookup --file /usr/local/share/GeoIP/GeoLite2-Country.mmdb --ip
163.157.254.161

  {
    "continent":
      {
        "code":
          "EU" <utf8_string>
        "geoname_id":
          6255148 <uint32>
        "names":
          {
            "de":
              "Europa" <utf8_string>
            "en":
              "Europe" <utf8_string>
            "es":
              "Europa" <utf8_string>
            "fr":
              "Europe" <utf8_string>
            "ja":
              "ヨーロッパ" <utf8_string>
            "pt-BR":
              "Europa" <utf8_string>
            "ru":
              "Европа" <utf8_string>
            "zh-CN":
              "欧洲" <utf8_string>
          }
      }
    "registered_country":
      {
        "geoname_id":
          2661886 <uint32>
        "is_in_european_union":
          true <boolean>
        "iso_code":
          "SE" <utf8_string>
        "names":
          {
            "de":
              "Schweden" <utf8_string>
            "en":
              "Sweden" <utf8_string>
            "es":
              "Suecia" <utf8_string>
            "fr":
              "Suède" <utf8_string>
            "ja":
              "スウェーデン王国" <utf8_string>
            "pt-BR":
              "Suécia" <utf8_string>
            "ru":
              "Швеция" <utf8_string>
            "zh-CN":
              "瑞典" <utf8_string>
          }
      }
  }


т. е. нет информации о пути поиска "country", зато есть информация о
пути поиска "registered_country".

первоначальный вариант dlfunc maxminddb на это не был расчитан:

# exim -be '${dlfunc{/usr/local/libexec/exim/exim-dlfunc.so}{maxminddb}{/usr/local/share/GeoIP/GeoLite2-Country.mmdb}{163.157.254.161}{YES}}'
2018-09-18 00:13:36 maxminddb dlfunc: Got an error looking up the entry
data for country ISO code - The lookup path does not match the data (key
that doesn't exist, array index bigger than the array, expected array or
map where none exists)
country="--" continent="--"


поэтому в случае, если функция MMDB_get_value из libmaxminddb возвращает
результат MMDB_LOOKUP_PATH_DOES_NOT_MATCH_DATA_ERROR в ответ на запрос
для пути поиска "country", "iso_code", сейчас запрос выполняется второй
раз, но уже для пути поиска "registered_country", "iso_code".

в результате теперь возвращается Швеция:

# exim -be '${dlfunc{/usr/local/libexec/exim/exim-dlfunc.so}{maxminddb}{/usr/local/share/GeoIP/GeoLite2-Country.mmdb}{163.157.254.161}{YES}}'
country="SE" continent="EU"


при этом в режиме отладки будет выведено сообщение:

maxminddb dlfunc: Got an error looking up the entry data for country ISO
code - The lookup path does not match the data (key that doesn't exist,
array index bigger than the array, expected array or map where none
exists); Try to find "registered_country" ISO code instead of "country"
ISO code


теперь об адресе 62.180.229.48 - данных по нему просто нет в GeoLite2,
вернее есть данные только по контитенту, но нет данных по стране:

mmdblookup --file /usr/local/share/GeoIP/GeoLite2-Country.mmdb --ip
62.180.229.48

  {
    "continent":
      {
        "code":
          "EU" <utf8_string>
        "geoname_id":
          6255148 <uint32>
        "names":
          {
            "de":
              "Europa" <utf8_string>
            "en":
              "Europe" <utf8_string>
            "es":
              "Europa" <utf8_string>
            "fr":
              "Europe" <utf8_string>
            "ja":
              "ヨーロッパ" <utf8_string>
            "pt-BR":
              "Europa" <utf8_string>
            "ru":
              "Европа" <utf8_string>
            "zh-CN":
              "欧洲" <utf8_string>
          }
      }
  }


такой же результат получен и через dlfunc maxmind:

# exim -be '${dlfunc{/usr/local/libexec/exim/exim-dlfunc.so}{maxminddb}{/usr/local/share/GeoIP/GeoLite2-Country.mmdb}{62.180.229.48}{YES}}'
2018-09-18 00:31:02 maxminddb dlfunc: Got an error looking up the entry
data for country ISO code - The lookup path does not match the data (key
that doesn't exist, array index bigger than the array, expected array or
map where none exists)
country="--" continent="--"


только вот нынешний вариант dlfunc maxmind аварийно прекращает
выполнение в случае, если страна не найдена (ни "country" ни
"registered_country").
и не возвращает код континента, даже если он в базе GeoLite2 есть, а ISO
кода страны в базе нет.

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

но при этом определение значения континента без определния значения
страны не особо и полезно.

в общем, пока непонятно, что с этим делать.
делить функцию на две отдельных функции для определения страны и
континента не хочется, т. к. придётся вызывать функции MMDB_open и
MMDB_lookup_string два раза вместо одного.


а вот если проверить результаты запросов к GeoIP и GeoIPv6 для второго
проблемного адресa 62.180.229.48, то окажется, что данные по нему
отличаются в GeoIP.dat и в GeoIPv6.dat:

# geoiplookup 62.180.229.48
GeoIP Country Edition: GB, United Kingdom


# geoiplookup6 ::ffff:62.180.229.48
GeoIP Country V6 Edition: EU, Europe


# geoiplookup6 0:0:0:0:0:ffff:3eb4:e530
GeoIP Country V6 Edition: EU, Europe


те же запросы средствами dlfunc ip2country и geoipv6:

# exim -be '${dlfunc{/usr/local/libexec/exim/exim-dlfunc.so}{ip2country}{62.180.229.48}}'
GB


# exim -be '${dlfunc{/usr/local/libexec/exim/exim-dlfunc.so}{geoipv6}{62.180.229.48}}'
GB


странно, что запрос из командосточной утилиты geoiplookup6 и dlfunc
geoipv6 вернул разные рузультаты. разве что я неправильно отобразил
62.180.229.48 на IPv6, хотя я выше две нотации проверил - рузльтат
одинаковый.


и GeoIP.dat и GeoIPv6.dat у меня обновляются с помощью скрипта
/usr/local/bin/geoipupdate.sh из состава пакета GeoIP-1.6.12 под FreeBSD.
при этом GeoIP.dat датирован аж 27.03.2018, а GeoIPv6.dat датирован
17.09.2018 (т. е. он таки обновляется, не смотря на заверения
сотрудников MaxMind). до этого GeoIPv6.dat обновлялся 05.09.2018, если я
не ошибаюсь.

с другой стороны - 62.180.229.48 является таки адресом из британской
сети. странно, почему информацию об этом выкинули из GeoIPv6.dat.
присутствовала ли она ранее в GeoLite2-Country.mmdb - мне неизвестно.


при такой каше в реультатах запросов к geo базам таки пока надёжнее
использовать вместе и запросы к базе GeoIP.dat (GeoIPv6.dat) и запросы к
базе GeoLite2-Country.mmdb, указав в значении confIP2CONTINENT_BACKEND и
DLFUNC_GEOIP2 и DLFUNC_GEOIP. вместо DLFUNC_GEOIP можно указывать старое
значение DLFUNC.

порядок указания значений DLFUNC_GEOIP2 и DLFUNC_GEOIP может быть
произвольным, всё равно сначала будет выполнен запрос к
GeoLite2-Country.mmdb (или к GeoLite2-City.mmdb, если путь именно к
этому файлу будет указан в первом параметре функции maxmind).


как-то так...



> в случае использования DLFUNC_GEOIP2 в качестве значения
> confIP2COUNTRY_BACKEND путь в файлу данных:
> define(`confIP2COUNTRY_MAXMINDDB_FILE',
> `/usr/local/share/GeoIP/GeoLite2-Country.mmdb')dnl
> 
> можно использовать как базу GeoLite2-Country.mmdb, так и базу
> GeoLite2-City.mmdb
> 
> игнорирование ошибок в работе dlfunc (на данный момент времени
> используетс в dlfunc maxminddb):
> NO          - не игноировать ошибки
> YES         - игноировать ошибки
> define(`confIP2COUNTRY_DEFER_OK', `YES')dnl
> 
> в случае возникновения ошибок в работе dlfunc maxminddb (не удалось
> прочитать файл базы, не удалось выделить память, не удалось извлечь
> данные из результатов запроса) приём письма будет продолжен, а в лог
> файл будет выведено сообщение об ошибке.
> 
> механизм определения континента по IP адресу
> DLFUNC		- определение континента по IP адресу с помощью dlfunc
> 		ip2country
> 		при включенной поддержке IPv6 используется dlfunc
> 		geoipv6
> 		для использования dlfunc необходимо:
> 		собрать exim с EXPAND_DLFUNC
> 		собрать и установить GeoIP library
> DLFUNC_GEOIP	- синоним DLFUNC
> DLFUNC_GEOIPV6	- определение континента по IP адресу с помощью dlfunc
> 		geoipv6
> 		для использования dlfunc необходимо:
> 		собрать exim с EXPAND_DLFUNC
> 		собрать и установить GeoIP library
> DLFUNC_GEOIP2	- определение континента по IP адресу с помощью dlfunc
> 		maxminddb
> 		для использования dlfunc необходимо:
> 		собрать exim с EXPAND_DLFUNC
> 		собрать и установить libmaminddb library
> define(`confIP2CONTINENT_BACKEND', `DLFUNC_GEOIP')dnl


-- 
Best wishes
Victor Ustugov        mailto:victor на corvax.kiev.ua
public GnuPG/PGP key: https://victor.corvax.kiev.ua/corvax.asc
Skype ID: corvax_nb   JID: victor на corvax.kiev.ua




Подробная информация о списке рассылки Exim-conf