Nginx/Caddy/HAProxy как входная точка; WAF — ModSecurity. Альтернативы типа CrowdSec/open-appsec 列印

  • Caddy, HAProxy, Nginx, WAF, nginx, nginx настройка
  • 0

Входная точка — это место, где трафик впервые встречается с вашей инфраструктурой. Здесь решается больше, чем кажется: завершение TLS и распределение нагрузки, разглаживание хвостовых задержек, нормализация заголовков, защита от банальных атак уровня приложений, а иногда и кэш. В наш век «independent cloud» этот слой ещё и финансовый клапан: именно здесь вы уменьшаете платный «выход» из чужих зон, оптимизируете число хопов и экономите CPU на приложениях. Современный практичный треугольник выглядит так: Nginx, Caddy и HAProxy как варианты входной точки; Web Application Firewall в роли второго кольца обороны — классический ModSecurity с набором правил OWASP CRS или поведенческие альтернативы в стиле CrowdSec и open-appsec. Ниже — как спроектировать систему, чтобы она была быстрой, сопровождаемой и предсказуемой в расходах, и при этом оставалась «вашей», а не зависела от чёрных ящиков.

Начинают обычно с выбора «ядра» входной точки. Nginx — универсальный комбайн, у которого сильная HTTP-часть, понятная прокладка upstream’ов, зрелый кэш и официальные сборки с HTTP/3. Caddy — инженерное минимализм-ориентированное решение, где автоматический TLS и понятная декларативная конфигурация часто важнее предельной гибкости; для команд без выделенных SRE это снижает операционные затраты заметнее, чем любой «оптимизирующий флаг». HAProxy — классическая «бритва Оккама» для L4/L7 балансировки: невероятно быстрый, строгий в отношении таймингов, с мощными health-чеками; за счёт нативных ACL легко вырезает мусор до того, как он коснётся приложений. Разумный подход — не «выбрать одного навсегда», а понимать, какой профиль у каждого и где он окупается. Внутри одного независимого облака легко жить с двумя вкусами: например, внешний периметр на HAProxy ради жёстких таймингов и PROXY-protocol, а дальше — Nginx как HTTP-шлюз с кэшем и удобной статикой. Caddy часто ставят там, где важно, чтобы всё «завелось» за час без баталий с Certbot и нюансами TLS.

Безопасность на входе строится слоями. Первый слой — «чистая» HTTP-нормализация и базовые ограничения частоты, второй — собственно WAF. Исторически ModSecurity воспринимают как стандарт де-факто: движок, набор правил OWASP CRS и коннекторы к веб-серверам. На Apache это ставится просто, на Nginx — через libmodsecurity v3 и модуль-коннектор. Здесь стоит честно произнести вслух то, что обычно прячут под ковёр: сопровождаемость связки «Nginx + ModSecurity v3» зависит от коннектора и сборки, и в некоторых дистрибутивах пакет отстаёт от основного релиза Nginx. Это не блокер, но фактор: крупные команды собирают связку сами или выносят движок WAF за пределы Nginx (SPOE для HAProxy, отдельный фильтрующий слой), чтобы не завязывать обновления TLS-стека и движка правил в один релизный цикл. Альтернативный путь — поведенческие решения: CrowdSec, который видит паттерны агрессии на уровне инфраструктуры и отдаёт вердикт «блокировать» через «баунсеры» для Nginx/HAProxy/Caddy, и open-appsec, который интегрируется в плоскость L7 рядом с Nginx и учится на реальном трафике, сокращая число ручных исключений. В независимом облаке ценность этих решений — не только «умность», но и экономия: меньше ложных срабатываний — меньше пустых ретраев вверх по цепочке и меньше бессмысленного расхода канала.

Практика начинается с минимально достаточной конфигурации. Для Nginx логично включить HTTP/2 и HTTP/3, аккуратно нормализовать заголовки и отдать приложение в upstream. Если у вас уже выкачан сертификат, конфигурация выглядит прямолинейно и без сюрпризов: слушаем 443 по TCP и UDP, рекламируем Alt-Svc, отключаем бессмысленные ciphers и шлём трафик дальше, не забывая о заголовках, которые помогают диагностике и безопасной работе фронтов.

server {
    listen 80;
    server_name edge.example.com;
    return 301 https://edge.example.com$request_uri;
}

server {
    listen 443 quic reuseport;
    listen 443 ssl http2;
    server_name edge.example.com;

    ssl_certificate     /etc/letsencrypt/live/edge.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/edge.example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
    ssl_ciphers         TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256;

    add_header Alt-Svc 'h3=":443"; ma=86400' always;
    add_header Strict-Transport-Security "max-age=31536000" always;
    add_header X-Frame-Options DENY always;
    add_header X-Content-Type-Options nosniff always;

    set_real_ip_from 10.0.0.0/8;
    real_ip_header X-Forwarded-For;

    location / {
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass http://127.0.0.1:8080;
        proxy_read_timeout 60s;
    }
}

Если вы выбираете Caddy ради простоты и автосертификатов, описать ту же самую точку входа можно парой абзацев в Caddyfile. Здесь не нужно думать о cron-обновлениях — Caddy сам ведёт переговоры с ACME, а HTTP/2/3 включены из коробки.

edge.example.com {
    reverse_proxy 127.0.0.1:8080
    header {
        Strict-Transport-Security "max-age=31536000"
        X-Frame-Options "DENY"
        X-Content-Type-Options "nosniff"
    }
    encode zstd gzip
}

HAProxy даёт ощущение настоящего сетевого инструмента. Он легко принимает PROXY-protocol от внешнего балансировщика, быстро переключает бэкенды, жёстко соблюдает тайминги и может фильтровать ещё до того, как дойдём до веб-сервера. Конфигурация на уровне «фронтенд принимает HTTPS, бэкенд — локальный Nginx» выглядит прагматично: включаем ALPN, устанавливаем лимиты на соединение и размер заголовков, добавляем строгие health-чеки.

global
    maxconn 20000
    log 127.0.0.1 local0
    tune.ssl.default-dh-param 2048

defaults
    mode http
    option httplog
    timeout connect 5s
    timeout client  30s
    timeout server  30s

frontend fe_https
    bind :443 ssl crt /etc/ssl/private/edge.pem alpn h2,http/1.1
    http-response set-header Strict-Transport-Security max-age=31536000
    http-request set-header X-Forwarded-Proto https
    http-request set-header X-Forwarded-For %[src]
    use_backend be_app

backend be_app
    server app1 127.0.0.1:8080 check

Теперь пора добавить WAF. Классический путь — ModSecurity с OWASP CRS. На Nginx «правильная» связка — libmodsecurity v3 и nginx-connector. Системный слой — пакет движка и набор правил, на веб-слое — подключение модуля и директивы включения. По-честному стоит зафиксировать две вещи. Во-первых, коннектор под Nginx обновляется отдельным циклом, иногда отстаёт от mainline и требует аккуратности при апдейтах. Во-вторых, правильное место WAF-слоя — максимально близко к границе, но всё же за балансировщиком, который умеет быстро отстреливать явный мусор и не тратит CPU WAF на очевидные «ботинки». В Nginx включение выглядит наглядно: сначала core-конфиг ModSecurity, затем набор CRS и аккуратные исключения под ваше приложение, чтобы не проверять, например, статические файлы.

# В http{} уровне:
modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/main.conf;

# /etc/nginx/modsec/main.conf
SecRuleEngine On
SecRequestBodyAccess On
SecResponseBodyAccess Off
Include "/etc/nginx/modsec/crs-setup.conf"
Include "/etc/nginx/modsec/rules/*.conf"

# Исключим статику из проверки
SecRule REQUEST_URI "@rx \.(css|js|png|jpg|jpeg|gif|webp|svg|woff2?)$" "id:900001,phase:1,pass,ctl:ruleEngine=Off"

Если хочется держать WAF отдельно от TLS-терминации и балансировки, у HAProxy есть SPOE-механизм — асинхронный обмен с внешним процессом-«офицером» принятия решений. Под капотом часто стоит тот же ModSecurity, но теперь обновления движка и набора правил живут своим графиком, не затрагивая точку входа. Это добавляет чуть-чуть сложности в деплой, но резко упрощает сопровождение на длинной дистанции: ядро HAProxy обновляется, когда нужно сетевикам, а движок правил — когда нужно аппсек-команде.

CrowdSec и open-appsec меняют угол зрения: вместо «пакета сигнатур» они смотрят на поведение. CrowdSec собирает примитивы событий (частые 404, странные payload’ы, переборы логинов), прогоняет их через сценарии и отдаёт вердикт «заблокировать» в ту точку входа, где стоит «баунсер»: для Nginx это модуль, для HAProxy — нативный фильтр, для Caddy — плагин. На стороне операций это выглядит удивительно экономно: вы не сидите сутками, выписывая исключения на конкретные эндпоинты, а быстро гасите вал повторяющихся шаблонов «плохого поведения» почти без ложных срабатываний. open-appsec идёт ещё дальше: он интегрируется с Nginx на уровне процесса, строит модель «нормального» трафика и постепенно снижает долю ручных правил. Его сила — в том, что политика адаптируется, а не «замораживается» экземпляром CRS от какого-то дня. Его слабость — всё та же инженерная организационная дисциплина: нужно выделить время на обучение и контроль в первые недели, особенно если трафик неоднородный.

Сеть и финансы нельзя оставлять «на потом». В независимом облаке легко разнести роли: на границе — HAProxy с ALPN, жёсткими таймингами и минимальным логом, следом — Nginx с HTTP/3 и удобной работой с TLS-расширениями, за ним — WAF, который видит уже очищенный поток. Если у вас один узел, порядок можно переставить: Nginx с вшитым WAF, а за ним — приложение. Главное — не забывать о недорогих, но важных деталях: нормализованный Accept-Encoding убирает «взрыв вариантов» и экономит кэш, чёткий лимит на размер заголовков отрезает экзотику сразу, защита от медленных клиентов (slowloris) не даёт одинокому «нехорошему» соединению занимать воркеры часами. На уровне правил полезно выносить пагубные вещи наверх: пустые User-Agent, сомнительные методы, аномальные Referer — всё это дешевле отбрасывать там, где CPU ещё не стал дорогим.

Сопровождаемость — это больше, чем «положили конфиг и забыли». Разделите ответственность: обновления TLS-стека и фронтов — в одном цикле, обновления WAF-движка и правил — в другом. Логи держите раздельно и понятными: на входе — сокращённый httplog с нужными полями, на WAF — отчёт о срабатываниях с корреляцией по request-id, у приложения — обычный access/error. На длинной дистанции это экономит часы расследований и снижает вероятность «сняли модуль — сломали сертификаты». В динамике цените автоматическую валидацию конфигов и canary-перезапуски: Nginx и HAProxy перезагружаются без потери соединений, Caddy перезапускается мягко — используйте это как привычку, а не как «магическую кнопку в исключительных случаях».

Код доставки запросов критичен, поэтому не жалейте времени на точечные проверки. Для Nginx проверьте, что UDP/443 действительно открыт, иначе браузеры останутся на HTTP/2, а вы будете ждать «обещанное чудо» QUIC зря. Для HAProxy прогоните пару health-check’ов вручную и убедитесь, что отказ бэкенда не превращает фронтенд в «чёрную дыру». Для WAF начните с режима «детект» и логику включайте по шагам: сначала самое общее, потом конкретику под ваши эндпоинты. CrowdSec дайте неделю на обучение с записью в логи, а потом включайте блокировки по сценарию с наименьшим риском, постепенно добавляя новые. open-appsec попросит чуть больше вдумчивой настройки в начале, зато вернёт это снижением доли ручных исключений через месяц.

Когда всё заведено, польза становится почти скучной: стабильные P95 и P99, уменьшающийся расход CPU на приложениях, исчезающие за ненадобностью «затычки» в коде. На этом этапе стоит подумать о географии. Два-три узла входной точки в регионах, где живут пользователи, дают заметное снижение латентности уже только из-за физики; дальше можно размножать правила и политику по шаблону, держать их в git и раскатывать Ansible’ом. Если хочется полностью уйти от DNS-прыжков, anycast /24 решает это одним махом, и HAProxy с Nginx к этому относятся спокойно — им без разницы, как пришёл пакет, если TLS и HTTP соблюдены. Расходы при этом легко предсказать: вы платите за VPS/серверы с честным vCPU и NVMe, а не за магические «гигабайты выхода», и это тот случай, когда инженерные решения прямо превращаются в экономию.

Бывают ситуации, когда хочется стартовать без недели подготовки. В этом случае разумно поднять один узел входной точки с заранее собранной связкой и минимальным WAF-профилем. Мы часто начинаем именно так: Nginx с HTTP/3 в роли фронта, приложение проксируется локально, ModSecurity с базовым CRS в режиме «детект», а на HAProxy выходят только те проекты, которым прямо сейчас нужны «жёсткие» тайминги и PROXY-protocol. Через день-два, когда логи покажут картину, включаем блокировки и переносим тот же профиль на второй узел в другом регионе. Так вы получаете ощутимое улучшение и предсказуемость за часы, а не за квартал, при этом архитектура остаётся вашей, переносимой на любую площадку.

Финальный совет простой и, возможно, самый ценный. Не пытайтесь «сразу идеально». Сначала сделайте входную точку, которая честно завершает TLS, правильно проксирует, даёт HTTP/2/3 и не ломает заголовки. Потом включите «малыша-WAF» с самыми общими правилами и измерьте, что изменилось. Затем доведите поведение до удобного для разработчиков уровня: чтобы исключение добавлялось один раз и не превращалось в бесконечную серию «если урл такой, то пропусти». И только после этого добавляйте поведенческие системы. Такой маршрут занимает чуть больше календаря, но значительно меньше нервов и денег. Если хочется ускориться без риска, можно сразу развернуть узлы на наших VPS с честными портами 10 Гбит/с и NVMe, получить готовую конфигурацию Nginx/Caddy/HAProxy с выбранным WAF и миграцию сайтов без простоев, а затем просто проверить доступность нужных локаций — география, как и всегда, решает половину UX.


這篇文章有幫助嗎?

« 返回

Powered by WHMCompleteSolution


知識庫