Jump to content
Калькуляторы

Вопрос по проксированию в nginx

Возникла у меня тут специфичная задача.

Есть веб-сервер с REST API, на котором потребовалось контролировать его использование (внешними пользователями), а штатные логи нужную информацию не сохраняют.

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

Мне нужно перехватывать обращения к ресурсу /method1?p1=v1&p2=v2 и писать в логи следующую информацию:

- дата/время, IP-адрес посетителя, адрес ресурса (method1);

- параметры запроса p1 и p2 (точнее значения этих параметров);

- заголовок User-Agent;

- декодированное имя пользователя из заголовка Authorization;

- значения заголовков ClientType и ClientId;

- если возможно, то ответный status code;

- если возможно, то содержимое некоторых узлов в возвращаемом XML;

- было бы хорошо, если логи писать не в один общий файл, а в файл с именем пользователя (из заголовка авторизации)

Возможно, нужно будет также подменять Host в запросе, пересылаемом на проксируемый сервер.

 

Насколько я понимаю, это все реализуемо штатными возможностями nginx, директивами proxy_pass и access_log.

Не ткнете в ссылку, где был бы пример подобной конфигурации?

Я пока не понимаю, как мне декодировать заголовки (имя пользователя) и как логгировать ответы.

Share this post


Link to post
Share on other sites

Я решал задачу логгирования для дебага 

https://noname.com.ua/mediawiki/index.php/Nginx_Log_RequestHeaders

 

Штатными средствами нельзя залоггировать заголовки о которых вы не знаете заранее

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Вы хотите полный дебаг включая ответы от сервера. nginx тут не поможет.

простой вариант запустить tcpdump и писать в файл.

Более продвинутый вариант начать использовать log-коллекторы. Можно взять тот же elasticsearch с агентами filebeat (файловые логи), packetbeat (сетевой трафик). Выглядят страшно, но на самом деле несложно там. 

Edited by naves

Share this post


Link to post
Share on other sites

В 03.10.2022 в 01:58, naves сказал:

Вы хотите полный дебаг включая ответы от сервера.

Это пожелание.

В первую очередь нужны все же запросы.

 

В 02.10.2022 в 15:49, sirmax сказал:

Штатными средствами нельзя залоггировать заголовки о которых вы не знаете заранее

Заголовки я знаю, но заголовки могут отсутствовать, тогда нужно писать в лог что-нибудь понятное (например прочерк).

Share this post


Link to post
Share on other sites

дата/время, IP-адрес посетителя, адрес ресурса (method1);

$time_local $remote_addr $uri

- параметры запроса p1 и p2 (точнее значения этих параметров);

$arg_p1 $arg_p2

- заголовок User-Agent;

$http_user_agent

- декодированное имя пользователя из заголовка Authorization;

$remote_user

- значения заголовков ClientType и ClientId;

$http_clienttype $http_clientid

- если возможно, то ответный status code;

$status

- если возможно, то содержимое некоторых узлов в возвращаемом XML;

 

- было бы хорошо, если логи писать не в один общий файл, а в файл с именем пользователя (из заголовка авторизации)

 

access_log "/logs/blablabla/custom_${remote_user}_log";

но тут могут попихать всяких ../../..//etc/passwd тоесть надо бы сначала прогнать через фильтр

 

map $remote_user $t_remote_user {

 default 'unknown';

 ~^$[a-zA-Z0-9_-]{1,99}  $remote_user;

}

 

и в имени уже $t_remote_user

 

НО должны быть права у воркера на создание файлов с логами.

Share this post


Link to post
Share on other sites

В 03.10.2022 в 17:28, st_re сказал:

access_log "/logs/blablabla/custom_${remote_user}_log"

Не был уверен, что на этот момент известны заголовки запроса.

 

В 03.10.2022 в 17:28, st_re сказал:

map $remote_user $t_remote_user

А вот это отлично, спасибо.

 

 

Share this post


Link to post
Share on other sites

лог пишется в момент окончания обработки запроса..

если в пути на лог файл есть переменные. то он открывается (создаётся) в момент записи лога

 

перебирая имена юзеров можно будет забить ФС околопустыми файлами...

Share this post


Link to post
Share on other sites

В 30.09.2022 в 09:26, alibek сказал:

Я пока не понимаю, как мне декодировать заголовки (имя пользователя) и как логгировать ответы.

Если именно нгинкс, можно посмотреть в сторону lua. Или написать свой прокси на знакомом языке

Share this post


Link to post
Share on other sites

19 часов назад, naves сказал:

Вы хотите полный дебаг включая ответы от сервера. nginx тут не поможет.

простой вариант запустить tcpdump и писать в файл.

Более продвинутый вариант начать использовать log-коллекторы. Можно взять тот же elasticsearch с агентами filebeat (файловые логи), packetbeat (сетевой трафик). Выглядят страшно, но на самом деле несложно там. 

Все делается на нжинксе с луа

 

дам пример как дойду до компа, с телефона неудобно 

Share this post


Link to post
Share on other sites

Частично заработало, но никак не получается решить один момент.

 

Речь идет про проксирование сервера Макроскоп.

Проксирование нужно для того, чтобы контролировать использование учетных записей — штатных инструментов в Макроскопе для этого нет.

Поэтому прокси-сервер будет логгировать доступ, а отдельный скрипт будет анализировать логи и выдавать уведомления при срабатывании.

Для проксирования использую nginx примерно с такой конфигурацией:

proxy_max_temp_file_size  0;
proxy_pass_request_headers  on;
proxy_buffering  off;

server {
    listen       80;
    listen       8080;

    location / {
        proxy_pass http://10.102.0.41:8080;
    }

    location = /configex {
        access_log /data/cybereye/log/macroscop.$remote_user_filter.connect.$logdate.log  macroscop_connect;
        proxy_pass http://10.102.0.41:8080/configex?$query_string;
    }

    location = /command {
        access_log /data/cybereye/log/macroscop.$remote_user_filter.command.$logdate.log  macroscop_command;
        proxy_pass http://10.102.0.41:8080/command?$query_string;
    }

    location = /connecttosmartassistant {
        access_log /data/cybereye/log/macroscop.$remote_user_filter.assistant.$logdate.log  macroscop_assistant;
        proxy_pass http://10.102.0.41:8080/connecttosmartassistant?$query_string;
    }

    location = /mobile {
        access_log /data/cybereye/log/macroscop.$remote_user_filter.mobile.$logdate.log  macroscop_mobile;
        proxy_pass http://10.102.0.41:8080/mobile?$query_string;
    }

    location = /mobilepush {
        access_log /data/cybereye/log/macroscop.$remote_user_filter.push.$logdate.log  macroscop_push;
        proxy_pass http://10.102.0.41:8080/mobilepush?$query_string;
    }

    location = /ptz {
        access_log /data/cybereye/log/macroscop.$remote_user_filter.ptz.$logdate.log  macroscop_ptz;
        proxy_pass http://10.102.0.41:8080/ptz?$query_string;
    }

}

 

При такой конфигурации я успешно подключаюсь к Макроскопу (как мобильным приложением, так и веб-версией), получаю список камер. Но ни одна из камер не работает, не отдает видеопоток.
Скорее всего это связано с тем, что видеопотоки тоже должны проксироваться.
 
В дампе трафика (при нормальном подключении к Макроскопу, не через прокси) происходит следующее.
 
Вначале выполняется авторизация через configex и запрашиваются серверные данные (списки серверов и камер).
 
Затем выполняется mobilepush, если это мобильное устройство.
 
Затем к каждой камере (из ранее полученного списка) отправляется запрос mobile и в ответ приходит примерно такое содержание:
HTTP/1.1 200 OK
Cache-Control: no-cache,no-store,max-age=0,must-revalidate
Pragma: no-cache
Transfer-Encoding: chunked
Expires: Sat, 26 Jul 1997 05:00:00 GMT
Server: Microsoft-HTTPAPI/2.0 Microsoft-HTTPAPI/2.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Date: Thu, 29 Sep 2022 11:23:39 GMT

1000
--myboundary
Content-Type: image/jpeg
Timestamp: 29.09.2022 11:23:40
BinaryTimestamp: 5249686492630663846
ZonedTimeStamp: 29.09.2022 11:23:40.327 ZZZZ
Content-Length: 45396
...

 

 

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

Share this post


Link to post
Share on other sites

В 11.10.2022 в 11:58, alibek сказал:

Частично заработало, но никак не получается решить один момент.

 

Речь идет про проксирование сервера Макроскоп.

Проксирование нужно для того, чтобы контролировать использование учетных записей — штатных инструментов в Макроскопе для этого нет.

Поэтому прокси-сервер будет логгировать доступ, а отдельный скрипт будет анализировать логи и выдавать уведомления при срабатывании.

Для проксирования использую nginx примерно с такой конфигурацией:

proxy_max_temp_file_size  0;
proxy_pass_request_headers  on;
proxy_buffering  off;

server {
    listen       80;
    listen       8080;

    location / {
        proxy_pass http://10.102.0.41:8080;
    }

    location = /configex {
        access_log /data/cybereye/log/macroscop.$remote_user_filter.connect.$logdate.log  macroscop_connect;
        proxy_pass http://10.102.0.41:8080/configex?$query_string;
    }

    location = /command {
        access_log /data/cybereye/log/macroscop.$remote_user_filter.command.$logdate.log  macroscop_command;
        proxy_pass http://10.102.0.41:8080/command?$query_string;
    }

    location = /connecttosmartassistant {
        access_log /data/cybereye/log/macroscop.$remote_user_filter.assistant.$logdate.log  macroscop_assistant;
        proxy_pass http://10.102.0.41:8080/connecttosmartassistant?$query_string;
    }

    location = /mobile {
        access_log /data/cybereye/log/macroscop.$remote_user_filter.mobile.$logdate.log  macroscop_mobile;
        proxy_pass http://10.102.0.41:8080/mobile?$query_string;
    }

    location = /mobilepush {
        access_log /data/cybereye/log/macroscop.$remote_user_filter.push.$logdate.log  macroscop_push;
        proxy_pass http://10.102.0.41:8080/mobilepush?$query_string;
    }

    location = /ptz {
        access_log /data/cybereye/log/macroscop.$remote_user_filter.ptz.$logdate.log  macroscop_ptz;
        proxy_pass http://10.102.0.41:8080/ptz?$query_string;
    }

}

 

При такой конфигурации я успешно подключаюсь к Макроскопу (как мобильным приложением, так и веб-версией), получаю список камер. Но ни одна из камер не работает, не отдает видеопоток.
Скорее всего это связано с тем, что видеопотоки тоже должны проксироваться.
 
В дампе трафика (при нормальном подключении к Макроскопу, не через прокси) происходит следующее.
 
Вначале выполняется авторизация через configex и запрашиваются серверные данные (списки серверов и камер).
 
Затем выполняется mobilepush, если это мобильное устройство.
 
Затем к каждой камере (из ранее полученного списка) отправляется запрос mobile и в ответ приходит примерно такое содержание:
HTTP/1.1 200 OK
Cache-Control: no-cache,no-store,max-age=0,must-revalidate
Pragma: no-cache
Transfer-Encoding: chunked
Expires: Sat, 26 Jul 1997 05:00:00 GMT
Server: Microsoft-HTTPAPI/2.0 Microsoft-HTTPAPI/2.0
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Date: Thu, 29 Sep 2022 11:23:39 GMT

1000
--myboundary
Content-Type: image/jpeg
Timestamp: 29.09.2022 11:23:40
BinaryTimestamp: 5249686492630663846
ZonedTimeStamp: 29.09.2022 11:23:40.327 ZZZZ
Content-Length: 45396
...

 

 

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

ДО этого должен быть запрос.. (ну и убедиться, что запрос идёт туда же, куда и остальной трафик.)

 

ну и да, открыть нетворк монитор в браузере, и смотреть что посылается и что в ответ.

Share this post


Link to post
Share on other sites

Это не через браузер происходит, а через мобильное приложение.

Посмотреть будет сложно, разве что трафик сниффером записать.

Share this post


Link to post
Share on other sites

В 11.10.2022 в 11:58, alibek сказал:

так и веб-версией

 

В 12.10.2022 в 00:46, alibek сказал:

Это не через браузер происходит

что-то не сходится

 

В 12.10.2022 в 00:46, alibek сказал:

разве что трафик сниффером записать

тоже вариант

Share this post


Link to post
Share on other sites

В 11.10.2022 в 11:58, alibek сказал:

Не подскажите, что нужно исправить, чтобы проксирование заработало?

попробуйте proxy_http_version 1.1;

Share this post


Link to post
Share on other sites

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

Share this post


Link to post
Share on other sites

Наиболее вероятно что откуда лится видео - отдаётся отдельной ссылкой, целиком url, и там либо ip либо хост, который разумеется за пределами локалки не достижим.

Share this post


Link to post
Share on other sites

В 11.10.2022 в 13:58, alibek сказал:

Не подскажите, что нужно исправить, чтобы проксирование заработало?

1. для начала настройте самое тупое проксирование и убедитесь что оно работает

 

   location / {
        proxy_pass http://10.102.0.41:8080;
    }
 

пока не заработает, дальнейшие упражнения не имеют смысла.

 

2. строчка   "proxy_pass http://10.102.0.41:8080/configex?$query_string;" избыточна и неверна, достаточно "proxy_pass http://10.102.0.41:8080;". uri и request_uri подставляются автомагически:

 

If proxy_pass is specified without a URI, the request URI is passed to the server in the same form as sent by a client when the original request is processed, or the full normalized request URI is passed when processing the changed URI

 

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

location / {

  proxy_pass http://10.102.0.41:8080;

  location /blabla {
    access_log blablabla;
  }
}

Share this post


Link to post
Share on other sites

В 14.10.2022 в 14:26, boco сказал:

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

О, спасибо, так намного удобнее.

 

В 14.10.2022 в 14:26, boco сказал:

для начала настройте самое тупое проксирование и убедитесь что оно работает

Так оно и не работает.

Возможно потому, что сервер отдает потоки в бесконечной сессии.

Share this post


Link to post
Share on other sites

Обновленный конфиг:

Скрытый текст
    proxy_max_temp_file_size  0;
    proxy_pass_request_headers  on;
    proxy_buffering  off;

    map $remote_user $remote_user_filter {
        default '_unknown';
        ~^[a-zA-Z0-9_-]+$  $remote_user;
        * '_invalid';
    }

    map $time_iso8601 $logdate {
        default '0000-00-00';
        "~^(\d{4})-(\d{2})-(\d{2})" $1-$2-$3;
    }

    log_format macroscop
    '$time_iso8601: '
    '#$status '
    '$remote_addr ($remote_user) '
    '[$connection_requests:$connection] '
    '"$request"'
    ' ua="$http_user_agent"'
    ' recv=$request_length'
    ' sent=$bytes_sent'
    ' body=$body_bytes_sent'
    ;
...
server {
...
    error_log   /log/error.log notice;
    access_log  off;

    location / {
        root   /site;
        proxy_pass http://10.102.0.41:8080;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        access_log /log/macroscop.log  macroscop;

        location = /configex {
            access_log /log/macroscop.$remote_user_filter.connect.$logdate.log  macroscop_connect;
        }
        location = /command {
            access_log /log/macroscop.$remote_user_filter.command.$logdate.log  macroscop_command;
        }
        location = /connecttosmartassistant {
            access_log /log/macroscop.$remote_user_filter.assistant.$logdate.log  macroscop_assistant;
        }
        location = /mobile {
            access_log /log/macroscop.$remote_user_filter.mobile.$logdate.log  macroscop_mobile;
        }
        location = /mobilepush {
            access_log /log/macroscop.$remote_user_filter.push.$logdate.log  macroscop_push;
        }
        location = /ptz {
            access_log /log/macroscop.$remote_user_filter.ptz.$logdate.log  macroscop_ptz;
        }

    }

}

 

Вложенные location срабатывают, потому что запросы пишутся в соответствующие файлы.

Но почему-то везде сервер возвращает ошибку 404.

В error.log примерно так:

Цитата

2022/10/14 15:48:26 [error] 27240#27240: *11 open() "/site/configex" failed (2: No such file or directory), client: xx.xx.xx.80, server: xxxx, request: "GET /configex?responsetype=json HTTP/1.1", host: "xxxx", referrer: "http://xxxx/web/index.html?v=3.6.69"

Такое впечатление, как будто proxy_pass не срабатывает.

На сервере несколько интерфейсов, одновременно смотреть их через tcpdump нельзя.

Если запускать tcpdump на внешнем интерфейсе, то вижу то же, что в логах — внешний http-запрос, на который сервер отвечает кодом 404.

Если запускать tcpdump на интерфейсе 10.102.0.41, то там тишина (на порту 8080), я не вижу, чтобы шли какие-то обращения.

Как бы проверить, почему проксирование не работает?

Share this post


Link to post
Share on other sites

В 14.10.2022 в 16:01, alibek сказал:

одновременно смотреть их через tcpdump нельзя

-i any

До чего техника дошла.

 

Да, судя по трафику, все что соответствует вложенным location — не проксируется.

Все что под них не попадает (то есть соответствует корневому location /) проксируется нормально.

 

P.S. Если продублировать proxy_pass в каждом вложенном location, то возвращаюсь к тому, с чего начал — подключаюсь, получаю конфигурацию с сервера, но видеопотоки не работают.

Но зато увидел в tcpdump, что нужно проксировать веб-сокет.

Хотя бы ясно теперь, в какую сторону копать.

 

P.P.S. Йес! Правда изображение тупит, но это уже настройки и тюнинг.

Share this post


Link to post
Share on other sites

В 14.10.2022 в 18:01, alibek сказал:

Такое впечатление, как будто proxy_pass не срабатывает.

я был неправ. нужно добавлять proxy_pass во вложенные локации, эта директива не наследуется, в отличие от proxy_set_header

Share this post


Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.