Инструменты пользователя

Инструменты сайта


service:haproxy

Различия

Показаны различия между двумя версиями страницы.

Ссылка на это сравнение

Предыдущая версия справа и слеваПредыдущая версия
Следующая версия
Предыдущая версия
service:haproxy [22.03.2022 10:32] – [Конфиг для Exchange 2013] viacheslavservice:haproxy [30.07.2024 19:21] (текущий) – внешнее изменение 127.0.0.1
Строка 1: Строка 1:
 +====== haproxy ======
 +[[https://www.youtube.com/watch?v=6o0SWcBTwuQ|Introduction to haproxy]]
 +
 +Reverse proxy и NLB 7-го и 4-го уровня.
 +
 +==== Установка ====
 +<code bash>
 +sudo apt install --no-install-recommends software-properties-common
 +sudo add-apt-repository ppa:vbernat/haproxy-2.5 -y
 +sudo apt install haproxy=2.5.\*
 +</code>
 +https://www.haproxy.com/blog/how-to-install-haproxy-on-ubuntu/
 +
 +==== Конфиг ====
 +Конфиг состоит из 4 основных частей:
 +  - **global** - общие настройки
 +  - **defaults** - значения по умолчанию для frontend и backend. Этих секций может быть несколько, и параметры будут применяться ко всем бэкендам и фронтендам после каждой.
 +  - **frontend** - приём пользовательских запросов, прослушиваемый IP, проверка HTTP, выбор бэкенда
 +  - **backend** - мониторинг серверов, балансировка, очередь
 +
 +Есть ещё секция **listen** - её можно использовать как объединение frontend и backend. Обычно применяется для TCP. На примере HAProxy stats page:
 +<code bash>
 +frontend stats
 +    bind *:8404
 +    stats enable
 +    stats uri /stats
 +    stats refresh 10s
 +    stats admin if { src 127.0.0.1 }
 +</code>
 +https://www.haproxy.com/blog/exploring-the-haproxy-stats-page/
 +
 +Есть также секции:
 +  - **resolvers** - настройка DNS (сервера тогда можно указывать как DNS-имя)
 +  - **mailers** - configuring email notifications
 +  - **peers** - синхронизация между узлами HAProxy.
 +
 +**defaults** может быть в нескольких экземплярах - последующая сбрасывает настройки предыдущей. Положим, можно сначала задать настройки для TCP, потом сделать секции frontend и backend для TCP, а потом сделать defaults и всё остальное для HTTP.
 +
 +**backend**\\
 +Самые популярные варианты балансировки:\\
 +balance roundrobin - просто циклический перебор\\
 +balance leastconn - выбор сервера с наименьшим числом соединений
 +
 +cookie - засылается клиенту для сохранения настроек соединения - клиент продолжает общаться с тем же сервером до конца сессии.
 +
 +default-server - можно перечислить опции для всех серверов, чтобы не писать одно и то же для каждого сервера
 +
 +Простейший конфиг (frontend и backend)
 +<code bash>
 +frontend http
 +        bind :80
 +        acl test url_beg -i test
 +        # или так:
 +        # acl test path -i -m beg /test
 +        use_backend test if test
 +        # вариант записи в одну строку (anonymous or inline ACL)
 +        # use_backend test if { path -i -m beg /test }
 +        
 +        # редирект на Яндекс
 +        acl yandex path -i -m beg /yandex
 +        http-request redirect location https://yandex.ru if yandex
 +        
 +        # во всех остальных случаях слать на root backend (типа else)
 +        default_backend root
 +
 +backend test
 +        balance roundrobin
 +        server web1 192.168.1.31:80
 +        server web2 192.168.1.32:80
 +
 +backend root
 +        balance roundrobin
 +        server web1 192.168.1.31:80
 +        server web2 192.168.1.32:80
 +</code>
 +
 +<code bash>
 +# Протестировать конфиг
 +haproxy -f /etc/haproxy/haproxy.cfg -c
 +</code>
 +
 +HAProxy easily tells [[https://www.keepalived.org/manpage.html|keepalived]] about its state and copes very well with floating virtual IP addresses.
 +
 +https://www.haproxy.com/blog/the-four-essential-sections-of-an-haproxy-configuration/\\
 +https://www.haproxy.com/blog/introduction-to-haproxy-acls/\\
 +http://www.haproxy.org/#docs
 +
 +==== tune.ssl.default-dh-param ====
 +Предупреждение при проверке конфига, когда на фронтенде есть сертификат:
 +  [WARNING] 012/103010 (81203) : parsing [/etc/haproxy/haproxy.cfg:38] : 'bind :443' :
 +  unable to load default 1024 bits DH parameter for certificate '/etc/ssl/certs/company/cert.pem'.
 +  , SSL library will use an automatically generated DH parameter.
 +  [WARNING] 012/103010 (81203) : Setting tune.ssl.default-dh-param to 1024 by default, if your workload permits it you should set it to at least 2048. Please set a value >= 1024 to make this warning disappear.
 +  Configuration file is valid
 +
 +Решение:
 +<code bash>
 +sudo openssl dhparam -out /etc/haproxy/dhparams.pem 2048
 +
 +sudo vi /etc/haproxy/haproxy.cfg
 +# Вставить в секцию global
 +ssl-dh-param-file /etc/haproxy/dhparams.pem
 +</code>
 +https://www.digitalocean.com/community/tutorials/haproxy-ssl-tls-warning-setting-tune-ssl-default-dh-param-to-1024-by-default
 +==== Конфиг для Exchange 2013 ====
 +OWA healthcheck page: https://mail.domain.ru/owa/healthcheck.htm
 +
 +[[https://sysadminblogger.wordpress.com/2018/08/13/exchange-2013-2016-switching-from-zen-load-balancer-to-haproxy/|Exchange 2013/2016: Switching from Zen Load Balancer to HAProxy]]\\
 +[[https://discourse.haproxy.org/t/haproxy-and-outlook-2010-on-exchange-2016/4496/16|HAproxy and Outlook 2010 on Exchange 2016]] - Outlook не хотел подключаться без этих заголовков\\
 +[[https://www.haproxy.com/static/media/uploads/eng/resources/aloha_load_balancer_appnotes_0065_exchange_2013_deployment_guide_en.pdf|ALOHA Load-Balancer deployment guide for Microsoft Exchange 2013]] (PDF)\\
 +[[https://mangolassi.it/topic/17099/my-config-for-haproxy-and-exchange-2013|My config for HAPRoxy and Exchange 2013]]\\
 +[[https://www.haproxy.com/blog/microsoft-exchange-2013-load-balancing-with-haproxy/|Microsoft Exchange 2013 Load Balancing with HAProxy]]\\
 +[[https://bidhankhatri.com.np/system/haproxy-configuration-for-windows-exchange-server-2016-and-2019/|HAProxy configuration for Windows Exchange Server 2016/2019]]
 +
 +++++ haproxy.cfg с одной из прошлых работ |
 +
 +<file bash haproxy.cfg>
 +global
 +log 127.0.0.1 local0 info
 +maxconn 10000000
 +daemon
 +quiet
 +debug
 +tune.ssl.default-dh-param 2048
 +
 +defaults
 +log global
 +mode http
 +option httplog
 +option dontlognull
 +timeout connect 1600000ms
 +timeout client 1600000ms
 +timeout server 1600000ms
 +timeout check 1600000ms
 +stats enable
 +stats uri /stats
 +
 +# в браузере вылезает окно с авторизацией перед входом
 +userlist basic-auth-list
 +user TEST insecure-password Pass123
 +
 +frontend fe_http_all
 +bind *:80
 +mode http
 +maxconn 10000000
 + acl autodiscover url_beg /Autodiscover
 + acl autodiscover url_beg /autodiscover
 +# use_backend be_h_ex2013 if autodiscover
 + use_backend be_ex2013
 +
 +frontend fe_https
 +mode http
 +maxconn 10000000
 +#bind *:443 ssl crt /etc/ssl/certs/final7.pem
 +bind *:443 ssl crt /etc/ssl/certs/exchange_certificate_and_key_nopassword.pem
 +acl Autodiscover url_beg /Autodiscover
 +acl autodiscover url_beg /autodiscover
 +acl bad_ip src 223.72.82.215 185.25.51.234 78.128.92.106 185.86.150.196 104.196.214.152
 +acl good_ip_ews src -f /etc/haproxy/good_ip_ews.lst
 +#acl legacy_host hdr_beg(host) -i legacy.
 +#acl mail_host hdr_beg(host) -i mail.
 +acl q_host hdr_beg(host) -i q.
 +#acl mapi url_beg /mapi
 +#acl rpc url_beg /rpc
 +#acl RPC url_beg /RPC
 +acl owa url_beg /owa
 +#acl eas url_beg /microsoft-server-activesync
 +#acl Eas url_beg /Microsoft-Server-Activesync
 +#acl ecp url_beg /ecp
 +acl ews url_beg /ews
 +acl EWS url_beg /EWS
 +acl calendar url_beg /owa/calendar/
 +acl Calendar url_beg /OWA/calendar/
 + use_backend be_ntpserv_owa if bad_ip
 +# use_backend be_secure_ex2013 if q_host owa
 + use_backend be_secure_ex2013 if q_host
 + use_backend be_ntpserv_owa if Calendar
 + use_backend be_ntpserv_owa if calendar
 +# use_backend be_cas01_ex2013 if owa
 + use_backend be_cas02_ex2013 if ews good_ip_ews # менять на активный be_cas0X_ex2013 for_change
 + use_backend be_cas02_ex2013 if EWS good_ip_ews # менять на активный be_cas0X_ex2013 for_change
 + use_backend be_ntpserv_owa if ews
 + use_backend be_ntpserv_owa if EWS
 +default_backend be_ex2013 # менять на активный be_cas0X_ex2013 for_change
 +#default_backend be_ex2007
 +
 +frontend fe_imap
 +mode tcp
 +bind *:993
 +default_backend be_imap_ex2013
 +
 +frontend fe_tls
 +mode tcp
 +bind *:143
 +default_backend be_tls_ex2013
 +
 +#frontend fe_smtp
 +#mode tcp
 +#bind *:25
 +#default_backend be_smtp_ex2007
 +
 +#backend be_ecp_ex2013
 +#maxconn 10000000
 +#mode http
 +#balance roundrobin
 +#server cas01 192.168.5.111:443 check
 +#server cas02 192.168.5.112:443 check
 +
 +backend be_secure_ex2013
 +#acl q-auth http_auth(basic-auth-list)
 +#http-request auth realm cas01 unless q-auth
 +maxconn 10000000
 +mode http
 +balance roundrobin
 +server cas01 192.168.5.111:443 check ssl inter 15s verify required ca-file /usr/share/ca-certificates/certs/root2.pem maxconn 30000
 +server cas02 192.168.5.112:443 check ssl inter 15s verify required ca-file /usr/share/ca-certificates/certs/root2.pem maxconn 30000
 +
 +backend be_ex2013
 +maxconn 10000000
 +mode http
 +balance roundrobin
 +server cas01 192.168.5.111:443 check ssl inter 15s verify required ca-file /usr/share/ca-certificates/certs/root2.pem maxconn 30000
 +server cas02 192.168.5.112:443 check ssl inter 15s verify required ca-file /usr/share/ca-certificates/certs/root2.pem maxconn 30000
 +
 +backend be_cas01_ex2013
 +maxconn 10000000
 +mode http
 +balance roundrobin
 +server cas01 192.168.5.111:443 check ssl inter 15s verify required ca-file /usr/share/ca-certificates/certs/root2.pem maxconn 30000
 +
 +backend be_cas02_ex2013
 +maxconn 10000000
 +mode http
 +balance roundrobin
 +server cas02 192.168.5.112:443 check ssl inter 15s verify required ca-file /usr/share/ca-certificates/certs/root2.pem maxconn 30000
 +
 +backend be_ntpserv
 +maxconn 10000000
 +mode http
 +balance roundrobin
 +server ntpserv 192.168.5.184
 +
 +backend be_ntpserv_owa
 +maxconn 10000000
 +mode http
 +balance roundrobin
 +server ntpserv88 192.168.5.184:88
 +
 +backend be_imap_ex2013
 +mode tcp
 +balance roundrobin
 +server cas01 192.168.5.111:993
 +server cas02 192.168.5.112:993
 +
 +backend be_tls_ex2013
 +mode tcp
 +balance roundrobin
 +server cas01 192.168.5.111:143
 +server cas02 192.168.5.112:143
 +
 +#listenstats :7000
 +#stats enable
 +#stats uri /
 +#optionhttpclose
 +#stats auth admin:P@ssw0rd
 +</file>
 +++++
 +
 +
 +===== ACL =====
 +https://www.haproxy.com/blog/introduction-to-haproxy-acls/\\
 +https://www.youtube.com/watch?v=9ISPGye5MnU
 +
 +ACLs - правила доступа, кто может получать доступ.
 +
 +Примеры
 +<code bash>
 +# использовать бэкенд be_example, если заголовок host в запросе совпадает с example.com (-m[atch] dom[ain]), не учитывать регистр (-i).
 +use_backend be_example if { req.hdr(host) -i -m dom example.com }
 +# генерация имени бэкенда из запроса с помощью карты.
 +use_backend be_%[req.hdr(host),lower,map(/etc/haproxy/hosts.map)]
 +# редирект на https. unless = if !{}.
 +http-request redirect scheme https unless { ssl_fc }
 +# именованное правило
 +acl is_admin_range src 10.0.48.0/24
 +# запрет ходить на URL, начинающийся на /admin всем, кроме админского диапазона.
 +http-request deny if !is_admin_range { path_beg /admin/ }
 +# посылать на /login, если нет клиентского сертификата или через lua.
 +http-request redirect location /login unless { ssl_c_used } || { lua.is_auth_valid }
 +# не совсем acl, захват значения кол-ва запросов в логи, полезно для оценки и предварительных измерений (https://cbonte.github.io/haproxy-dconv/2.5/configuration.html#http-request%20capture).
 +http-request capture sc_http_req_rate(0) len 4
 +</code>
 +
 + ACLs находятся в секциях фронтэнда (чаще всего), бекэнда или listen (совмещённый фронт и бэк, в основном используется для TCP). Выполняются обычно по порядку сверху вниз либо в порядке TCP connect -> TCP content -> HTTP request -> HTTP response. Нужно проверять конфигурацию и обращать внимание на предупреждения.
 +
 +Линейные (inline) ACL состоит из действия (action), выборки (fetch) и необязательных конвертеров, флагов и т. д.
 +<code bash>
 +use_backend be_example if { req.hdr(host) -i -m dom example.com }
 +</code>
 +У именных (named) acl после имени идёт выборка
 +<code bash>
 +acl is_example req.hdr(host) -i -m dom example.com
 +use_backend be_example if is_example
 +</code>
 +
 +==== Выборка (fetch) ====
 +Выборка получает данные, обычно из запроса или ответа. Полученная информация может быть передана конвертеру и затем посылается на сравнение (match). Некоторые выборки принимают аргументы, например, ''req.hdr(host)'', некоторые - нет ''ssl_fc_session_id''. Самые популярные выборки: [[https://cbonte.github.io/haproxy-dconv/2.5/configuration.html#7.3.6-url_param|url_param]], src (IP источника), req.hdr (заголовок источника), cook (выборка по имени кук, возвращает значение), rand (случайное число), nbsrv (возвращает кол-во активных серверов на указанном бэкенде), payload (тело запроса TCP, нужно для определения протокола, сравнение).
 +
 +==== Конвертер ====
 +Конвертеры необязательны, идут за выборкой или за предыдущим конвертером, отделяются запятыми, например
 +<code bash>
 +# имя хоста в нижний регистр, взять первые 5 символов
 +req.hdr(host),lower,bytes(0,5)
 +</code>
 +Некоторые конвертеры также принимают аргументы. Популярные конвертеры: lower, bytes, hex, base64, map (лезет в карту "ключ-значение" и выдаёт значение), field (часть строки при заданном разделителе), mod (сравнение по модулю), regsub (регулярка).
 +
 +==== Флаги ====
 +Флаги находятся между выборками/конвертерами и значениями.
 +<code bash>
 +use_backend be_example if { path -m beg /login }
 +</code>
 +Часто используются: -i (нечувствительность к регистру), -m (match), -f (смотреть в acl-файл), менее популярные: -n (запрет разрешения имени в DNS), -M (читать указанный файл как карту), -u (задать acl ID).
 +
 +=== Сравнения ===
 +Варианты сравнения: beg, end, sub, dom, len, reg, found.
 +<code bash>
 +# reg
 +use_backend be_static if { path -m reg /login/[a-z]+/failed/.* }
 +# reg может сочетаться с -f
 +use_backend be_static if { path -m reg -f /etc/haproxy/static_patterns.acl }
 +# регулярные выражения ресурсоёмки, надо следить за производительностью
 +
 +# found - если заголовка host нет, отклонить запрос
 +http-request deny unless { req.hdr(host) -m found }
 +</code>
 +
 +=== Выборки со встроенным сравнением ===
 +Выборки со встроенным сравнением: ''path_beg = path -m beg'', path_end, path_reg, path_sub и т. д. Не все выборки имеют такие варианты. Часто используются beg, dir, dom, end, len, reg, sub.
 +
 +=== Сравнения с числами ===
 +Сравнения с числами - eq, ge, gt, le, lt. 
 +<code bash>
 +http-request deny if { sc_http_req_rate(0) gt 50 }
 +# диапазон разделён двоеточием, например 10:30
 +acl ssl req.ssl_ver 3:3.1
 +</code>
 +
 +==== Значения ====
 +Значения должны быть статическими, т. е., не другими выборками/конвертерами/переменными.
 +<code bash>
 +# Строки, их может быть несколько
 +use_backend be_static if { path_beg /images /icons }
 +# ip или их диапазоны
 +http-request deny unless { src 192.168.0.0/24 127.0.0.0/8 }
 +# Двоичные данные
 +use_backend be_multiplayer if { payload(0,4) -m bin 3f021bca }
 +</code>
 +
 +==== Действия ====
 +Собственно, для чего и затеваются ACLs. Одно из наиболее популярных - http-request и use_backend.
 +<code bash>
 +# редирект
 +http-request redirect scheme https code 301 unless { ssl_fc } 
 +http-request redirect location /login unless { req.cook(sessionid) -m found }
 +# переделать путь на лету - не редирект, т. е., клиент не узнает, что бэкенд получает другой путь
 +http-request set-path /legacy%[path] if {req.hdr(host) -i old.example.com }
 +# добавить заголовок из stick table
 +http-request set-header x-pages-viewed %[sc_gpc0_rate(0)]
 +# использовать кэш
 +http-request cache-use static if {path_beg /static/ }
 +# отправлять на бэкенд be_example в соответствии с заголовком host
 +use_backend be_example if { req.hdr(host) -i -m dom www.example.com }
 +# динамически создавать имя бэкенда из заголовка в нижнем регистре
 +use_backend be_%[req.hdr(host),lower]
 +# дополнительно задействовать карту
 +use_backend be_%[req.hdr(host),lower,map_end(/etc/haproxy/hosts.map,default)]
 +</code>
 +
 +Порядок срабатывания действий в соответствии с фазами соединения:
 +  * tcp-request connection (на раннем этапе полезно отшибать DoS-атаки с кучей соединений, чтобы не тратить ресурсы на их обработку)
 +  * tcp-request content (используемый протокол, режим)
 +  * http-request (например, добавить заголовки, изменить путь перед передачей запроса клиента на бэкенд)
 +  * http-response (например, добавить заголовки перед отправкой ответа бэкенда клиенту)
 +
 +''use_backend'' может быть использован на любом этапе и останавливает обработку.\\
 +''tcp-request accept'' или ''http-request accept'' останавливают обработку на своих фазах.
 +
 +==== Переменные ====
 +Не требуются, если только не нужно делать чего-то сложного. Некоторые случаи, когда они используются:
 +  * Пути или заголовки из запроса на этапе ответа
 +  * В выборках/lua, которые принимают значения переменных (например, concat)
 +  * Задание записи в карте с sessionid cookie на основe заголовка ответа: <code>http-request set-var(txn.sessionid) req.cook(sessionid)
 +use_backend %[req.cook(sessionid),map(/etc/haproxy/sessions.map)] if { req.cook(sessionid),map(/etc/haproxy/sessions.map) -m found }
 +http-response set-map(/etc/haproxy/sessions.map) var(txn.sessionid)
 +res.hdr(x-sessionid-backend) if { res.hdr(x-sessionid-backend) -m found }
 +</code>
 +
 +Как их использовать
 +
 +  * В http-request/tcp-request content/http-response - ''set-var([scope].[name]) value''
 +  * Scope может быть txn (самый частый случай), proc, sess, req, res
 +  * Name может состоять из чисел, букв, точек и подчёркиваний.
 +  * Во всех случаях переменная будет иметь имя **txn.name**, никогда как просто **name**
 +  * Значение переменной извлекается в выборке как ''var(txn.name)''
 +  * В LUA - ''%%txn:set_var("txn.name","value")%%'' и ''%%txn:get_var("txn.name")%%''
 +
 +==== Карты и ACL-файлы ====
 +ACL-файлы - это просто список для сравнения, а карта при совпадении значения при сравнении возвращает другое значение из второй колонки.
 +
 +  * Все они являются файлами на диске, который читаются на этапе запуска
 +  * ACL-файлы имеют одну колонку, например, ''192.168.1.0/24'', карты - две: ''192.168.1.0/24 admin_net''
 +  * Все они могут быть прочитаны и изменены через runtime API:
 +    * ''add acl'', ''del acl'', ''show acl''
 +    * ''add map'', ''del map'', ''show map'', ''set map''
 +  * Все они могут быть изменены через модуль ''lb_update'' (в версии enterprise)
 +
 +=== ACL-файлы ===
 +  * Одна строка на образец
 +  * Образец может быть IP-адресом (диапазоном), строкой или регуляркой
 +  * Может быть использован с режимами сравнения ''-m beg'', ''-m end'', ''-m reg''
 +  * Используется с помощью ключа ''-f''<code bash>http-request deny if { path_beg /admin/ } !{ src -f /etc/haproxy/admins.acl }</code>
 +
 +== Действия и ACL-файлы ==
 +  * http-request add-acl
 +  * http-response add-acl
 +  * http-request add-acl
 +  * http-response add-acl
 +
 +Таким образом, можно сделать так, чтобы запросы с какого-нибудь сервера автоматически правили ACL-файлы, например, внося туда IP-адреса для блокировки.
 +<code bash>
 +http-request add-acl(/etc/haproxy/block.acl) %[url_param(ip)] if { src 10.0.0.0/8 } url_param(action) add
 +http-request add-acl(/etc/haproxy/block.acl) %[url_param(ip)] if { src 10.0.0.0/8 } url_param(action) remove
 +</code>
 +
 +=== Карты ===
 +  * Используются через "конвертер карт"
 +    * Подбирает образец, например ''src,map(/etc/haproxy/ip_types.map''
 +    * Возвращает результат поиска
 +    * Сравнивать можно практически любым способом - beg, dir, dom, end, len, reg, sub
 +  * Действия http-request/response содержат варианты set-map, add-map, del-map
 +  * Используются для:
 +    * Сопоставления имён хостов бэкендам
 +    * Хранения лимитов для ключей
 +
 +===== Карты (maps) =====
 +https://www.haproxy.com/blog/introduction-to-haproxy-maps/\\
 +https://www.youtube.com/watch?v=M0-08Hrn86E
 +
 +Нужны для
 +  * Направления трафика на бэкенды
 +  * GeoIP
 +  * API key
 +  * Лимитов для доменов/путей
 +
 +Карта выглядит примерно так:
 +<code bash>
 +# key       value
 +example.com be_example
 +haproxy.com be_haproxy
 +test.ru     be_test
 +</code>
 +
 +<code bash>  
 +frontend fe_main
 +  bind :443 ssl crt /etc/ssl/certs/main/
 +  # Взять хост из заголовка, перевести в нижний регистр, искать значение в карте.
 +  # Если значения нет, использовать значение по умолчанию (здесь: be_static)
 +  use_backend %[req.hdr(host),lower,map_dom(/etc/haproxy/maps/hosts.map,be_static)]
 +</code>
 +
 +==== map() ====
 +  * На вход в конвертер карты подаётся образец, который нужно найти, например, ''src,map(/etc/haproxy/ips.map)''. Конвертер он потому, что, в отличие от обычной выборки, он значение даёт не сам, а ищет его в стороннем файле.
 +  * Обязательным значением является путь к файлу карты.
 +  * Необязательный аргумент - значение по умолчанию.
 +  * Возвращает значение из второй колонки, если найдено совпадение строки в первой.
 +  * Варианты сравнения - str, beg, sub, dir, dom, end, reg, regm, int, ip
 +
 +==== Обновление карты ====
 +Так как haproxy читает файлы только во время инициализации, нужен метод для обновления карт без перезапуска haproxy.
 +
 +Все методы не обновляют самих файлов карт на диске! Чтобы обновлять сами файлы, нужно отдельно запускать show map и дамп в файл (например, в кроне).
 +
 +Первый метод - модуль LB-Update (только для версии enterprise). Его легко настраивать, он хорошо работает в кластерах.
 +<code bash>
 +dynamic-update
 +  update id /etc/hapee-1.8/maps/sample.map url http://10.0.0.1/sample.map delay 300s
 +</code>
 +
 +Второй метод - через сокет (см. https://www.haproxy.com/blog/dynamic-configuration-haproxy-runtime-api/).
 +<code bash>
 +echo "show map /etc/haproxy/domains.map" |socat stdio /var/run/haproxy/admin.sock
 +echo "clear map /etc/haproxy/domains.map" |socat stdio /var/run/haproxy/admin.sock
 +echo "del map /etc/haproxy/domains.map example.com" |socat stdio /var/run/haproxy/admin.sock
 +# add map не проверяет, есть ли добавляемое уже в карте, есть риск получить дубль,
 +# поэтому надо сочетать это в скрипте с show map, чтобы проверить наличие до добавления
 +echo "add map /etc/haproxy/domains.map example.com be_example" |socat stdio /var/run/haproxy/admin.sock
 +echo "set map /etc/haproxy/domains.map example.com be_example" |socat stdio /var/run/haproxy/admin.sock
 +</code>
 +
 +При использовании nbproc у каждого процесса haproxy свой взгляд на файлы карт. В отличие от модуля lb-update здесь у каждого процесса свой сокет, и нужно обновлять карты для каждого процесса. С версии 1.9 используется усовершенствованный nbthread, позволяющий работать без потерь производительности при нескольких процессах haproxy.\\
 +https://www.haproxy.com/blog/multithreading-in-haproxy/
 +
 +Третий - через ACL (неприменимо, если используется nbproc)
 +<code bash>
 +frontend fe_main
 +  bind :80
 +  # при запросе определённого url из определённой сети
 +  acl in_network src 192.168.122.0/24
 +  acl is_map_add path_beg /map/add
 +  # задать запись в карте (в памяти)
 +  http-request set-map(/etc/haproxy/maps/hosts.map) %[url_param(domain)] %[url_param(backend)] if is_map_add in_network
 +  # не передавать этот запрос на бэкенд
 +  http-request deny deny_status 200 if is_map_add in_network
 +  # использовать бэкенд с картой при всех прочих запросах
 +  use_backend %[req.hdr(host),lower,map(/etc/haproxy/maps/hosts.map)]
 +</code>
 +
 +==== Примеры ====
 +
 +=== Paths rate limit ===
 +Карта:
 +  /api/routeA  40
 +  /api/routeB  20
 +
 +<code bash>
 +frontend api_gateway
 +  bind :80
 +  default_backend api_servers
 +  # Set up stick table to track request rates
 +  stick-table type binary len 8 size 1m expire 10s store http_req_rate(10s)
 +  # Track client by base32+src (Host header + URL path + src IP)
 +  http-request track-sc0 base32+src
 +  # Check map file to get rate limit for path
 +  http-request set-var(req.rate_limit)  path,map_beg(/etc/haproxy/maps/rates.map)
 +  # Client's request rate is tracked
 +  http-request set-var(req.request_rate)  base32+src,table_http_req_rate(api_gateway)
 +  # Subtract the current request rate from the limit
 +  # If less than zero, set rate_abuse to true
 +  acl rate_abuse var(req.rate_limit),sub(req.request_rate) lt 0    
 +  # Deny if rate abuse
 +  http-request deny deny_status 429 if rate_abuse
 +</code>
 +
 +=== Чёрный список ===
 +Пример файла ACL.
 +<code bash>
 +acl is_admin_ip src 192.168.122.0/24 127.0.0.0/8
 +# добавляем-удаляем значения с админских диапазонов
 +http-request add-acl(/etc/haproxy/blacklist.acl) %[url_param(ip)] if is_admin_ip { path_beg /blacklist/add }
 +http-request del-acl(/etc/haproxy/blacklist.acl) %[url_param(ip)] if is_admin_ip { path_beg /blacklist/del }
 +# не пробрасывать никуда, если правится чёрный список
 +http-request deny deny_status 200 if is_admin_ip { path_beg /blacklist/ }
 +# блочить, собственно, значения из списка
 +http-request deny if ( src -f /etc/haproxy/blacklist.acl }
 +</code>
 +
 +===== Stick tables =====
 +https://www.haproxy.com/blog/introduction-to-haproxy-stick-tables/\\
 +https://www.youtube.com/watch?v=syPkNnOXS8k
 +
 +Хранилище "ключ-значение". Ключом может быть IP, Integer, String, Binary.\\
 +Популярные значения - conn_cur, conn_rate, http_req_rate, http_err_rate, server_id.
 +
 +Используются для
 +  * Привязки клиента к серверу
 +    * Session cookies
 +    * SSL session ID
 +    * Исходный IP
 +  * Лимитирования
 +    * GET/POST floods
 +    * Login brute force
 +    * Web scraping
 +    * API key usage
 +  * Статистики/Логирования
 +
 +Таблица может быть **только одна** в секции frontend/backend/listen.\\
 +Данные в неё добавляются с помощью действия ''track-sc0'' в командах tcp-request connect, tcp-request content, http-request, http-response.
 +
 +В примере используется пустой бэкенд с таблицей, на который идёт ссылка как на таблицу из фронтенда.
 +<code bash>
 +backend st_src_global
 +  stick-table type ip size 1m expire 10s store http_req_rate(10s)
 +
 +frontend fe_main
 +  bind *:80
 +  http-request track-sc0 src table st_src_global
 +  http-request deny if { sc_http_req_rate(0) gt 10 }
 +</code>
 +
 +==== Multiprocessing ====
 +  * Если используется nbproc (версия 1.8 и старее), то у каждого процесса будет свой набор таблиц.
 +  * Если используется nbthread (версия 1.9 и новее), то все процессы используют один и тот же набор таблиц.
 +
 +==== Синтаксис ====
 +<code bash>
 +backend st_ip
 +# stick-table тип <ключ> размер <кол-во строк> истекает <время> хранить <значение,значение,значение>
 +  stick-table type ip size 1m expire 10s store http_req_rate(10s),conn_cur,gpc0
 +</code>
 +  * За типами string и binary идёт параметр len (длина) - это число байт для захвата, если входное значение больше, оно урезается.
 +  * Размер - кол-во строк в таблице (здесь 1 млн), нужен, чтобы не забить всю память. Из практических соображений не нужно задавать слишком много значений, т. к. может понадобиться сделать ещё несколько таблиц. На одну строку нужно 50 байт + размер ключа + размер значений.
 +  * Истечение срока хранения - максимальное время, прошедшее с момента добавления, обновления или сравнения записи в таблице, после истечения запись удаляется. В случае с таблицей, хранящей частоту запросов (rate), срок хранения должен равняться периоду изменения частоты запросов, чтобы срок хранения и окончание периода измерения совпадали.
 +
 +==== Хранение данных ====
 +<code bash>
 +http-request track-sc0 src table st_ip
 +</code>
 +  * sc0 - sticky counter 0
 +  * src - выборка, ключ для отслеживания
 +    * Частота запросов/сканирование: src, base32 (IP + URL), req.hdr(Authorization)
 +    * Статистика: req.hdr(host), ssl_fc_protocol, path
 +    * Привязка: src, req.cook(sessionid)
 +  * table - какую таблицу использовать. Этот параметр не нужен, если таблица находится в той же секции.
 +
 +В рамках одной сессии нужен уникальный номер sc для каждой записи.
 +
 +Здесь нужны разные номера, т. к. они активны для каждой сессии:
 +<code bash>
 +http-request track-sc0 src table st_src
 +http-request track-sc1 base32 table st_base32
 +</code>
 +
 +А здесь - нет, т. к. они никогда не пересекаются:
 +<code bash>
 +http-request track-sc0 src st_src_get if METH_GET
 +http-request track-sc0 src st_src_post if METH_POST
 +</code>
 +
 +  * Номер задаётся флагом компиляции ''MAX_SESS_STKCTR'', в HAPEE 12 номеров.
 +  * Номер sc используется потом в аргументах выборки, например, ''track-sc0'' -> ''st_http_req_rate(0)''
 +
 +==== Использование значений ====
 +1) С помощью выборки. Выборка, ссылающаяся на счётчик (sc), чаще всего имеет префикс sc_, и соответствующий номер как аргумент.
 +<code bash>
 +http-request deny if { sc_http_req_rate(0) gt 3 }
 +</code>
 +Популярные выборки: sc_http_req_rate, sc_conn_cur, sc_conn_rate.
 +
 +2) С помощью конвертера, если не задействованы счётчики. На входе - ключ, аргумент конвертера - имя таблицы.
 +<code bash>
 +http-request deny if { src,table_http_req_rate(st_src) gt 3 }
 +</code>
 +Применяется для таблиц, если используется peers.
 +
 +==== Просмотр содержимого ====
 +С помощью API-запросов. В версии Enterprise есть веб-интерфейс.
 +<code bash>
 +echo "show table st_src_global" |socat stdio /var/haproxy/admin.sock
 +# Обновлять каждые 5 секунд
 +watch -n 5 'echo "show table st_src_global" |socat stdio /var/run/haproxy/admin.sock'
 +</code>
 +
 +==== Примеры ====
 +Постоянное подключение к одному из серверов (привязка) + peers
 +<code bash>
 +# Протокол peers позволяет передавать данные из таблицы на все сервера-партнёры
 +peers mypeers
 +  # Имя партнёра должно совпадать с его hostname, либо с заданным в секции global его конфигурации haproxy
 +  peer haproxy1 192.168.1.11:10000
 +  peer haproxy2 192.168.1.12:10000
 +  
 +backend mybackend
 +  mode tcp
 +  balance roundrobin
 +  # Здесь нет указания значений, т. к. server ID добавляется автоматически
 +  stick-table type ip size 20k peers mypeers
 +  # К чему привязываться при закреплении пользователя к серверу
 +  stick on src
 +  server srv1 192.168.1.101:80
 +  server srv2 192.168.1.102:80
 +</code>
 +
 +Анти-флуд
 +<code bash>
 +backend st_src_global
 +  stick-table type ip size 1m expire 10s store http_req_rate(10s)
 +  
 +frontend fe_main
 +  bind *:80
 +  http-request track-sc0 src table st_src_global
 +  http-request deny if { sc_http_req_rate(0) gt 10 }
 +</code>
 +
 +Web scraping - в этом примере блокируются те IP, которые зашли на более чем 15 разных URL за последние 24 часа.
 +<code bash>
 +backend per_ip_and_url_rates
 +  stick-table type binary len 8 size 1m expire 24h store http_req_rate(24h)
 +
 +backend per_ip_rates
 +  stick-table type ip size 1m expire 24h store gpc0,gpc0_rate(30s)
 +
 +frontend fe_main
 +  bind *:80
 +  http-request track-sc0 src table per_ip_rates
 +  http-request track-sc1 url32+src table per_ip_and_url_rates unless { path_end .css .js .png .jpeg .gif }
 +  acl exceeds_limit sc_gpc0_rate(0) gt 15
 +  # Увеличить gpc0, если http_req_rate = 1 (он равен единице, когда кто-то запрашивает url первый раз)
 +  # Если url запрашивается не в первый раз, то http_req_rate будет больше 1, и это не сработает
 +  http-request sc-inc-gpc0(0) if { sc_http_req_rate(1) eq 1 } !exceeds_limit
 +  http-request deny if exceeds_limit
 +  default_backend web_servers
 +</code>
 +===== DDoS Attack and Bot Protection =====
 +https://www.haproxy.com/blog/application-layer-ddos-attack-protection-with-haproxy/\\
 +https://www.youtube.com/watch?v=-WcGDlX1liY\\
 +https://www.haproxy.com/blog/four-examples-of-haproxy-rate-limiting/
 +
 +===== Логи =====
 +https://www.youtube.com/watch?v=hZ9DQKyMce4
 +
 +<code>
 +# Дата сервер процесс[PID]: IP-адрес:порт [время запроса] фронтенд бекэнд/сервер тай/мин/ги/зап/роса код_статуса размер_запроса куки(- -) состояние_при_завершении(----)
 +Mar 10 11:13:42 vmls-haproxy1 haproxy[919886]: 77.232.165.18:2437 [10/Mar/2022:11:13:42.511] fe_web~ be_waf/vmls-waf 0/0/0/15/15 404 399 - - ---- 219/219/83/17/0 0/0 "GET https://www.domain.ru/common/img/uploaded/articles/cdhem/6-1.jpg HTTP/2.0"
 +</code>
 +
 +Состояние при завершении, расшифровка: https://cbonte.github.io/haproxy-dconv/2.5/configuration.html#8.5
 +
 +===== Проверки =====
 +https://www.haproxy.com/blog/how-to-enable-health-checks-in-haproxy/
 +
 +===== Кластер =====
 +Active-Passive: 1 виртуальный IP.
 +
 +{{:service:pasted:20220212-121707.png?600}}
 +
 +Active-Active: 2 виртуальных IP, на входе DNS Round Robin.
 +
 +{{:service:pasted:20220212-121718.png?600}}
 +
 +==== Синхронизация конфигурации ====
 +Сначала нужно [[service:ssh#haproxy|настроить ssh]].
 +Синхронизация между двумя нодами, rsync работает в режиме archive и update, т. е., обновления существующих файлов.\\
 +Выдаёт статистику, которая парсится по слову ''transferred:'' и потом удаляется всё, кроме числового значения.\\
 +Помимо каталога с конфигурацией, синхронизируется каталог с сертификатами. Так как сертификаты выпускает только одна нода,\\
 +строку с переменной ''certs'' на ней нужно закомментировать. Чтобы не было ошибки при сложении в ''all'', подставляется 0 как значение по умолчанию.\\
 +Если были скопированы какие-то файлы, HAProxy перечитывает конфигурацию.
 +<file bash /scripts/sync-config.sh>
 +#!/bin/bash
 +
 +rsyncConfig() {
 +        int=`rsync -au --stats $1 $2 |grep transferred:`
 +        echo ${int##*:}
 +}
 +
 +config=`rsyncConfig vmls-haproxy1:/etc/haproxy/ /etc/haproxy`
 +certs=`rsyncConfig vmls-haproxy1:/etc/ssl/certs/companyname/ /etc/ssl/certs/companyname`
 +all=$(( ${config:-0} + ${certs:-0} ))
 +
 +if [[ $all -ne 0 ]]
 +then
 +        echo "$all files transferred, reloading HAProxy..."
 +        systemctl reload haproxy
 +        echo "HAProxy has been reloaded"
 +else
 +        echo "$all files transferred, no need to do anything"
 +fi
 +</file>
 +
 +Добавить в cron
 +<code bash>
 +echo -e "\n# Sync HAProxy config\n*/5 *\t* * *\troot\t/scripts/sync-config.sh" >> /etc/crontab
 +</code>
 +
 +===== Видео =====
 +[[https://www.youtube.com/watch?v=CHdT3XA3JP8|HAProxy 2.6 - обзор новых функций]]
 +
 +
  

Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki