Проброс IP-адреса клиента в http-сервис 1С. Реализация для IIS









Настраиваем веб-сервер IIS для передачи в 1С IP-адреса клиента, вызвавшего http-сервис. Разбираемся с этим же вопросом при использовании фронтэнд вебсервера на примере nginx.

На прошлой неделе была опубликована заметка "Как передать IP адрес, который вызвал HTTP запрос в 1C (для веб-сервера Apache)". Судя по "звездопаду", обрушевшемуся на неё, тема определения в приложении IP-адреса клиента является насущной для сообщества.

В упомянутой статье дан рецепт проброса клиентского IP-адреса в 1С (равно как и в любое другое приложение) с помощью кастомного http-заголовка для веб-сервера Apache.

В комментариях было задано два вопроса, оставшихся без ответа:

1. А как быть если перед сервером стоит какой-нибудь прокси, например фронтенд веб-сервер nginx?

2. Как сделать подобное для веб-сервера IIS, если это вообще возможно?

Постараюсь ответить на первый и предложить свое решение для второго. Развёрнуто и с картинками. Итак: 

Фронтенд веб-сервер

Если впереди стоит фронтенд веб-сервер, например nginx, то задача решается совсем просто. Не понадобится даже использование решения, предлагаемого в вышеупомянутой статье.

Всё, что требуется, это донастроить фронтенд сервер, включив в его настройках заполнение заголовка X-Forwarded-For, который является дефакто-стандартом для передачи реального IP-адреса исходного запроса.

После включения заголовка X-Forwarded-For на фронт-сервере, этот заголовок будет доступен как в рабочем веб-сервере, так и в коде 1С. То есть мы можем обрабатывать его и извлекать необходимый IP-адрес.

Для nginx эта настройка будет выглядеть так:

proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
 

 Выдержка справки nginx

http://nginx.org/ru/docs/http/ngx_http_proxy_module.html

Встроенные переменные

В модуле ngx_http_proxy_module есть встроенные переменные, которые можно использовать для формирования заголовков с помощью директивы proxy_set_header:

$proxy_host

имя и порт проксируемого сервера, как указано в директиве proxy_pass;
$proxy_port
порт проксируемого сервера, как указано в директиве proxy_pass, или стандартный порт протокола;
$proxy_add_x_forwarded_for
поле заголовка запроса клиента “X-Forwarded-For” и добавленная к нему через запятую переменная $remote_addr. Если же поля “X-Forwarded-For” в заголовке запроса клиента нет, то переменная $proxy_add_x_forwarded_for равна переменной $remote_addr.

 

Проброс IP-адреса клиента на веб-сервере IIS

Для того, чтобы реализовать кастомный заголовок, содержащий IP-адрес клиента, на веб-сервере IIS, понадобится установить модуль URL Rewrite. Скачать его можно на официальном сайте: https://www.iis.net/downloads/microsoft/url-rewrite.

Модуль предназначен для версий IIS 7, IIS 7.5, IIS 8, IIS 8.5, IIS 10

Скриншоты примеров сделаны на инсталляции с версией IIS 7.5.

После установки иконка модуля появится в секции IIS при  выделенном в левой панели сайте.

Для создания и заполнения заголовка нам нужно будет создать правило.

Выделяем Default Web Site и в средней панели даблкликом открываем модуль URL Rewrite.

В правой панели жмем ссылку "View Server Variables…".

Там же, в правой панели жмем "Add…" и пишем желаемое название заголовка с префиксом "HTTP_" (обязательно в верхнем регистре). У меня http-заголовок будет называться Client-IP-Address, поэтому моя переменная зовется "HTTP_Client-IP-Address"
Префикс "HTTP_" обеспечит нам создание нового http-заголовка.

После создания переменной, возвращаемся к списку правил, нажав в правой панели на ссылку "Back to rules".

Создаем новое правило нажатием на ссылку "Add Rule(s)…" и выбираем "Blank rule" в категории "Inbound rules"

Даем нашему правилу какое-нибудь содержательное название. У меня это будет "Add Client-IP-Address header".

В секции Match URL выбираем Matches the Pattern, Regular Expressions и указываем выражение, которому должен соответствовать URL для того, чтобы наше правило сработало. Нам нужно, чтобы оно работало для всех URL, поэтому в поле Pattern пишем ".*"

Секция Conditions в этом правиле нам не нужна, пропускаем её.

В секции Server Variables добавляем запись. В форме записи выбираем ранее созданную переменную "HTTP_Client-IP-Address", а в качестве значения указываем "{REMOTE_ADDR}" — серверную переменную, содержащую IP-адрес, с которого поступил запрос.

В секции Action выберем None и сохраним правило, нажав в правой панели ссылку "Apply"
Перезагружать или перезапускать что-либо нет необходимости. Правило начинает действовать сразу после применения.

Проверяем. Для проверки вызываю из браузера HTTP-сервис типовой Бухгалтерии https://<Хост:Порт>/<ОпубликованнаяИБ>/ru_RU/hs/api/v1/kpi/

Ура, работает! Мы получили в 1С созданный нами заголовок "Client-IP-Address" и он содержит IP-адрес, с которого я выполнял проверку.

Вместо выполнения всех вышеописанных интерактивных действий можно разместить в корне файлового расположения дефолтного сайта ("C:inetpubwwwroot", если не менялись настройки по умолчанию) файл web.config со следующим содержанием. Или добавить в секцию <system.webServer> файла, если он уже существует.

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Add Client-IP-Address header">
<match url=".*" />
<serverVariables>
<set name="HTTP_Client-IP-Address" value="{REMOTE_ADDR}" />
</serverVariables>
<action type="None" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>

 

Но, пожалуй, это еще не всё.

Используем заголовок X-Forwarded-For

Научим наш веб-сервер заполнять IP-адрес клиента в зависимости от того, есть ли в запросе заголовок  X-Forwarded-For. Этот заголовок мы получим как результат настройки собственного фронтенд-вебсервера, или если запрос прошел через какие-то сторонние прокси-серверы до попадания на наш веб-сервер.

В общем случае, в заголовке X-Forwarded-For может быть список IP-адресов, разделенный запятыми. При прохождении запроса через цепочку прокси-серверов, каждый сервер добавляет в этот заголовок IP-адрес источника. Таким образом, самым первым в списке будет IP-адрес клиента, инициировавшего запрос. Соответственно, нам нужен именно этот первый IP-адрес.

Доработаем правила модуля URL Rewrite на веб-сервере.

В случае, если есть заполненный заголовок X-Forvarded-For, первым IP-адресом из него нужно обновить созданный нами ранее заголовок Client-IP-Address.

Для этого в модуле URL rewrite добавим еще одно правило. Назовем его "X_Forwarded_For to Client-IP-Address header"
Секцию Match URL заполняем так же как и у первого правила. Так же поступаем и с секцией Action.

Раскрываем секцию Conditions. Значение Logical grouping можем оставить любое, т.к. у нас будет только одно условие.
Добавляем условие.
Значение Condition input: "{HTTP_X_Forwarded_For}"
Check if input string: Matches the Pattern
Pattern: "(([0-9]{1,3}.){3}[0-9]{1,3})"

В данном условии проверяем есть ли в значении заголовка X_Forwarded_For строка, удовлетворяющая шаблону IP-адреса и сохраняем эту строку для дальнейшего использования, обрамляя её скобками.

 

 Регулярное выражение, использованное для получения IP-адреса

Регулярное выражение "(([0-9]{1,3}.){3}[0-9]{1,3})", использованное мною в примере, не является идеальным для проверки IP-адреса, т.к. вычленит невалидное "934.448.784.432" из строки 4266934.448.784.43254325, и приведено в демонстрационных целях, чтобы не усложнять пример. Оно выделяет содержимое исходя из шаблона "три цифры — точка — три цифры — точка — три цифры — точка — три цифры"
Принимая во внимание, что вряд ли в этом заголовке будут присутствовать строки, не являющиеся IP-адресами, такое выражение вполне можно использовать. 
Сторонникам перфекционизма могу предложить такой вариант регулярного выражения, учитывающий что числа в IP-адресе не могут превышать 255:
((?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?))

Переходим к секции Server Variables и добавляем новую строку.
Выбираем ту же переменную "HTTP_Client-IP-Address", что добавляли для первого условия.
В поле Value пишем "{C:0}", что означает использование первого совпадения из секции Conditions, т.е. полученный из заголовка IP-адрес.

В итоге получаем следующий алгоритм:

Правила срабатывают в порядке их размещения.
Первым правилом наш заголовок заполняется значением серверной переменной REMOTE_ADDR, содержащей IP-адрес клиента, от которого получен запрос.
Затем второе правило, при существовании заголовка X_Forwarded_For и условии что в его содержимом находятся удовлетворяющие выражению данные, т.е. IP-адрес, обновляет наш кастомный  заголовок.

У меня сейчас нет инсталляции с фронтенд-сервером, на которой можно было бы организовать реальную проверку, поэтому я буду использовать расширение для Chrome, позволяющее добавить к запросам такой заголовок с произвольным заполнением https://chrome.google.com/webstore/detail/x-forwarded-for-header/hkghghbnihliadkabmlcmcgmffllglin.

Для проверки вызываю из браузера тот же самый HTTP-сервис типовой Бухгалтерии https://<Хост:Порт>/<ОпубликованнаяИБ>/ru_RU/hs/api/v1/kpi/
Но теперь в http-заголовках присутствует X_Forwarded_For, заполненный значением "40.112.72.205,78.129.196.11".

Как видим, в нашем кастомном заголовке Client-IP-Address оказался первый IP-адрес из цепочки присутствующих в X-Forwarded-For.
 

Содержимое файла web.config после настройки второго правила будет иметь следующий вид:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="REMOTE_ADDR to Client-IP-Address header">
<match url=".*" />
<serverVariables>
<set name="HTTP_Client-IP-Address" value="{REMOTE_ADDR}" />
</serverVariables>
<action type="None" />
<conditions logicalGrouping="MatchAny">
</conditions>
</rule>
<rule name="X_Forwarded_For to Client-IP-Address header">
<match url=".*" />
<conditions logicalGrouping="MatchAny">
<add input="{HTTP_X_Forwarded_For}" pattern="(([0-9]{1,3}.){3}[0-9]{1,3})" />
</conditions>
<serverVariables>
<set name="HTTP_Client-IP-Address" value="{C:0}" />
</serverVariables>
<action type="None" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>

 

Пара слов о синтаксисе URL Rewrite

На официальном сайте с документацией приведено немало примеров, но найти информацию об используемом синтаксисе оказалось непросто. Поэтому позволю себе продублировать некоторые моменты здесь.

Секция Server Variables (элемент <serverVariables>) предназначена для определения серверных переменных и http-заголовков, которые требуется установить или изменить. Действие будет произведено только если запрос отвечает шаблону, установленному в секции Match URL и выполняются условия, назначенные в секции Conditions.

В поле Value можно использовать как простую строку, так и "ссылки" на серверные переменные, http-заголовки, и обратные ссылки на результаты регулярных выражений, использованных в других секциях.

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

Имя серверной переменной указывается "как есть". В приведенном примере использовались указание серверной переменной REMOTE_ADDR — {REMOTE_ADDR}.

Для указания http-заголовка используется префикс HTTP_ 

Для указания заголовка http-ответа используется префикс RESPONSE_

Обратные ссылки на результаты работы регулярных выражений секции Conditions обозначаются {C:n}, секции Match URL — {R:n}

Более подробно — в документации: URL Rewrite Module 2.0 Configuration Reference (english).

 

5 Comments

  1. Steelvan

    Плюсую за слово «насущной», вместо «актуальной».

    Минусую за слово «кастомного», вместо «самодельного»

    🙂

    Reply
  2. -vito-

    (1) Игорь,

    Вы вероятно не поверите, но сначала в черновиках было «самодельный», потом оно смешалось с «кастомным», в конце осталось только то что осталось 🙂

    Я еще с десяток ошибок нашел после отправки статьи на модерацию (ой, а можно такое использовать?) . 🙂

    Reply
  3. sergvagner2018

    (2) в данном случае содержание статьи никак не пострадало.

    Звезда отправлена!

    Reply
  4. Sibars

    Спасибо за дополнение, однозначно, плюс)

    Reply
  5. Cyberhawk
    X-Forvarded-For
    Reply

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *