Возможности Angie в качестве веб‑сервера и обратного прокси довольно известны. Но кроме работы на уровне L7 (HTTP), мы можем применять его на L4 (TCP и UDP) в качестве балансировщика и прокси‑сервера. Именно об этой функциональности мы и поговорим в сегодняшней статье.

Навигация по циклу

  1. Почему стоит переходить на Angie.

  2. Установка Angie из пакетов и в докере.

  3. Переезд с Nginx на Angie. Пошаговая инструкция.

  4. Настройка location в Angie. Разделение динамических и статических запросов.

  5. Перенаправления в Angie: return, rewrite и примеры их применения.

  6. Сжатие текста в Angie: статика, динамика, производительность.

  7. Серверное кэширование в Angie: тонкости настройки.

  8. Настройка TLS в Angie: безопасность и скорость.

  9. Настройка Angie в роли обратного HTTP-прокси.

  10. Балансировка нагрузки для HTTP(S) в Angie.

  11. Мониторинг Angie с помощью Console Light и API.

  12. Балансировка и проксирование L4-трафика в Angie.

  13. Клиентское кэширование в Angie.

  14. Динамические группы проксируемых серверов в Angie.

  15. Мониторинг Angie с Prometheus и Grafana.

  16. Отказоустойчивый кластер Angie с VRRP и Keepalived.

  17. Контроль доступа в Angie.

  18. Аутентификация клиентов в Angie с помощью TLS-сертификатов.

  19. Кастомизация Angie (njs, Lua, Perl).

  20. Запуск CGI-скриптов в Angie.

  21. Защита от DoS-атак в Angie стандартными модулями.

  22. Защита от DoS-атак в Angie (дополнительные средства).

Видеоверсия

Для вашего удобства подготовлена видеоверсия этой статьи, доступна на Rutube, VKVideo и YouTube.

Отличие модулей HTTP и Stream

Надеюсь, вы использовали Nginx или Angie в роли HTTP‑сервера, так как мы будем отталкиваться от конфигурации сервера в контексте HTTP‑модуля. Все директивы для работы на уровне HTTP указываются в рамках блока http. Для L4 используется отдельный модуль и соответствующий блок конфигурации: stream.

Ключевое отличие модулей HTTP и stream — работа на разных уровнях модели OSI (Open Systems Interconnection). Модуль HTTP относится к L7 — уровню приложения и реализует HTTP‑протокол. При работе модуля http сервер оперирует отдельными HTTP‑запросами, формируя ответ в соответствии с конфигурацией.

Модуль stream работает на уровне L4 — транспортном уровне, реализуя проксирование протоколов TCP и UDP. Для модуля stream понятие «запрос» в общем случае отсутствует. Можно считать, что модуль stream не пытается распознавать протоколы выше L4 и содержимое трафика остаётся черным ящиком. Исключение составляют модули с ограниченной возможностью разбора протоколов уровня приложения (MQTT Preread, SSL Preread, RDP Preread). Поэтому в модуле stream обычно оперируют термином «соединение», а не «запрос».

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

Важно не путать одноимённые модули и переменные из http‑контекста и stream‑модули. Они могут иметь одинаковые директивы, но при этом иметь различие в доступных параметрах и вариантах применения. В документации Angie модули разделены по контекстам: «HTTP‑модуль» и «Потоковый модуль». Алфавитные указатели директив и переменных также указывают на контекст.

Проксирование TCP и UDP-приложений

Начнём изучение потокового модуля (stream) с простой задачи: нужно проксировать TCP‑приложение (MySQL) через Angie для реализации ограничений по количеству подключений и списка доступа на основе IP‑адреса клиента.

Первое, что необходимо сделать, определить сервер для привязки к TCP‑сокету в контексте stream:

stream {
  server {
      listen 6306;
  }
}

В этом примере мы создали сервер, слушающий TCP‑порт 6306. Для директивы listen доступен широкий список дополнительных параметров. Например, можно включить создание сокетов для каждого рабочего процесс�� (reuseport) или указать режим SSL (ssl). Варианты указания сокета такие же, как и для HTTP‑режима (IP с портом, только порт, диапазон портов, отдельные сокеты для IPv4 и IPv6, UNIX‑сокеты).

Второе, что мы укажем, — на какой сервер нужно перенаправлять трафик. За это отвечает модуль proxy и его директива proxy_pass

server {
  listen 6306;
  proxy_pass 192.168.1.2:3306;
}

Обратите внимание, что схема (http(s)://) перед адресом сервера не указывается. 

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

stream {
  limit_conn_zone $binary_remote_addr zone=addr:10m;
  
  server {
    listen 6306;
    proxy_pass 192.168.1.2:3306;
    
    limit_conn addr 1;
    
    allow 192.168.1.0/24;
    deny  all;
  }
}

Итак, все требования учтены: мы ограничиваем количество подключений до одного на каждый клиентский IP‑адрес и разрешаем доступ только из подсети 192.168.1.0/24. В отличие от HTTP‑модуля, в stream по умолчанию не ведётся access.log. Это можно исправить, определив формат лога и подключив его в stream‑контексте.

stream {
    log_format basic '$remote_addr [$time_local] '
                 '$protocol $status $bytes_sent $bytes_received '
                 '$session_time ' "$upstream_addr";
    access_log /var/log/angie/stream-access.log basic;
}

Теперь в stream-access.log будут попадать записи следующего вида:

192.168.122.1 [08/Aug/2025:17:18:57 +0300] TCP 200 180902 3535 301.392 127.0.0.1:82

Такие записи появляются после завершения сессии работы с клиентом и показывают общую статистику трафика (напоминаю, что запросов здесь нет). Для удобства введены искусственные коды статуса сессии ($status), значения которых выбраны по аналогии с HTTP‑кодами ответа: 200, 400, 403, 500, 502, 503.

Проксирование UDP‑сервисов в целом похоже на работу с TCP, но имеет свои нюансы. Например, для отслеживания UDP‑сессий требуется указание параметра reuseport. Также можно явно указать количество ожидаемых ответов от сервера для завершения UDP‑сессии. Рассмотрим следующий пример:

server {
	listen 553;
	listen 553 udp reuseport;
	proxy_pass 192.168.1.1:53;
	proxy_responses 1;
}

В приведённой конфигурации Angie принимает TCP и UDP‑трафик на порт 553 и проксирует его на сервер 192.168.1.1:53. При этом здесь явно указано, что мы ожидаем ровно один ответ от сервера в случае обращения по UDP. Также стоит упомянуть, что трафик не подвергается конвертации из TCP в UDP и обратно. Для проксирования всегда используется протокол, с которым клиент подключился к Angie.

Работа с TLS

При работе на L4 доступен полный спектр возможностей Angie для TLS. Разберём типичную задачу: нужно обернуть в TLS открытый трафик приложения (реализовать TLS‑терминацию) для безопасной передачи в публичных сетях. Сделать это можно следующим образом.

stream {
  server {
  	listen 443 ssl;
  	ssl_certificate /etc/angie/ssl/1.crt;
  	ssl_certificate_key /etc/angie/ssl/1.key;
  	proxy_pass 192.168.1.2:80;
  }
}

Как и в HTTP‑модуле, достаточно включить TLS в директиве listen параметром ssl и указать сертификат с ключом. При этом уже открытый трафик проксируется на сервер.

В примере выше мы не использовали определение сервера по доменному имени (нет директивы server_name). Допустим, стоит задача распределять трафик на 443 порту на различные приложения: часть доменов будет обслуживаться в самом Angie в модуле HTTP, а часть проксироваться на другие сервера на L4. Решение такой задачи возможно; достаточно использовать директивы server_name и pass.

http {
    server {
        listen 8000;
        location / {
            root html;
        }
    }
}

stream {
    server {
        listen 443 ssl;
        server_name test.ru;
        ssl_certificate     /etc/angie/ssl/1.crt;
        ssl_certificate_key /etc/angie/ssl/1.key;
        pass 127.0.0.1:8000;
    }
    server {
        listen 443 ssl;
        server_name serv.test.ru;
        ssl_certificate     /etc/angie/ssl/1.crt;
        ssl_certificate_key /etc/angie/ssl/1.key;
        proxy_pass 192.168.1.2:9000;
    }
}

В примере выше Angie открывает 443 TCP‑сокет для приёма соединений и терминации TLS. На основе данных из расширения SNI определяется имя сервера, и для домена test.ru сработает директива pass. Эта директива передаёт клиентское соединение в другой модуль сервера (HTTP в нашем случае), и обработка запросов будет происходить в модуле HTTP на сервере c сокетом 8000. Для домена serv.test.ru будет работать TLS‑терминация и L4-проксирование на сокет 192.168.1.2:9000.

А что, если терминация на уровне Angie не нужна, но мы всё ещё хотим распределять трафик на основе доменного имени? Для этого существует модуль ssl_preread. Он реализует фазу предварительного чтения запроса и может получать версию TLS, доменное имя и список протоколов по ALPN (например, h2 или h3). Эти значения записываются в соответствующие переменные: $ssl_preread_protocol, $ssl_preread_server_name и $ssl_preread_alpn_protocols. Более того, на основе этих переменных можно настроить гибкую маршрутизацию трафика. Рассмотрим пример:

map $ssl_preread_protocol $upstream {
    ""        192.168.1.2:22;
    "TLSv1.2" 192.168.1.2:443;
    default   192.168.1.2:443;
}

server {
	listen 8443;
	ssl_preread on;
	proxy_pass $upstream;
}

Здесь нет TLS‑терминации (трафик передаётся «как есть»), но включен режим ssl_preread. В блоке map создаётся переменная $upstream, которая зависит от $ssl_preread_protocol. Если версия прокола имеет пустое значение, то трафик проксируется на SSH‑сервер (192.168.1.2:22); если там TLSv1.2 или что‑нибудь еще, то трафик проксируется на 192.168.1.2:443. Такие же схемы можно делать на основе имени сервера или протоколов в расширении ALPN.

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

server {
	listen 443 ssl;
	ssl_certificate /etc/angie/ssl/1.crt;
	ssl_certificate_key /etc/angie/ssl/1.key;
	proxy_ssl on;
	proxy_ssl_verify on;
    proxy_ssl_name test.ru;
    proxy_ssl_server_name on;
	proxy_ssl_trusted_certificate /etc/angie/ssl/trust.crt;
	proxy_pass 192.168.1.2:443;
}

Пример показывает проксирование на сервер 192.168.1.2:443 c использованием TLS. При этом происходит проверка сертификата проксируемого сервера (proxy_ssl_verify) с учетом имени test.ru (proxy_ssl_name) и доверенного сертификата (proxy_ssl_trusted_certificate).

Наконец, мы добрались до возможностей балансировки TCP‑ и UDP‑приложений.

Балансировка на транспортном уровне

Если вы знакомы с балансировкой HTTP‑приложений в Angie, переход на L4 будет простым. Поддерживаются все алгоритмы и режимы балансировки, кроме ip_hash и режима sticky cookie

Список доступных возможностей (пометка PRO означает доступность только в коммерческой версии Angie PRO):

  • алгоритм Round Robin;

  • алгоритм Least Connection;

  • алгоритм Hash;

  • алгоритм Random Least Connection;

  • пассивные проверки серверов (max_fails);

  • опции slow_start, resolve для серверов;

  • режим Sticky Route;

  • алгоритм Least Time (random) (PRO);

  • алгоритм Feedback (PRO);

  • режимы Sticky Learn и Sticky Learn Remote action (PRO);

  • активные проверки серверов (PRO);

  • опции state, drain, backup_switch для серверов (PRO).

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

stream {
  upstream backend {
    server 192.168.0.1:8000;
    server 192.168.0.2:8000;
  }
  server {
  	listen 9090;
  	proxy_pass backend;
  }
}

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

Для указания нужного алгоритма балансировки используются директивы модуля upstream: hash, random, least_conn, feedback (PRO), least_time (PRO). Также не забываем про параметры директивы server. Допустим, нам необходимо реализовать балансировку методом least connections с функцией привязки сессий по доменам и ограничением количества подключений к каждому серверу. Получим следующую конфигурацию:

map $ssl_preread_server_name $route {
  test.ru            a;
  test2.ru           b;
  default           "";
}

upstream backend {
  least_conn;
  server 192.168.0.1:443 sid=a max_fails=2 fail_timeout=10s max_conns=100;
  server 192.168.0.2:443 sid=b max_fails=2 fail_timeout=10s max_conns=100;
  sticky route $route;
}

server {
  listen 443;
  ssl_preread on;
  proxy_pass backend;
}

Обратите внимание, что в конфигурации выше для серверов установлены параметры max_fails и fail_timeout, которые отвечают за пассивные проверки. В контексте L4-балансировки max_fails контролирует количество попыток подключения к серверу (а не запросов, как в HTTP‑модуле), а fail_timeout как обычно определяет время, за которое эти попытки должны быть сделаны для признания сервера нерабочим (до прохождения еще одного периода fail_timeout).

Здесь можно отметить еще одно отличие от балансировки HTTP‑сервисов. Настройка переключения на следующий сервер (proxy_next_upstream) здесь имеет булевый тип: включена или выключена. При включении (по умолчанию) переход на следующий сервер происходит, если подключиться к выбранному невозможно.

С другой стороны, есть возможности, недоступные в HTTP‑модуле: ограничение скорости чтения ответа от сервера (proxy_download_rate) и ограничение скорости чтения данных от клиента (proxy_upload_rate).

Итоги

Мы разобрали основные сценарии использования Angie в качестве TCP и UDP‑прокси и балансировщика; нашли отличия модуля stream от модуля HTTP. Можно сказать, что возможности Angie на L4 практически сравняли��ь с возможностями модуля HTTP, за исключением естественных ограничений при работе на более низком уровне.

Следующая статья цикла: Клиентское кэширование в Angie.