Как стать автором
Обновить

Маршрутизация силами Haproxy, DoH, GeoIP, защита сервисов через mTLS и выгрузка метрик в Prometheus, настройка ACME.SH

Уровень сложностиСложный
Время на прочтение38 мин
Количество просмотров1.2K
Так же будет переосмысление предыдущих статей на тему маршрутизации VLESS+TLS VLESS-REALITY

Данная статья так же будет объединением предыдущих статей про маршрутизацию REALITY методом steal-oneself(укради себя) и переосмысляет их. Первые три можно прочитать тут 1 2 3. Перейдем на unix socket взамен использования портов, установим панель 3X-UI на сервер без докера от пользователя xray, подключим подписку. Так же дам пример как через Haproxy развернуть сертификат для ocserv или любого другого сервиса для сквозной маршрутизации.

Версии используемого ПО Ubuntu 24.04 LTS и Haproxy 2.8 из репозитория, конфиг подготовлен для этой версии, на версиях выше может потребоваться доработка, версия XCA 2.8, на 2.9 идентично.

В первой части статьи установим Haproxy настроим выдачу сертификатов без перезапуска Haproxy через acme.sh, настроим базовую маршрутизацию, базово подготовим Apache2 для работы за обратным прокси, подготовим УЦ, настроим mTLS, и встроенный экспортер Prometheus в Haproxy.

Часть 1. Базовый конфиг и сертификаты LE

Работаю от root командой sudo su
Обновим список пакетов и обновим систему:
apt update && apt upgrade -y
Установим необходимое ПО:
apt install -y haproxy htop socat netcat-traditional apache2
Поменяем стандартный 80 порт apache2 на 8080 и отключим модули SSL:
Отрываем конфиг nano /etc/apache2/ports.conf и приводим к виду как на примере ниже

Listen 8080

#<IfModule ssl_module>
#	Listen 443
#</IfModule>

#<IfModule mod_gnutls.c>
#	Listen 443
#</IfModule>

Перезапускаем apache2 systemctl restart apache2

Установим acme.sh и подготовим директорию для сертификатов.
Создадим пользователя acme:
adduser --system --disabled-password --disabled-login --home /var/lib/acme --quiet --force-badname --group acme
Добавим его в группу haproxy:
adduser acme haproxy
Создадим директорию для файлов acme:
mkdir /usr/local/share/acme.sh/
Перейдем в tmp загрузим acme.sh перейдем в директорию:
cd /tmp/ && git clone https://github.com/acmesh-official/acme.sh.git && cd acme.sh/
Установим acme.sh без cron в созданную ранее директорию:
./acme.sh --install --no-cron --no-profile --home /usr/local/share/acme.sh
Сделаем симлинк для удобства:
ln -s /usr/local/share/acme.sh/acme.sh /usr/local/bin/
Изменим права на директорию acme.sh:
chmod 755 /usr/local/share/acme.sh/
Создадим директорию для хранения сертификатов:
mkdir /etc/haproxy/certs
Изменим владельца:
chown haproxy:haproxy /etc/haproxy/certs
Выставим права:
chmod 770 /etc/haproxy/certs

Теперь создадим аккаунт в acme.sh, далее все действия выполняются от пользователя acme:
sudo -u acme -s
acme.sh --register-account --server letsencrypt -m example@mail.com
Изменим УЦ для выдачи по умолчанию ZeroSSL не работает с RU:
acme.sh --set-default-ca --server letsencrypt
Выполним acme.sh --update-account
Полученный ACCOUNT_THUMBPRINT нам потребуется далее для настройки Haproxy.
Завершим работу с пользователем командой exit

Приступим к подготовке конфигурации Haproxy
Сотрем имеющуюся конфигурацию командой > /etc/haproxy/haproxy.cfg
Откроем файл nano /etc/haproxy/haproxy.cfg
Настроим секцию Global, в замен "*ACCOUNT_THUMBPRINT*" вставьте полученный ранее отпечаток на стадии acme.sh --update-account, уровень безопасности выберите сами, для совместимости я использую tls1.2 на фронтенде отдельно можно установить tls1.3

global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /var/run/haproxy/admin.sock level admin mode 660
        setenv ACCOUNT_THUMBPRINT '*ACCOUNT_THUMBPRINT*' # поменять на полученный из acme
        stats timeout 30s
        user    haproxy
        group   haproxy
        daemon
    # https://ssl-config.mozilla.org/
    # Улучшенная безопастность и совместимость со старыми браузерами ( Intermediate ) Supports Firefox 27, Android 4.4.2, Chrome 31, Edge, IE 11 on Windows 7, Java 8u31, OpenSSL 1.0.1, Opera 20, Safari 9
        ssl-default-bind-curves X25519:prime256v1:secp384r1
        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-bind-options prefer-client-ciphers ssl-min-ver TLSv1.2 no-tls-tickets

        ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
        ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-server-options ssl-min-ver TLSv1.2 no-tls-tickets

    # TLS 1.3 современная безопасность без отката к TLS 1.2  ( Modern ) Supports Firefox 63, Android 10.0, Chrome 70, Edge 75, Java 11, OpenSSL 1.1.1, Opera 57, Safari 12.1
#        ssl-default-bind-curves X25519:prime256v1:secp384r1
#        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
#        ssl-default-bind-options prefer-client-ciphers ssl-min-ver TLSv1.3 no-tls-tickets

#        ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
#        ssl-default-server-options ssl-min-ver TLSv1.3 no-tls-tickets

        ssl-dh-param-file /etc/haproxy/dh4096.pem # openssl dhparam -out /etc/haproxy/dh4096.pem 4096
#        tune.ssl.default-dh-param 2048
      # Тюнинг http/2
        tune.h2.initial-window-size 536870912 # Увеличиваем начальный размер окна для входящих и исходящих соединений.
        tune.h2.fe.max-concurrent-streams 512 # Установим кол-во одновременных потоков на входящие соединений.
        tune.h2.be.max-concurrent-streams 512 # Установим кол-во одновременных потоков на исходящие соединений.

Сгенерируем DH командой: openssl dhparam -out /etc/haproxy/dh4096.pem

Настроим секции defaults и resolvers для разрешения доменных имен на backend'е

defaults
       log global
       mode http
       option httplog
       option dontlognull
       timeout connect 40s
       timeout client  1m
       timeout server  1m
       timeout tunnel 1h
       timeout http-request 30s
       errorfile 400 /etc/haproxy/errors/400.http
       errorfile 403 /etc/haproxy/errors/403.http
       errorfile 408 /etc/haproxy/errors/408.http
       errorfile 500 /etc/haproxy/errors/500.http
       errorfile 502 /etc/haproxy/errors/502.http
       errorfile 503 /etc/haproxy/errors/503.http
       errorfile 504 /etc/haproxy/errors/504.http

resolvers dnsserver
        nameserver ns1 127.0.0.1:53
        parse-resolv-conf
        resolve_retries       3
        timeout resolve       1s
        timeout retry         1s
        hold other           30s
        hold refused         30s
        hold nx              30s
        hold timeout         30s
        hold valid           10s
        hold obsolete        30s

Настроим http фронтенд (порт 80)

frontend f_http
        bind *:80
        bind [::]:80
        mode http
     # отклоним невалидные юзер агенты
        http-request reject if { req.hdr(user-agent) -m len le 32 }
        http-request reject if { req.hdr(user-agent) -m sub evil }
     # ответим на запрос ACME
        http-request return status 200 content-type text/plain lf-string "%[path,field(-1,/)].${ACCOUNT_THUMBPRINT}\n" if { path_beg '/.well-known/acme-challenge/'}
        http-request deny if { path -m sub /. } # запрет доступа к скрытым файлам
        http-request redirect scheme https code 301 unless { ssl_fc }

Теперь настроим TCP фронтенд на 443 порту для SSL Passthrough, а так же сделаем доступным SSH на 443 порту.

frontend f_tcp
        bind *:443    # ipv4 SSL Passthrough
        bind [::]:443 # ipv6 SSL Passthrough
        mode tcp
        option tcplog
        tcp-request inspect-delay 3s
     # Ограничим частоту запросов
        stick-table type ip size 1m expire 10s store conn_cur
        tcp-request session track-sc0 src
        tcp-request session reject if { sc_conn_cur(0) gt 240 }
        tcp-request content capture req.ssl_sni len 10
        tcp-request content accept if { req_ssl_hello_type 1 } or !{ req_ssl_hello_type 1 }
     # Правила сопостовления 
        use_backend tcp-ssh if !{ req.ssl_hello_type 1 } { payload(0,7) -m bin 5353482d322e30 } or !{ req.ssl_hello_type 1 } { req.len 0 } #use_backend tcp-ssh if !{ req.ssl_hello_type 1 } { req.len 0 }

        use_backend tcp_to_https if { req.ssl_sni -i one.example.ru }

Сразу же добавим https фронтед

frontend f_https # 
        bind abns@frontendhttps.sock accept-proxy ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 strict-sni tfo
        http-request reject if { req.hdr(user-agent) -m sub evil }
        http-request deny if { path -m sub /. } # запрет доступа к скрытым файлам
        http-response set-header X-Content-Type-Options nosniff
        http-response set-header X-XSS-Protection 1;mode=block
        http-response set-header X-Frame-Options SAMEORIGIN
        http-after-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains;" # Устанавливаем HSTS
        stick-table type ip size 100k expire 5m store http_req_rate(10s)
        http-request track-sc0 src
        http-request deny deny_status 429 if { sc_http_req_rate(0) gt 300 }
        http-request return status 200 content-type text/plain lf-string "Status 200 OK! Your IP %[src]." if { path /https-status-443 }
Справка *_sni

req.ssl_sni - анализирует заголовок SNI для маршрутизации без SSL-терминации трафика и сквозной передачи зашифрованного трафика на бекенд.
ssl_fc_sni - Проводит SSL-терминацию трафика для передачи на http бекенды.

Теперь настало время для backend секции

backend tcp-ssh # Бекенд для SSH
        mode tcp
        option http-keep-alive
        timeout http-keep-alive 30s
        server ssh 127.0.0.1:22 check port 22
backend tcp_to_https # 
        mode tcp
        server s1 abns@frontendhttps.sock send-proxy-v2-ssl-cn tfo check 
        retry-on conn-failure empty-response response-timeout

Проверим конфигурацию haproxy haproxy -f /etc/haproxy/haproxy.cfg -c
Если ответ Configuration file is valid перезапустим командой systemctl restart haproxy

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

Начнем получать сертификаты, переключимся на пользователя acme:
sudo -u acme -s
Создадим первый запрос на сертификат для домена one:
acme.sh --issue -d one.example.ru --stateless --ecc
Произведем развертывания сертификата через deploy-hook выполним:

DEPLOY_HAPROXY_HOT_UPDATE=yes \
DEPLOY_HAPROXY_STATS_SOCKET=/var/run/haproxy/admin.sock \
DEPLOY_HAPROXY_PEM_PATH=/etc/haproxy/certs \
acme.sh --deploy -d one.example.ru --deploy-hook haproxy

Таким методом развертываете необходимое кол-во сертификатов, теперь можно установить задачу в crontab на обновление сертификатов командой acme.sh --install-cronjob .
Проверить сертификат можно командой:
echo "show ssl cert /etc/haproxy/certs/one.example.ru" | nc -U /var/run/haproxy/admin.sock | grep Status
Если при выполнении вы видите 'Status: Used' значит все прошло успешно, теперь можно перейти по адресу one.example.ru/https-status-443 и увидеть Status 200 OK! Your IP (Ваш IP), значит все работает, и будем приступать к следующему этапу, защите по списками FireHol и GeoIP по GeoIP так же можно маршрутизировать трафик для клиентов из разных стран или континентов.

Часть 2. Списки FireHol, GeoIP и настройка rate limit

Подготовим директории для хранения списков
mkdir /etc/haproxy/geoip /etc/haproxy/firehol /etc/haproxy/scripts
Напишем скрипт для загрузки firehol, и преобразуем его в чистый список IP и подсетей,
nano /etc/haproxy/scripts/firehol.sh

#!/bin/bash

# Определяем директории
TMP_DIR="/tmp/firehol"
DEST_DIR="/etc/haproxy/firehol"

# Создаем временную директорию
mkdir -p "$TMP_DIR"

# Скачиваем файлы
urls=(
    "https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level1.netset"
    "https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level2.netset"
    "https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_level3.netset"
    "https://raw.githubusercontent.com/firehol/blocklist-ipsets/master/firehol_abusers_1d.netset"
)

files=("level1.acl" "level2.acl" "level3.acl" "abusers_1d.acl")

for i in "${!urls[@]}"; do
    curl -s "${urls[$i]}" -o "$TMP_DIR/${files[$i]%.acl}.netset"
done

# Очищаем файлы от комментариев и пустых строк
for file in "${files[@]}"; do
    netset_file="$TMP_DIR/${file%.acl}.netset"
    acl_file="$TMP_DIR/$file"
    grep -vE '^\s*#|^\s*$' "$netset_file" > "$acl_file"
done

# Генерируем md5 для файлов
declare -A md5s
for file in "${files[@]}"; do
    md5s["$file"]=$(md5sum "$TMP_DIR/$file" | awk '{ print $1 }')
done

# Проверяем наличие файлов в целевой директории и обновляем их при необходимости
for file in "${files[@]}"; do
    dest_file="$DEST_DIR/$file"
    if [[ ! -f "$dest_file" ]] || [[ "$(md5sum "$dest_file" | awk '{ print $1 }')" != "${md5s[$file]}" ]]; then
        cp "$TMP_DIR/$file" "$dest_file"
        echo "Обновлен файл: $dest_file"
    fi
done

chown -R haproxy:haproxy /etc/haproxy/firehol/
# Удаляем временную директорию
rm -rf "$TMP_DIR"

Данный скрипт сформирует 4 файла в директории /etc/haproxy/firehol

Теперь добавим GeoIP списки IPv4+IPv6
nano /etc/haproxy/scripts/geoip.sh

#!/bin/bash
# https://github.com/Loyalsoldier/geoip/blob/release/GeoLite2-Country-Locations-en.csv

GEOIP_ACL="/etc/haproxy/geoip"
GEOIP_TEMP="/tmp"


function DownloadDB () {
        # Очистим старые файлы:
        rm -rf  $GEOIP_TEMP/{*.csv,*.zip}

        curl -o $GEOIP_TEMP/geip_csv.zip https://raw.githubusercontent.com/Loyalsoldier/geoip/release/GeoLite2-Country-CSV.zip && \
        unzip -j $GEOIP_TEMP/geip_csv.zip -d $GEOIP_TEMP "*IPv4.csv" "*en.csv" "*IPv6.csv"

        # Переименуем csv:
        find $GEOIP_TEMP -depth -type f -name '*IPv4.csv' -exec mv {} $GEOIP_TEMP/ips4.csv \;
        find $GEOIP_TEMP -depth -type f -name '*IPv6.csv' -exec mv {} $GEOIP_TEMP/ips6.csv \;
        cat $GEOIP_TEMP/ips4.csv $GEOIP_TEMP/ips6.csv > $GEOIP_TEMP/ips.csv
        find $GEOIP_TEMP -depth -type f -name '*en.csv' -exec mv {} $GEOIP_TEMP/countres.csv \;
}

function CreateAcl () {
        if [ ! -d $GEOIP_ACL ]; then
                mkdir -p $GEOIP_ACL
        fi

        while read line; do
                COUNTRY_CODE=$(echo $line | cut -d, -f5)
                COUNTRY_ID=$(echo $line | cut -d, -f1)

                CONTINENT_CODE=$(echo $line | cut -d, -f3)
                CONTINENT_ID=$(echo $line | cut -d, -f1)

                # Создадим списки
                cat $GEOIP_TEMP/ips.csv | grep $COUNTRY_ID | cut -d, -f1 > $GEOIP_ACL/$COUNTRY_CODE.list
                cat $GEOIP_TEMP/ips.csv | grep $CONTINENT_ID | cut -d, -f1 > $GEOIP_ACL/$CONTINENT_CODE.acl
        done <$GEOIP_TEMP/countres.csv
}

DownloadDB
CreateAcl
rm -rf  $GEOIP_TEMP/{*.csv,*.zip}
chown -R haproxy:haproxy /etc/haproxy/geoip/
systemctl restart haproxy

Данный скрипт сформирует файлы со списком IP/подсетей по странам и континентам в директории /etc/haproxy/geoip

Данные скрипты добавим в crontab для обновления например раз в сутки в 4ч утра:
0 4 * * * /etc/haproxy/scripts/firehol.sh && /etc/haproxy/scripts/geoip.sh && systemctl restart haproxy >/dev/null 2>&1

Настроим rate limit и подключим списки firehol, в секцию f_http добавим следующие строки после mode http, rate limit выставим на значение 30, после 30 запросов в течении 10 секунд, будет отклонять трафик с IP который превысил ограничение, в зависимости от сложности сайта limit нужно будет корректировать, для 80 порта ставлю меньше, т.к. там только acme и redirect на https.
через tcp-request connection silent-drop if подключим списки firehol, content будет сохранять для дальнейшего анализа, connection оборвет соединение без записи в журнал.
frontend f_http должен теперь выглядеть вот так:

Справка по tcp-request

tcp-request connection - работает на этапе установления соединения, прежде чем оно будет установлено, и если выполняется reject условие, немедленно завершает попытку подключения это полезно для защиты от явных IP адресов скамеров/спамеров и для защиты от DDoS, должно идти первым правилом.
tcp-request session - работает с установленной сессией TCP, если определенные условия выполняются. Например, для отклонения о превышении rate limit, немедленно завершает установленную сессию.
tcp-request content - используется для отклонения конкретного содержимого, которое передается в рамках уже установленных соединений. Это может быть полезно, для фильтрации определенных данных, например, запреты на конкретные запросы или паттерны.

Так же отличаются и действия:
reject - отклоняет соединение но отправляет клиенту ответ о завершении.
silent-drop - делает тоже что и reject только "тихо" без ответа клиенту.
accept - соответственно разрешает подключение по указанному правилу.
Установив '!' перед правилом например { src -f /etc/haproxy/geoip/RU.list }, оно будет инвертировано.

frontend f_http
        bind *:80
        bind [::]:80
        mode http
     # FireHOL IP List
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/level1.acl }
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/level2.acl }
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/level3.acl }
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/abusers_1d.acl } 
     # Ограничение запросов, дропаем на раннем этапе для http фронта
        stick-table type ip size 1m expire 10s store conn_cur
        tcp-request session track-sc0 src
        tcp-request session reject if { sc_conn_cur(0) gt 30 }

Теперь добавим в TCP фронтенд ограничение в 240 запросов за 10секунд, и списки firehol
frontend f_tcp должен теперь выглядеть вот так:

frontend f_tcp
        bind *:443    # ipv4 SSL Passthrough
        bind [::]:443 # ipv6 SSL Passthrough
        mode tcp
        option tcplog
        tcp-request inspect-delay 3s
     # FireHOL IP List
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/level1.acl }
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/level2.acl }
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/level3.acl }
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/abusers_1d.acl } 
     # Ограничим частоту запросов
        stick-table type ip size 1m expire 10s store conn_cur
        tcp-request session track-sc0 src
        tcp-request session reject if { sc_conn_cur(0) gt 240 }
        tcp-request session capture req.ssl_sni len 10
        tcp-request content capture req.ssl_sni len 10
        tcp-request content accept if { req_ssl_hello_type 1 } or !{ req_ssl_hello_type 1 } 

Про GeoIP блокировку ее добавить можно так, в секцию f_http и f_tcp внести следующую строку после firehol списков:
tcp-request connection reject if { src -f /etc/haproxy/geoip/AF.acl } тем самым в данном примере заблокировав все подсети Африканского континента, полный список континентов и стран доступен тут, установив '!' перед { src -f /etc/haproxy/geoip/AF.acl } разрешите трафик только от данного региона, и запретите весь остальной, в скрипте есть разделение, континенты с расширением acl, а файлы стран с расширением list.

Маршрутизация по странам настраивается аналогичным образом пример для TCP:
use_backend tcp_ru { req.ssl_sni -i example.ru } { src -f /etc/haproxy/geoip/RU.list }
В данном примере на домен example.ru смогут войти только клиенты с IP входящих в файл RU.list. можно пойти иным путем, например:
use_backend tcp_ru if { src -f /etc/haproxy/geoip/RU.list }
В данном варианте если клиент подключается с IP из Российского списка он будет отправлен на соответствующий backend tcp.

use_backend tcp_by if { src -f /etc/haproxy/geoip/BY.list }
А в данном примере клиенты с IP Белоруссии уйдут на соответствующий backend tcp.
Всех остальных которые не прошли по правилам отправим на дефолтный бекенд:
default_backend default_tcp

Для работы соответственно потребуется создать соответствующие бэкенды перед запуском.

Все тоже самое можно проделать на HTTP фронтэнде, на примере заблокируем клиентов из Белоруссии явно сообщив им об этом ошибкой 403 и текстом и их IP адресом:
http-request deny deny_status 403 content-type text/plain lf-string "Access denied, IP not from whitelist. Your IP %[src]." if { src -f /etc/haproxy/geoip/BY.list }

Или отправим клиентов из России на один бэкенд, а клиентов из Белоруссии на второй, и установим default_backend на тот случай если их IP нет в списках стран, пример:
Для России use_backend http_ru if { src -f /etc/haproxy/geoip/RU.list }
Для Белоруссии use_backend http_by if { src -f /etc/haproxy/geoip/BY.list }
И для тех кто не прошел по правилам стран:
default_backend http_default

Для Haproxy Enterprise (HAPEE) данные действия не требуются, есть модули MaxMind и Digital Element, которые упрощают работу с GeoIP.

Часть 3. mTLS и настройка УЦ для клиентских сертификатов

Первым делом загрузим XCA для удобства управления и выдачи сертификатов, я использую портативную версию, подготовим ROOTCA и SUBCA

При запуске XCA создайте БД, после открытия базы, сообщит что шифрование не безопасно, перейдите в "изменить" и поменяйте на "AES-256-CBC", так же в настройках измените алгоритм подписи по умолчанию на "SHA384" и отключите устаревшие расширения Netscape.

Настройка базы данных XCA
Настройка базы данных XCA

Создадим корневой сертификат для подписи, для этого перейдите в "Сертификаты" и "Новый сертификат"

Инструкция в картинках

Создадим корневой сертификат, которым подпишем промежуточный.

Первоисточник
Первоисточник

Настройте первоисточник, по умолчанию будет самозаверенный, алгоритм подписи SHA384 и шаблон выберите "CA"

Субъект
Субъект

Заполните субъект, в данном случае только внутреннее имя и CN нажмите сгенерировать ключ

Создание ключа
Создание ключа

Тип ключа выберете EC, и кривую prime256v1 или secp384r1

Расширения
Расширения

Настроим расширения, Установим длину цепочки т.е. сколько может быть промежуточных сертификатов, становим Key identifier, настроим срок действия, и SAN, через "редактировать" добавить "Copy CN"

Применения ключа
Применения ключа

Настроим применения ключа, выставим Critical. Можно сохранять

Теперь выдадим промежуточный

Первоисточник
Первоисточник

Теперь в "Подписание" укажем что хотим подписать данный сертификат нашим только что созданным корневым сертификатом ROOTCA.

Субъект
Субъект

Заполните субъект, создайте ключ по аналогии с корневым сертификатом.

Расширения
Расширения

Настройте расширения, идентично как в ROOTCA, с единственным отличием, промежуточный ЦС не будет выдавать сертификаты УЦ, для этого установите длину цепочки как 0

Применение ключа
Применение ключа

Аналогично ROOTCA

Теперь создадим ключ клиента и подпишем созданным SUBCA

Первоисточник
Первоисточник

Заполняем первоисточник, выбираем для подписи SUBCA и шаблон TLS_client

Субъект
Субъект

Заполним субъект, в CN пишем наименование клиента, в данном примере client, а во внутреннее имя добавлю имя домена, для удобства поиска, создаем ключ.

Расширения
Расширения

Обязательно в расширениях ставим все галочки Key indetifier, и так же SAN копируем CN

Применение ключа
Применение ключа

Установим значения critical.

Теперь можно экспортировать сертификат клиента в формате цепочки PKCS#12 *.pfx

Экспорт
Экспорт

Теперь необходимо установить сертификат в "Личное" для Windows, и для Android в "Сертификаты VPN и приложений"

Теперь необходимо сохранить ROOTCA и SUBCA в файл pem по отдельности, и в один файл, назовем его fullca.pem

CRL
CRL

Теперь необходимо получить файл revoked.crl, для этого, на SUBCA кликаем ПКМ>ЦС>Сгенерировать CRL, для удобства выставляйте пару лет.

Аналогичные действия и для ROOTCA

Экспортируем SUBCA и ROOTCA в файл revoked.crl именно в таком порядке.

Теперь когда у нас есть файлы ROOTCA.pem, SUBCA.pem, fullca.pem и revoked.crl можем приступать к настройке фронтенда.

В секцию f_tcp под tcp_to_https добавим:
use_backend tcp_to_mtls if { req.ssl_sni -i auth.example.ru }
Теперь добавим backend

backend tcp_to_mtls
        mode tcp
        server mtls abns@frontendmtls.sock send-proxy-v2-ssl-cn tfo check
        retry-on conn-failure empty-response response-timeout

Настроим фронтенд

frontend f_mtls
        bind abns@frontendmtls.sock tfo accept-proxy ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 ssl-min-ver TLSv1.3 strict-sni verify required ca-file /etc/haproxy/ca/SUBCA.pem ca-verify-file /etc/haproxy/ca/fullca.pem crl-file /etc/haproxy/ca/revoked.crl crt-ignore-err 10,23 
        http-request reject if { req.hdr(user-agent) -m sub evil }
        http-request deny if { path -m sub /. } # запрет доступа к скрытым файлам
        stick-table type ip size 100k expire 5m store http_req_rate(30s)
        http-request track-sc0 src
        http-request deny deny_status 429 if { sc_http_req_rate(0) gt 60 }
        http-after-response set-header Strict-Transport-Security "max-age=16000000;"
        http-request return status 403 content-type text/plain lf-string "Your certificate has expired, please contact the administrator" if { ssl_c_verify 10 } #text/html file /certexpired.html
        http-request return status 403 content-type text/plain lf-string "Your certificate has been revoked, please contact the administrator" if { ssl_c_verify 23 } #text/html file /certrevoked.html
        http-request return status 200 content-type text/plain lf-string "Status 200 OK! Your IP %[src]." if { path /mtls-status-443 }
frontendmtls

verify required - отвечает за обязательную проверку сертификата клиента.
ca-file - файл промежуточного сертификата которым подписаны клиенты.
ca-verify-file - включает в себя корневой сертификат и промежуточный (добавлен в HAProxy 2.2 ), отправляет клиенту более короткий список CA в сообщении SERVER HELLO, который будет использоваться для проверки.
crt-ignore-err - позволяет отвечать клиенту кодом ошибки если код "10" - сертификат истек или еще не наступил, "23" - сертификат отозван.
ssl-min-ver TLSv1.3 - явно указываем TLSv1.3 для защиты клиентского сертификата без отката к TLSv1.2

Создадим для хранения наших файлов директорию:
mkdir /etc/haproxy/ca
Переместим файлы SUBCA.pem fullca.pem и revoked.crl в директорию удобным для вас способом.
Выставим права и владельца:
chmod 700 /etc/haproxy/ca/
chown -R haproxy:haproxy /etc/haproxy/ca/

Проверяем конфигурацию командой haproxy -f /etc/haproxy/haproxy.cfg -c
Если все ок, перезапускаем сервис systemctl restart haproxy

Проверяем через браузер, заходим на auth.example.ru и если у вас появился запрос сертификата а после предоставления сертификата получаете "Status 200 OK! Your IP" значит все сделано правильно. можете добавлять свои пути/домены для маршрутизации, домены соответственно нужно будет добавить и f_tcp например так:
use_backend tcp_to_mtls if { req.ssl_sni -i auth.example.ru } || { req.ssl_sni -i auth2.example.ru }
Или добавить под tcp_to_mtls аналогичную строку только с другим доменом.

Часть 4. Метрики

Подключим выгрузку метрик в Prometheus через встроенный экспортер в Haproxy, для этого есть два пути, 1. через путь в виде { path_beg /path/ } и один домен или через доп домен, так же защитим наш сервис паролем либо mTLS создав дополнительный SUBCA для метрик.

Выгрузка через второй домен, добавляем в f_tcp под tcp_to_mtls
use_backend tcp_to_metrics if { req.ssl_sni -i metrics.example.ru }

Теперь добавим frontend для метрик и секцию с паролем и разрешим подключаться только с Российских IP, в идеале разрешить только подсети вашего провайдера или если есть белый статичный IP разрешить его (метрики так же можно скрыть за mTLS):

frontend f_metrics
        bind abns@metricshttps.sock accept-proxy ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 strict-sni tfo 
        tcp-request connection accept if { src -f /etc/haproxy/geoip/RU.list } 
        http-request reject if { req.hdr(user-agent) -m sub evil }
        http-request deny if { path -m sub /. } # запрет доступа к скрытым файлам
        stick-table type ip size 100k expire 5m store http_req_rate(30s)
        http-request track-sc0 src
        http-request deny deny_status 429 if { sc_http_req_rate(0) gt 60 }
        http-after-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains;"
        http-response set-header X-Content-Type-Options nosniff
        http-response set-header X-XSS-Protection 1;mode=block
        http-response set-header X-Frame-Options SAMEORIGIN
        stats enable
        stats uri /path/stats
        stats realm Haproxy\ Statistics
        stats refresh 10s
        stats show-legends
        stats hide-version
        http-request use-service prometheus-exporter if { path_beg /haproxy-exporter-path/ }
userlist metrics
        user metrics insecure-password secretpassword

Метрики будут доступны по адресу https://metrics.example.ru/haproxy-exporter-path/metrics
с логином metrics и паролем secretpassword, для Grafana использую панель Haproxy 2 Full (ID 12693)

Так же, легким движением рук можем изменить доступ по паролю на доступ по сертификату для этого подготовим промежуточный УЦ и сделаем клиента для Prometheus по аналогии с mTLS frontend, пример конфигурации:

frontend f_metrics_mtls
        bind abns@metricshttps.sock tfo accept-proxy ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 ssl-min-ver TLSv1.3 strict-sni verify required ca-file /etc/haproxy/ca/metrics.pem ca-verify-file /etc/haproxy/ca/metricsfull.pem crl-file /etc/haproxy/ca/metrics.crl crt-ignore-err 10,23
        tcp-request connection accept if { src -f /etc/haproxy/geoip/RU.list } 
        http-request reject if { req.hdr(user-agent) -m sub evil } 
        http-request deny if { path -m sub /. } # запрет доступа к скрытым файлам
        stick-table type ip size 100k expire 5m store http_req_rate(30s)
        http-request track-sc0 src
        http-request deny deny_status 429 if { sc_http_req_rate(0) gt 90 }
        http-after-response set-header Strict-Transport-Security "max-age=16000000;"
        http-request return status 403 content-type text/plain lf-string "Your certificate has expired, please contact the administrator" if { ssl_c_verify 10 } 
        http-request return status 403 content-type text/plain lf-string "Your certificate has been revoked, please contact the administrator" if { ssl_c_verify 23 } 
        http-request use-service prometheus-exporter if { path_beg /haproxy-exporter-path/ } # exporter haproxy, ссылка для prometheus ( /path_beg/metrics ) 
#        use_backend node-exporter if { path_beg -i /node-exporter-path/ } # exporter сервера

Пример конфигурации Prometheus для авторизации по сертификату:

  - job_name: 'Haproxy'
    scheme: https
    static_configs:
      - targets: ['mdata.example.ru:443']
    metrics_path: /mypath/metrics
    tls_config:
#       ca_file: 'ca.crt'
       cert_file: 'client.crt'
       key_file: 'client.key'

Добавить node-exporter со стандартной установкой на 9100 порту можно так:
В фронтенд метрик добавить use_backend node-exporter if { path_beg -i /node-exporter-path }
Теперь добавим backend, перед передачей на бекенд удалим префикс пути:

backend node-exporter
        mode http
        http-request replace-path /node-exporter-path(/)?(.*) /\2
        server s1 127.0.0.1:9100

Теперь добавим Web статистику вставим следующий например перед фронтедом метрик:

frontend f_stats
        bind abns@stats.sock
        stats enable
        stats uri /stats
        stats realm Haproxy\ Statistics
        stats refresh 10s
        stats show-legends
        stats hide-version
        stats show-modules
backend stats
        mode http
        http-request replace-path /haproxy(/)?(.*) /\2
        server s1 abns@stats.sock

Теперь добавим во фронтенд mTLS правило:
use_backend stats if { path_beg -i /haproxy/ }
Статистика будет доступна по адресу https://auth.example.ru/haproxy/stats

Примеры из Grafana
HTTP
HTTP
Connections
Connections

Часть 5. 3X-UI, Reality TCP, XHTTP и DoH

Для начала создадим пользователя и группу для работы панели
adduser --system --disabled-password --disabled-login --home /usr/local/x-ui --quiet --force-badname --group xray

Для установки выполним:
bash <(curl -Ls https://raw.githubusercontent.com/mhsanaei/3x-ui/master/install.sh)
При установке меняем порт на 49004 или удобный вам.
После завершения скрипт покажет текущую конфигурацию для подключения, адрес пути и логин с паролем, сохраняем пригодиться позже.

После установки проведем настройку:
1. Откроем юнит x-ui на редактирование nano /etc/systemd/system/x-ui.service
Необходимо после [Service] добавить следующие строки:
User=xray
Group=haproxy

2. Создадим директорию для Socket: mkdir /var/lib/haproxy/xui
3. Изменим владельца файлов x-ui и директории для Socket:
chown -R xray:xray /etc/x-ui/ chown -R xray:xray /usr/local/x-ui/ chown -R xray:haproxy /var/lib/haproxy/xui
4. Перечитаем конфигурацию systemd: systemctl daemon-reload
5. Перезапустим сервис x-ui и сразу проверим статус работы:
systemctl restart x-ui.service && systemctl status x-ui.service

Перейдем к настройке Haproxy:
В mTLS фронтенд добавляем
use_backend http_3xui if { path_beg /defaultpath/ } || { path_beg /3xui-path }
В defaultpath вставляем путь выданный панелью при установке, можно оставить как есть, во второе правило, которое придумаете сами.
Добавим backend:

backend http_3xui # Админ панель 3X-UI
        mode http
        option forwardfor if-none
        http-request add-header X-Real-Ip %[src]
        http-request set-header Host %[req.hdr(Host)]
        http-request set-header X-Forwarded-For %[src]
        http-request set-header X-Forwarded-Host %[req.hdr(host)]
        http-request set-header X-Forwarded-Proto https if { ssl_fc }
        server s1 127.0.0.1:49004 check port 49004

Перезапускаем haproxy и проверяем, если панель открылась перейдем к настройке подключений, создадим TCP REALITY:

Пример настроек inbound

1.Укажем удобное примечание например: tcp_to_reality
2.Протокол: VLESS
3.Порт не меняем
4.Протокол(транспорт): TCP(RAW)
5.Proxy Protocol: Enable
6.Sockopt: Enable
7.TCP Fast Open: Enable
8.TCP Congestion: bbr
9.Tproxy: off
10.External Proxy: enable
[Тот же] [drive.example.ru] [443]Безопасность: Reality
11.Xver: 1
12.uTLS на ваш выбор, у меня firefox
13.Dest (Target): /var/lib/haproxy/fakehttps1.sock
14.SNI: drive.example.ru
15.Sniffing: включить по умолчанию.

Конфигурация1
Конфигурация1
Конфигурация 2
Конфигурация 2

Настроим HAProxy, в f_tcp добавим следующую строку под use_backend:
use_backend tcp_to_reality if { req.ssl_sni -i drive.example.ru }

Добавим бекенд:

backend tcp_to_reality
        mode tcp
        server r1 /x-ui/reality.sock send-proxy tfo check
        retry-on conn-failure empty-response response-timeout

Теперь добавим фронтенд для маскировки например под nextcloud:

frontend f_https_reality
        bind /var/lib/haproxy/fakehttps1.sock accept-proxy ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 ssl-min-ver TLSv1.3 strict-sni mode 660 user haproxy group haproxy
        http-request reject if { req.hdr(user-agent) -m sub evil }
        acl url_discovery path /.well-known/caldav /.well-known/carddav
        http-request redirect location /remote.php/dav/ code 301 if url_discovery
        http-request deny if { path -m sub /. }
        stick-table type ip size 100k expire 2m store http_req_rate(10s)
        http-request track-sc0 src
        http-request deny deny_status 429 if { sc_http_req_rate(0) gt 240 }
        http-response set-header X-Content-Type-Options nosniff
        http-response set-header X-XSS-Protection 1;mode=block
        http-response set-header X-Frame-Options SAMEORIGIN
        http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains;" # Устанавливаем HSTS
        http-request return status 200 content-type text/plain lf-string "Status 200 OK! Your IP %[src]." if { path / }
#        default_backend nextcloud

Сохраняем и проверяем конфигурацию:
haproxy -f /etc/haproxy/haproxy.cfg -c
Если ошибок нет, перезапускаем и проверяем подключение. После разворачиваем nextcloud убираем http-request return status 200 и раскомментируем default_backend nextcloud
Создаем бекенд для nextcloud:

backend nextcloud
        mode http
        option forwardfor
        http-request set-header X-Forwarded-Proto https if { ssl_fc }
        http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
        http-request set-header X-Forwarded-Host %[req.hdr(host)]
        http-request set-header X-Forwarded-For %[src]
        http-request add-header X-Real-Ip %[src]
        server s1 127.0.0.1:49009

Таким образом можно добавить неограниченной количество inbounds

Настроим XHTTP:

Пример

Создаем inbound со следующими настройками:
1. Примечание на ваш выбор.
2. Протокол: VLESS
3. Мониторинг IP: /var/lib/haproxy/x-ui/xhttp1.sock,0660
4. Протокол(транспорт): XHTTP
5. Хост: ваш домен пример: corp.example.ru
6. Путь: /secretpath
7. Mode: auto
8. Sockopt: enable
9. TCP Fast Open: enable
10. External Proxy: enable
[TLS] [corp.example.ru] [443]
11. Безопасность: пусто
12. Sniffing: включить по умолчанию.

Пример в картинках
XHTTP 1
XHTTP 1
XHTTP 2
XHTTP 2

Добавим во фронтенд f_https:
use_backend xhttp if { req.hdr(host) -i corp.example.ru } { path_beg /secretpathbeg/ } || { path /secretpath }
Добавим бекенд:

backend xhttp # XHTTP: Beyond REALITY
        mode http
        option forwardfor if-none
        http-request add-header X-Real-Ip %[src]
        http-request set-header Host %[req.hdr(Host)]
        http-request set-header X-Forwarded-For %[src]
        http-request set-header X-Forwarded-Host %[req.hdr(host)]
        retry-on conn-failure empty-response response-timeout
        server x /x-ui/xhttp1.sock send-proxy tfo check

Аналогично проверяем конфигурацию HAProxy, и перезапускаем.

Настроим DoH, есть два пути, в docker и без, пойдем вариантом без docker.
Выполняем скрипт:
curl -s -S -L https://raw.githubusercontent.com/AdguardTeam/AdGuardHome/master/scripts/install.sh | sh -s -- -v
Установиться с правами root, остановим сервис systemctl stop AdGuardHome.service,
Настроим конфигурацию: nano /opt/AdGuardHome/AdGuardHome.yaml

AdGuardHome.yaml
http:
  pprof:
    port: 6060
    enabled: false
  address: 127.0.0.1:49005
  session_ttl: 720h
users:
  - name: demo
    password: $2y$10$9ho8XPNKtOBVRlfgQV8CFOVCOsFVpjXAHZRzICh0PDBA.dsaCbioO
auth_attempts: 5
block_auth_min: 15
http_proxy: ""
language: ""
theme: auto
dns:
  bind_hosts:
    - 127.0.0.2
  port: 53
  anonymize_client_ip: false
  ratelimit: 20
  ratelimit_subnet_len_ipv4: 24
  ratelimit_subnet_len_ipv6: 56
  ratelimit_whitelist: []
  refuse_any: true
  upstream_dns:
    - 127.0.0.53
  upstream_dns_file: ""
  bootstrap_dns:
    - 9.9.9.10
    - 149.112.112.10
    - 2620:fe::10
    - 2620:fe::fe:10
  fallback_dns: []
  upstream_mode: load_balance
  fastest_timeout: 1s
  allowed_clients: []
  disallowed_clients: []
  blocked_hosts:
    - version.bind
    - id.server
    - hostname.bind
  trusted_proxies:
    - 127.0.0.0/8
    - ::1/128
  cache_size: 4194304
  cache_ttl_min: 0
  cache_ttl_max: 0
  cache_optimistic: false
  bogus_nxdomain: []
  aaaa_disabled: false
  enable_dnssec: false
  edns_client_subnet:
    custom_ip: ""
    enabled: false
    use_custom: false
  max_goroutines: 300
  handle_ddr: true
  ipset: []
  ipset_file: ""
  bootstrap_prefer_ipv6: false
  upstream_timeout: 10s
  private_networks: []
  use_private_ptr_resolvers: true
  local_ptr_upstreams: []
  use_dns64: false
  dns64_prefixes: []
  serve_http3: false
  use_http3_upstreams: false
  serve_plain_dns: true
  hostsfile_enabled: true
tls:
  enabled: false
  server_name: ""
  force_https: false
  port_https: 0
  port_dns_over_tls: 0
  port_dns_over_quic: 0
  port_dnscrypt: 0
  dnscrypt_config_file: ""
  allow_unencrypted_doh: true
  certificate_chain: ""
  private_key: ""
  certificate_path: ""
  private_key_path: ""
  strict_sni_check: false
querylog:
  dir_path: ""
  ignored: []
  interval: 2160h
  size_memory: 1000
  enabled: true
  file_enabled: true
statistics:
  dir_path: ""
  ignored: []
  interval: 24h
  enabled: true
filters:
  - enabled: false
    url: https://raw.githubusercontent.com/AdguardTeam/FiltersRegistry/master/filters/filter_1_Russian/filter.txt
    name: Ru filter
    id: 1736931102
  - enabled: false
    url: https://raw.githubusercontent.com/rebelion76/bankiru_plus_adblock_list/master/bankiru_plus.txt
    name: bankiru_plus
    id: 1736931124
  - enabled: false
    url: https://raw.githubusercontent.com/deathbybandaid/piholeparser/master/Subscribable-Lists/CountryCodesLists/Russia.txt
    name: Подписки RU
    id: 1736931125
  - enabled: false
    url: https://easylist-downloads.adblockplus.org/ruadlist.txt
    name: ruadlist
    id: 1736931126
  - enabled: false
    url: https://raw.githubusercontent.com/deathbybandaid/piholeparser/master/Subscribable-Lists/ParsedBlacklists/RU-AdList.txt
    name: RU-AdList
    id: 1736931127
  - enabled: false
    url: https://easylist-downloads.adblockplus.org/cntblock.txt
    name: RU Adlist Counters
    id: 1736931128
  - enabled: false
    url: https://gist.githubusercontent.com/drewpayment/4a316423f7ff7df9dce63a041c478486/raw/6a509d262d7dd687c645349be3fc6217c0764768/AdGuard%2520Russian%2520filter%2520-%2520Rules.txt 
    name: AdGuard Russian filter
    id: 1736931118
  - enabled: false
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_1.txt
    name: AdGuard DNS filter
    id: 1
  - enabled: false
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_2.txt
    name: AdAway Default Blocklist
    id: 2
  - enabled: false
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_4.txt
    name: Dan Pollock's List
    id: 1736931097
  - enabled: false
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_30.txt
    name: Phishing URL Blocklist (PhishTank and OpenPhish)
    id: 1736931098
  - enabled: false
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_50.txt
    name: uBlock₀ filters – Badware risks
    id: 1736931099
  - enabled: false
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_33.txt
    name: Steven Black's List
    id: 1736931100
  - enabled: false
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_59.txt
    name: AdGuard DNS Popup Hosts filter
    id: 1736931101
  - enabled: false
    url: https://raw.githubusercontent.com/AdguardTeam/FiltersRegistry/master/filters/filter_3_Spyware/filter.txt
    name: Spyware
    id: 1736931103
  - enabled: false
    url: https://raw.githubusercontent.com/AdguardTeam/FiltersRegistry/master/filters/filter_17_TrackParam/filter.txt
    name: Trackers
    id: 1736931104
  - enabled: false
    url: https://raw.githubusercontent.com/AdguardTeam/FiltersRegistry/master/filters/filter_15_DnsFilter/filter.txt
    name: Ad Guard filter
    id: 1736931107
  - enabled: false
    url: https://raw.githubusercontent.com/AdguardTeam/FiltersRegistry/master/filters/filter_11_Mobile/filter.txt
    name: Mobile
    id: 1736931108
  - enabled: false
    url: https://raw.githubusercontent.com/AdguardTeam/FiltersRegistry/master/filters/filter_21_Annoyances_Other/filter.txt
    name: Раздражающие элементы
    id: 1736931109
  - enabled: false
    url: https://raw.githubusercontent.com/AdguardTeam/FiltersRegistry/master/filters/filter_22_Annoyances_Widgets/filter.txt
    name: Widgets
    id: 1736931110
  - enabled: false
    url: https://raw.githubusercontent.com/AdguardTeam/FiltersRegistry/master/filters/filter_20_Annoyances_MobileApp/filter.txt
    name: Widgets_Mobile
    id: 1736931111
  - enabled: false
    url: https://raw.githubusercontent.com/AdguardTeam/FiltersRegistry/master/filters/filter_19_Annoyances_Popups/filter.txt
    name: Всплывайки сайты
    id: 1736931112
  - enabled: false
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_23.txt
    name: Windows Spy list
    id: 1736931113
  - enabled: false
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_8.txt
    name: nocoin
    id: 1736931114
  - enabled: false
    url: https://blocklistproject.github.io/Lists/smart-tv.txt
    name: smart-tv
    id: 1736931115
  - enabled: false
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_49.txt
    name: HaGeZi's Ultimate Blocklist
    id: 1736931116
  - enabled: false
    url: https://adguardteam.github.io/HostlistsRegistry/assets/filter_27.txt
    name: OISD Blocklist Big
    id: 1736931117
  - enabled: false
    url: https://raw.githubusercontent.com/hant0508/uBlock-filters/master/filters.txt
    name: uBlock-filters
    id: 1736931119
  - enabled: false
    url: https://raw.githubusercontent.com/AdguardTeam/FiltersRegistry/master/filters/filter_10_Useful/filter.txt
    name: AdguardTeam
    id: 1736931120
  - enabled: false
    url: https://filters.adtidy.org/extension/chromium-mv3/filters/24.txt
    name: AdGuard Quick Fixes filter MV3
    id: 1736931121
  - enabled: false
    url: https://gitlab.com/eyeo/anti-cv/abp-filters-anti-cv/-/raw/master/russian.txt
    name: Russian anti-cv
    id: 1736931122
  - enabled: false
    url: https://filters.adtidy.org/extension/ublock/filters/1_optimized.txt
    name: ublock optimized
    id: 1736931123
whitelist_filters: []
user_rules: []
dhcp:
  enabled: false
  interface_name: ""
  local_domain_name: lan
  dhcpv4:
    gateway_ip: ""
    subnet_mask: ""
    range_start: ""
    range_end: ""
    lease_duration: 86400
    icmp_timeout_msec: 1000
    options: []
  dhcpv6:
    range_start: ""
    lease_duration: 86400
    ra_slaac_only: false
    ra_allow_slaac: false
filtering:
  blocking_ipv4: ""
  blocking_ipv6: ""
  blocked_services:
    schedule:
      time_zone: Local
    ids: []
  protection_disabled_until: null
  safe_search:
    enabled: false
    bing: true
    duckduckgo: true
    ecosia: true
    google: true
    pixabay: true
    yandex: true
    youtube: true
  blocking_mode: default
  parental_block_host: family-block.dns.adguard.com
  safebrowsing_block_host: standard-block.dns.adguard.com
  rewrites: []
  safe_fs_patterns:
    - /opt/AdGuardHome/userfilters/*
  safebrowsing_cache_size: 1048576
  safesearch_cache_size: 1048576
  parental_cache_size: 1048576
  cache_time: 30
  filters_update_interval: 24
  blocked_response_ttl: 10
  filtering_enabled: true
  parental_enabled: false
  safebrowsing_enabled: false
  protection_enabled: true
clients:
  runtime_sources:
    whois: true
    arp: true
    rdns: true
    dhcp: true
    hosts: true
  persistent: []
log:
  enabled: true
  file: ""
  max_backups: 0
  max_size: 100
  max_age: 3
  compress: false
  local_time: false
  verbose: false
os:
  group: ""
  user: ""
  rlimit_nofile: 0
schema_version: 29

Основное изменение это включение allow_unencrypted_doh: true
для данного примера, логин и пароль demo, и используется системный резолвер, порт панели 49005, добавлены списки для блокировки, по умолчанию отключены.

Подготовим HAProxy, во фронтенд f_tcp добавим следующую строку:
use_backend tcp_to_mtls if { req.ssl_sni -i ns1.example.ru }

Для понижения прав, необходимо или понизить привилегированный порт, или поднять порт dns например на 5353.

В f_https добавим:
use_backend http_adh if { ssl_fc_sni -i corp.example.ru } { path_beg -i /secretpathdns/ }
Добавим бэкенд:

backend http_adh # DOH AdGuardHome
        mode http
        option forwardfor if-none
        http-request add-header X-Real-Ip %[src]
        http-request set-header Host %[req.hdr(Host)]
        http-request set-header X-Forwarded-For %[src]
        http-request set-header X-Forwarded-Host %[req.hdr(host)]
        http-request set-header X-Forwarded-Proto https if { ssl_fc }
        http-request replace-path /secretpath(/)?(.*) /\2
        server dns 127.0.0.1:49005 check port 49005

{ path_beg -i /secretpathdns/ } нужен для защиты dns-query, перед отправкой на бекенд путь будет удален, и запрос дойдет без ошибок к DoH, адрес для DoH следующий https://corp.examle.ru/secretpath/dns-query

Теперь же пришло добавить панель управления AdGuardHome, в f_tcp добавим следующую строку:
use_backend tcp_to_mtls if { req.ssl_sni -i ns1.example.ru }
Теперь внесем в f_mtls следующую строку:
use_backend http_adh if { ssl_fc_sni -i ns1.example.ru }

Снова проверяем конфигурацию и перезагружаем HAProxy.

Основное изменение это включение allow_unencrypted_doh: true для данного примера, логин и пароль demo, и используется системный резолвер, порт панели 49005

Подготовим HAProxy, во фронтенд f_tcp добавим следующую строку:
use_backend tcp_to_mtls if { req.ssl_sni -i ns1.example.ru }

Для понижения прав, необходимо или понизить привилегированный порт, или поднять порт dns например на 53535, создать пользователя, и необходимо изменить следующие:
os:
group: ""
user: ""

На созданного пользователя: например adguard:
os:
group: adguard
user: adguard

В f_https добавим:
use_backend http_adh if { ssl_fc_sni -i corp.example.ru } { path_beg -i /secretpathdns/ }
Добавим бэкенд:

backend http_adh # DOH AdGuardHome
        mode http
        option forwardfor if-none
        http-request add-header X-Real-Ip %[src]
        http-request set-header Host %[req.hdr(Host)]
        http-request set-header X-Forwarded-For %[src]
        http-request set-header X-Forwarded-Host %[req.hdr(host)]
        http-request set-header X-Forwarded-Proto https if { ssl_fc }
        http-request replace-path /secretpath(/)?(.*) /\2
        server dns 127.0.0.1:49005 check port 49005

{ path_beg -i /secretpathdns/ } нужен для защиты dns-query, перед отправкой на бекенд путь будет удален, и запрос дойдет без ошибок к DoH, адрес для DoH следующий https://corp.examle.ru/secretpath/dns-query Теперь же пришло добавить панель управления AdGuardHome, в f_tcp добавим следующую строку: use_backend tcp_to_mtls if { req.ssl_sni -i ns1.example.ru } Теперь внесем в f_mtls следующую строку: use_backend http_adh if { ssl_fc_sni -i ns1.example.ru } Снова проверяем конфигурацию и перезагружаем HAProxy.

Итог по 3X-UI панель работает за mTLS, кроме сервиса подписки, она доступна из вне по длинному пути, сами inbound работают через unix socket что должно обеспечить более высокую производительность в связке с TFO, REALITY развернут методом steal-oneself, при котором мы не зависим от чужого сайта/сертификата, ответ более быстрый, следовательно работать будет быстрее.

Часть 6. Установка сертификата

Для выпуска сертификата для сервиса, потребуется сделать следующие на примере ocserv:
1. Создаем директорию: mkdir /etc/ocserv/ssl
2. Добавляем пользователя acme в группу ocserv: adduser acme ocserv
3. Меняем владельца: chown ocserv:ocserv /etc/ocserv/ssl
4.Изменяем права: chmod 770 /etc/ocserv/ssl

Переключаемся на пользователя acme: sudo -u acme -s

Получим сертификат: acme.sh --issue -d example.com --stateless
Установим сертификат и перезапустим ocserv:

acme.sh --install-cert -d example.ru \
--cert-file /etc/ocserv/ssl/example.ru.pem \
--key-file /etc/ocserv/ssl/example.ru.key \
--fullchain-file /etc/ocserv/ssl/fullchain.ru.pem \
--reloadcmd "systemctl restart ocserv"

На этом все, проделываем аналогичным способом для ваш сервисов.

Заключение

В данной статье показал пример как можно защитить сервисы за mTLS, пример работы с GeoIP выгрузка в Prometheus из HAProxy exporter, и обновил информацию по REALITY.

В статье используется множество поддоменов, поменяйте на свои соответственно, аналогично и с path.

Полный конфигурации haproxy.cfg
global
        log /dev/log    local0
        log /dev/log    local1 notice
        chroot /var/lib/haproxy
        stats socket /var/run/haproxy/admin.sock level admin mode 660
        setenv ACCOUNT_THUMBPRINT '*ACCOUNT_THUMBPRINT*' # поменять на полученный из acme
        stats timeout 30s
        user    haproxy
        group   haproxy
        daemon
    # https://ssl-config.mozilla.org/
    # Улучшенная безопастность и совместимость со старыми браузерами ( Intermediate ) Supports Firefox 27, Android 4.4.2, Chrome 31, Edge, IE 11 on Windows 7, Java 8u31, OpenSSL 1.0.1, Opera 20, Safari 9
        ssl-default-bind-curves X25519:prime256v1:secp384r1 #:secp521r1:X448
        ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305
        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-bind-options prefer-client-ciphers ssl-min-ver TLSv1.2 no-tls-tickets

        ssl-default-server-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305 
        ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
        ssl-default-server-options ssl-min-ver TLSv1.2 no-tls-tickets

    # TLS 1.3 современная безопасность без отката к TLS 1.2  ( Modern ) Supports Firefox 63, Android 10.0, Chrome 70, Edge 75, Java 11, OpenSSL 1.1.1, Opera 57, Safari 12.1
#        ssl-default-bind-curves X25519:prime256v1:secp384r1
#        ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
#        ssl-default-bind-options prefer-client-ciphers ssl-min-ver TLSv1.3 no-tls-tickets

#        ssl-default-server-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
#        ssl-default-server-options ssl-min-ver TLSv1.3 no-tls-tickets

        ssl-dh-param-file /etc/haproxy/dh4096.pem # openssl dhparam -out /etc/haproxy/dh4096.pem 4096
#        tune.ssl.default-dh-param 2048
      # Тюнинг http/2
        tune.h2.initial-window-size 536870912 # Увеличиваем начальный размер окна для входящих и исходящих соединений.
        tune.h2.fe.max-concurrent-streams 512 # Установим кол-во одновременных потоков на входящие соединений.
        tune.h2.be.max-concurrent-streams 512 # Установим кол-во одновременных потоков на исходящие соединений.
#------------------------------------------
defaults
       log global
       mode http
       option httplog
       option dontlognull
       timeout connect 40s
       timeout client  1m
       timeout server  1m
       timeout tunnel 1h
       timeout http-request 30s
       errorfile 400 /etc/haproxy/errors/400.http
       errorfile 403 /etc/haproxy/errors/403.http
       errorfile 408 /etc/haproxy/errors/408.http
       errorfile 500 /etc/haproxy/errors/500.http
       errorfile 502 /etc/haproxy/errors/502.http
       errorfile 503 /etc/haproxy/errors/503.http
       errorfile 504 /etc/haproxy/errors/504.http
#------------------------------------------
resolvers dnsserver
        nameserver ns1 127.0.0.1:53
        parse-resolv-conf
        resolve_retries       3
        timeout resolve       1s
        timeout retry         1s
        hold other           30s
        hold refused         30s
        hold nx              30s
        hold timeout         30s
        hold valid           10s
        hold obsolete        30s
#-----------------------------------
frontend f_http
        bind *:80
        bind [::]:80
        mode http
     # FireHOL IP List
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/level1.acl }
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/level2.acl }
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/level3.acl }
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/abusers_1d.acl }
     # Ограничение запросов, дропаем на раннем этапе для http фронта
        stick-table type ip size 1m expire 10s store conn_cur
        tcp-request session track-sc0 src
        tcp-request session reject if { sc_conn_cur(0) gt 60 }
     # отклоним невалидные юзер агенты
        http-request reject if { req.hdr(user-agent) -m len le 32 }
        http-request reject if { req.hdr(user-agent) -m sub evil }
     # ответим на запрос ACME
        http-request return status 200 content-type text/plain lf-string "%[path,field(-1,/)].${ACCOUNT_THUMBPRINT}\n" if { path_beg '/.well-known/acme-challenge/' }
        http-request deny if { path -m sub /. } # запрет доступа к скрытым файлам
        http-request redirect scheme https code 301 unless { ssl_fc }
#------------------------------------------
frontend f_tcp
        bind *:443    # ipv4 SSL Passthrough
        bind [::]:443 # ipv6 SSL Passthrough
        mode tcp
        option tcplog
        tcp-request inspect-delay 3s
     # FireHOL IP List
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/level1.acl }
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/level2.acl }
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/level3.acl }
        tcp-request connection silent-drop if { src -f /etc/haproxy/firehol/abusers_1d.acl }
     # Ограничим частоту запросов
        stick-table type ip size 1m expire 10s store conn_cur
        tcp-request content track-sc0 src
        tcp-request content reject if { sc_conn_cur(0) gt 240 }
        tcp-request content capture req.ssl_sni len 10
        tcp-request content accept if { req_ssl_hello_type 1 } or !{ req_ssl_hello_type 1 }
     # Правила сопостовления 
        use_backend tcp-ssh if !{ req.ssl_hello_type 1 } { payload(0,7) -m bin 5353482d322e30 } or !{ req.ssl_hello_type 1 } { req.len 0 }

        use_backend tcp_to_https if { req.ssl_sni -i corp.example.ru }

        use_backend tcp_to_reality if { req.ssl_sni -i drive.example.ru }

        use_backend tcp_to_metrics if { req.ssl_sni -i metrics.example.ru }

        use_backend tcp_to_mtls if { req.ssl_sni -i auth.example.ru } || { req.ssl_sni -i ns1.example.ru }

#        use_backend tcp_to_ocserv if { req.ssl_sni -i example.ru }
#------------------------------------------
frontend f_https # 
        bind abns@frontendhttps.sock accept-proxy ssl crt /etc/haproxy/certs/ ssl crt /etc/haproxy/ssl/ alpn h2,http/1.1 strict-sni tfo
        http-request reject if { req.hdr(user-agent) -m sub evil }
        http-request deny if { path -m sub /. } # запрет доступа к скрытым файлам
        http-response set-header X-Content-Type-Options nosniff
        http-response set-header X-XSS-Protection 1;mode=block
        http-response set-header X-Frame-Options SAMEORIGIN
        http-after-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains;" # Устанавливаем HSTS
        stick-table type ip size 100k expire 5m store http_req_rate(10s)
        http-request track-sc0 src
        http-request deny deny_status 429 if { sc_http_req_rate(0) gt 300 }
        http-request return status 200 content-type text/plain lf-string "Status 200 OK! Your IP %[src]." if { path /https-status-443 }

        use_backend http_3xui_sub if { ssl_fc_sni -i corp.example.ru } { path_beg /sub-path/ } || { path_beg /jsub-path/ }
        use_backend xhttp if { req.hdr(host) -i corp.example.ru } { path_beg -i /secretpath/ } || { path_beg -i /secretpath }

        use_backend http_adh if { ssl_fc_sni -i corp.example.ru } { path_beg -i /secretpathdns/ } # { path_beg /dns-query/ }

#        use_backend websocket if { req.hdr(Host) -i corp.example.ru } { path /ws } || { path_beg /ws/ }

frontend f_mtls # Административный доступ
        bind abns@frontendmtlsadm.sock accept-proxy alpn h3,h2,http/1.1 tfo ssl-min-ver TLSv1.3 ssl crt /etc/haproxy/certs/ verify required ca-file /etc/haproxy/ca/ca.pem ca-verify-file /etc/haproxy/ca/fullca.pem crl-file /etc/haproxy/ca/revoked.crl crt-ignore-err 10,23
        http-request silent-drop if { path_end /dns-query } || { path_beg /dns-query/ }
        http-request reject if { req.hdr(user-agent) -m sub evil }
        http-request deny if { path -m sub /. } # запрет доступа к скрытым файлам
        http-after-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains;" # Устанавливаем HSTS
        http-request return status 403 content-type text/plain lf-string "Your certificate has expired, please contact the administrator" if { ssl_c_verify 10 }
        http-request return status 403 content-type text/plain lf-string "Your certificate has been revoked, please contact the administrator" if { ssl_c_verify 23 }
        http-request return status 200 content-type text/plain lf-string "Status 200 OK! Your IP %[src]." if { path /https-status-443 }

        use_backend http_3xui if { ssl_fc_sni -i corp.example.ru } { path_beg /defaultpath/ } || { path_beg /3xui-path }
        use_backend stats if { path_beg -i /haproxy/ }
        use_backend http_adh if { ssl_fc_sni -i ns1.example.ru }

#-- Statistics ----------------------------
frontend f_metrics_mtls
        bind abns@metricshttps.sock tfo accept-proxy ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 ssl-min-ver TLSv1.3 strict-sni verify required ca-file /etc/haproxy/ca/metrics.pem ca-verify-file /etc/haproxy/ca/metricsfull.pem crl-file /etc/haproxy/ca/metrics.crl crt-ignore-err 10,23
        tcp-request connection accept if { src -f /etc/haproxy/geoip/RU.list }
        http-request reject if { req.hdr(user-agent) -m sub evil }
        http-request deny if { path -m sub /. } # запрет доступа к скрытым файлам
        stick-table type ip size 100k expire 5m store http_req_rate(30s)
        http-request track-sc0 src
        http-request deny deny_status 429 if { sc_http_req_rate(0) gt 90 }
        http-after-response set-header Strict-Transport-Security "max-age=16000000;"
        http-request return status 403 content-type text/plain lf-string "Your certificate has expired, please contact the administrator" if { ssl_c_verify 10 }
        http-request return status 403 content-type text/plain lf-string "Your certificate has been revoked, please contact the administrator" if { ssl_c_verify 23 }
        http-request use-service prometheus-exporter if { path_beg /haproxy-exporter-0835n-rm05v-g1nry-r8h-h7dn-gu8v/ } # exporter haproxy, ссылка для prometheus ( /path_beg/metrics ) 
        use_backend node-exporter if { path_beg -i /node-exporter-4umx-2nf46x3-qghy-7b3o-zxca-jte/ } # exporter сервера
backend node-exporter
        mode http
        http-request replace-path /node-exporter-4umx-2nf46x3-qghy-7b3o-zxca-jte(/)?(.*) /\2
        server s1 127.0.0.1:9100
frontend f_stats
        bind abns@stats.sock
        stats enable
        stats uri /stats
        stats realm Haproxy\ Statistics
        stats refresh 10s
        stats show-legends
        stats hide-version
        stats show-modules
backend stats
        mode http
        http-request replace-path /haproxy(/)?(.*) /\2
        server s1 abns@stats.sock
#------------------------------------------
frontend f_https_reality
        bind /var/lib/haproxy/fakehttps1.sock accept-proxy ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 strict-sni mode 660 user haproxy group haproxy ssl-min-ver TLSv1.3
        http-request reject if { req.hdr(user-agent) -m sub evil }
        acl url_discovery path /.well-known/caldav /.well-known/carddav
        http-request redirect location /remote.php/dav/ code 301 if url_discovery
        http-request deny if { path -m sub /. }
        stick-table type ip size 100k expire 2m store http_req_rate(10s)
        http-request track-sc0 src
        http-request deny deny_status 429 if { sc_http_req_rate(0) gt 240 }
        http-response set-header X-Content-Type-Options nosniff
        http-response set-header X-XSS-Protection 1;mode=block
        http-response set-header X-Frame-Options SAMEORIGIN
        http-response set-header Strict-Transport-Security "max-age=16000000;" # Устанавливаем HSTS
        http-request return status 200 content-type text/plain lf-string "Status 200 OK! Your IP %[src]." if { path / }
#        default_backend nextcloud
#------- BACKEND --------------------------
#[ TCP Passthrough
backend tcp-ssh # Бекенд для SSH
        mode tcp
        option http-keep-alive
        timeout http-keep-alive 30s
        server ssh 127.0.0.1:22 check port 22

#backend tcp_to_ocserv # 
#        mode tcp
#        server oc 127.0.0.1:48658 check port 48658 send-proxy

backend tcp_to_https # 
        mode tcp
        server s1 abns@frontendhttps.sock send-proxy-v2-ssl-cn tfo check 
        retry-on conn-failure empty-response response-timeout

backend tcp_to_reality 
        mode tcp
        server reality1 /x-ui/reality1.sock send-proxy tfo check
        retry-on conn-failure empty-response response-timeout

backend tcp_to_mtls
        mode tcp
        server mtls abns@frontendmtlsadm.sock send-proxy-v2-ssl-cn tfo check
        retry-on conn-failure empty-response response-timeout

backend tcp_to_metrics
        mode tcp
        server metrics abns@metricshttps.sock send-proxy-v2 tfo check
        retry-on conn-failure empty-response response-timeout

backend http_3xui # Админ панель 3X-UI
        mode http
        option forwardfor if-none
        http-request add-header X-Real-Ip %[src]
        http-request set-header Host %[req.hdr(Host)]
        http-request set-header X-Forwarded-For %[src]
        http-request set-header X-Forwarded-Host %[req.hdr(host)]
        http-request set-header X-Forwarded-Proto https if { ssl_fc }
        server 3xui 127.0.0.1:49004 check port 49004

backend http_3xui_sub # подписка 3х
        mode http
        option forwardfor if-none
        http-request add-header X-Real-Ip %[src]
        http-request set-header Host %[req.hdr(Host)]
        http-request set-header X-Forwarded-For %[src]
        http-request set-header X-Forwarded-Host %[req.hdr(host)]
        http-request set-header X-Forwarded-Proto https if { ssl_fc }
        server 3xsub 127.0.0.1:49007 check port 49007

backend xhttp # XHTTP: Beyond REALITY
        mode http
        option forwardfor if-none
        http-request add-header X-Real-Ip %[src]
        http-request set-header Host %[req.hdr(Host)]
        http-request set-header X-Forwarded-For %[src]
        http-request set-header X-Forwarded-Host %[req.hdr(host)]
        retry-on conn-failure empty-response response-timeout
        server xhttp1 /x-ui/xhttp1.sock send-proxy tfo check

backend http_adh # DOH AdGuardHome
        mode http
        option forwardfor if-none
        http-request add-header X-Real-Ip %[src]
        http-request set-header Host %[req.hdr(Host)]
        http-request set-header X-Forwarded-For %[src]
        http-request set-header X-Forwarded-Host %[req.hdr(host)]
        http-request set-header X-Forwarded-Proto https if { ssl_fc }
        http-request replace-path /secretpathdns(/)?(.*) /\2
        server dns 127.0.0.1:49005 check port 49005

#backend http_site1 # Apache2
#        mode http
#        option httpchk
#        http-check expect status 200
#        option forwardfor if-none
#        http-request add-header X-Real-Ip %[src]
#        http-request set-header Host %[req.hdr(Host)]
#        http-request set-header X-Forwarded-For %[src]
#        http-request set-header X-Forwarded-Host %[req.hdr(host)]
#        http-request set-header X-Forwarded-Proto https if { ssl_fc }
#        server ap2 127.0.0.1:8080 check

backend nextcloud #
        mode http
        option forwardfor
        http-request set-header X-Forwarded-Proto https if { ssl_fc }
        http-request set-header X-Forwarded-Proto http if !{ ssl_fc }
        http-request set-header X-Forwarded-Host %[req.hdr(host)]
        http-request set-header X-Forwarded-For %[src]
        http-request add-header X-Real-Ip %[src]
        http-response set-header Strict-Transport-Security max-age=157680000;
        server nextcloud 127.0.0.1:49009 #check port 49009

# --- IP List -------------------------------
# Range 48658—48999 - TCP // 49001—49100 - http сервисы // 49101-49150 - WS
Теги:
Хабы:
+14
Комментарии5

Публикации