Маршрутизируем трафик с помощью HAProxy, совмещаем сервисы на 443 порту

—

от автора

Это продолжение статьи про совмещение Vless, WebSocket и ssh на порту 443 силами HAProxy. В данном продолжении полностью перепишем конфигурацию HAProxy, добавим GeoIP списки для блокировки доступа, развернем личный DOH силами AdGuardHome на одном домене с WebSocket и сайтами и добавим openconnect vpn.

Начнем с базовой информации, нам потребуется 3 доменных имени например основной домен mydomain.com или my.domain.com и два дополнительных для VLESS и OCServ, допустим 1.mydomain.com ( my1.domain.com ) и 2.mydomain.com ( my2.domain.com ) соответственно, домены могут быть и бесплатными или техдомены хостера, не важно нам нужна лишь А запись на IP сервера.

HAProxy будет маршрутизировать трафик в режиме TCP для 443 порта на основе SNI, дополнительные домены пойдут на свои backend сервисы, а весь остальной или только основой уйдет на http frontend, где будут размещены защищенные сервисы на основе пути (статистика HAProxy, XUI панель, WebSocket’s в лице wstunnel и DOH про него позже) а все остальное уйдет на default_backend на котором было бы неплохо разместить какой нибудь сайт.

Приступим к подготовке сервера, в данной статье я буду работать от root, так будет проще настроить методом ctrl+c ctrl+v, для начала обновим список пакетов, установим необходимые пакеты и выполним базовую настройку:
Обновляем систему apt update && apt upgrade -y
Устанавливаем пакеты apt install ocserv haproxy unzip netcat-traditional socat htop unbound unbound-anchor seccomp

Проведем базовую защиту сервера, отключим IPv6 и внесем правки в sysctl.conf, применим после внесения командой sysctl -p
nano /etc/sysctl.conf

# Защита от IP-спуфинга net.ipv4.conf.all.rp_filter = 1 net.ipv4.conf.default.rp_filter = 1  # Игнорировать широковещательные запросы ICMP net.ipv4.icmp_echo_ignore_broadcasts = 1  # Защита от плохих сообщений об ошибках icmp net.ipv4.icmp_ignore_bogus_error_responses = 1  # Отключить маршрутизацию исходных пакетов net.ipv4.conf.all.accept_source_route = 0 net.ipv6.conf.all.accept_source_route = 0 net.ipv4.conf.default.accept_source_route = 0 net.ipv6.conf.default.accept_source_route = 0  # Включить exec shield ядра kernel.randomize_va_space = 1  # Блокировать SYN атаки net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_syn_backlog = 2048 net.ipv4.tcp_synack_retries = 2 net.ipv4.tcp_syn_retries = 5  # Игнорировать марсианские пакеты net.ipv4.conf.all.log_martians = 1 net.ipv4.icmp_ignore_bogus_error_responses = 1  # Игнорировать перенаправления отправки net.ipv4.conf.all.send_redirects = 0 net.ipv4.conf.default.send_redirects = 0  # Игнорировать перенаправления ICMP net.ipv4.conf.all.accept_redirects = 0 net.ipv6.conf.all.accept_redirects = 0 net.ipv4.conf.default.accept_redirects = 0 net.ipv6.conf.default.accept_redirects = 0 net.ipv4.conf.all.secure_redirects = 0 net.ipv4.conf.default.secure_redirects = 0  # Disabled IPv6 net.ipv6.conf.all.disable_ipv6 = 1 net.ipv6.conf.default.disable_ipv6 = 1 net.ipv6.conf.lo.disable_ipv6 = 1

Разрешим подключения на ssh и http/s
ufw allow 22/tcp
ufw allow 80,443/tcp
Активируем фаервол
ufw enable

Теперь настроим unbound в качестве локального резолвера (шаг не обязательный и можно пропустить)
Остановим systemd resolved
systemctl stop systemd-resolved.service
Отключим DNSStubListener
sh -c 'echo DNSStubListener=no' >> /etc/systemd/resolved.conf
Удалим resolv.conf
rm -f /etc/resolv.conf
Пропишем localhost в качестве ответчика DNS
echo 'nameserver 127.0.0.1' >> /etc/resolv.conf
Запустим systemd resoved
systemctl start systemd-resolved.service
Подготовим unbound:
Скачаем список корневых DNS серверов
curl -o /etc/unbound/root.hints https://www.internic.net/domain/named.cache
Теперь создадим скрипт для автоматического обновления корневых DNS

echo '#!/bin/sh curl -o /etc/unbound/root.hints https://www.internic.net/domain/named.cache' > /etc/cron.daily/root_hints

Сделаем его исполняемым
chmod 700 /etc/cron.daily/root_hints
Сгенерируем ключ DNSSEC для валидации
unbound-anchor -a /etc/unbound/root.key
Поменяем владельца для root.hints root.key
chown unbound:unbound /etc/unbound/root.hints
chown unbound:unbound /etc/unbound/root.key
Теперь создадим файл конфигурации
nano /etc/unbound/unbound.conf.d/dns.conf и внесем следующий код

Скрытый текст
server: port: 53 verbosity: 0 num-threads: 1 outgoing-range: 512 num-queries-per-thread: 1024 msg-cache-size: 512m  rrset-cache-size: 64m cache-max-ttl: 21600 cache-min-ttl: 2600 infra-host-ttl: 60 infra-lame-ttl: 120  interface: 127.0.0.1  outgoing-interface: $IP  access-control: 127.0.0.1/8 allow_snoop  root-hints: "/etc/unbound/root.hints"  username: unbound  directory: "/etc/unbound" logfile: "/var/log/unbound.log" log-time-ascii: yes use-syslog: no  do-not-query-localhost: no qname-minimisation: yes minimal-responses: yes prefetch: yes use-caps-for-id: yes rrset-roundrobin: yes  hide-identity: yes hide-version: yes  so-rcvbuf: 4m so-sndbuf: 4m  do-ip4: yes do-ip6: no do-udp: yes do-tcp: yes deny-any: yes

В конфигурации do-not-query-localhost: no позволит нам работать на localhost и отвечать на запросы, num-threads: 1 отвечает за кол-во потоков (по ядрам), interface: 127.0.0.1 включит прослушивание только на localhost с портом 53, outgoing-interface: $IP где $IP это IPv4 вашего сервера полученный командой ip a или панели хостера, а access-control: 127.0.0.1/8 allow_snoop разрешит трафик с localhost и позволит делать диагностику запросов.

Теперь настройка HAProxy

Сначала установим и создадим аккаунт acme для получения сертификатов
Добавим пользователя root в группу haproxy
adduser root haproxy
Создадим директорию для acme
mkdir /usr/local/share/acme.sh/
Скачаем acme.sh
git clone https://github.com/acmesh-official/acme.sh.git
Перейдем в каталог с acme
cd acme.sh/
Выполним установку в созданную директорию
./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/
Дадим права на запуск
chmod 755 /usr/local/share/acme.sh/
Создадим директорию для хранения сертификатов HAProxy
mkdir /etc/haproxy/certs
Поменяем создателя
chown haproxy:haproxy /etc/haproxy/certs
Установим права
chmod 770 /etc/haproxy/certs
Выполним инициализацию acme зарегистрируем аккаунт и поменяем default сервер выдачи youremail@example.com замените на свою почту
acme.sh --set-default-ca --server letsencrypt --register-account -m youremail@example.com
На выходе у вас должна быть строка вида «ACCOUNT_THUMBPRINT ‘diosrg8siojgiodsjhosdijhoisdfjsd’ » сохраняем ее она потребуется дальше.

Создадим базовую конфигурацию HAProxy для первого запуска и получения сертификатов.
Создадим ключи DH, процедура генерации ключа может занять продолжительное время!
openssl dhparam -out /etc/haproxy/dh4096.pem 4096
Очистим дефолтую конфигурацию
echo '' > /etc/haproxy/haproxy.cfg
Откроем конфиг
nano /etc/haproxy/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 'diosrg8siojgiodsjhosdijhoisdfjsd' # поменять на полученный из acme         stats timeout 30s         user    haproxy         group   haproxy         daemon         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-options prefer-client-ciphers no-tls-tickets ssl-min-ver TLSv1.2         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-options no-tls-tickets ssl-min-ver TLSv1.2         ssl-dh-param-file /etc/haproxy/dh4096.pem       # Тюнинг http/2 для WebSocket         tune.h2.initial-window-size 536870912 # Увеличиваем начальный размер окна для входящих и исходящих соединений.         tune.h2.fe.max-concurrent-streams 512 # Установим кол-во одновременных потоков на входящие соединений.         tune.h2.fe.glitches-threshold 1024 # Установим порог автоматического завершения соединения при превышении заданного кол-ва ошибок.         tune.h2.be.max-concurrent-streams 512 # Установим кол-во одновременных потоков на исходящие соединений.         tune.h2.be.glitches-threshold 1024 # Установим порог автоматического завершения соединения при превышении заданного кол-ва ошибок в 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         nameserver ns2 127.0.0.53: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 stats         mode http         bind 127.0.0.1:58080         stats enable         stats uri /stats         stats realm Haproxy\ Statistics         stats refresh 10s         stats auth sdvhsis3w:opsdiv90-ikps         stats show-legends         stats hide-version #------------------------------------------ frontend http         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 }          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 redirect scheme https unless { ssl_fc }  frontend tcp         bind *:443 # ssl passthrough         mode tcp         option tcplog         tcp-request inspect-delay 6s        # Защитим от перегрузки, если с одного IP за 10сек открыто более 30 соедиенний, отклоним все запросы и закроем все соединения       # Нужно учитывать что WsTunnel с активным параметром --connection-min-idle <n> будет занимать указанное колличество при старте       # У VLESS так же есть мультилексор, тоже нужно учитывать при настройке       # Может так случиться если несколько клиентов в одно и тоже время с одного IP решат подключиться, все они потеряют доступ к серверу.        # Для себя я вывел что 30 одновременных подключений с одного IP на случай если например дома выключат свет         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 30 }          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 } # Я бы не рекомендовал, вы лешитесь доступа если haproxy упадет          default_backend tcp_to_https  frontend https         bind 127.0.0.1:444 accept-proxy ssl crt /etc/haproxy/certs alpn h2,http/1.1 strict-sni         http-request reject if { req.hdr(user-agent) -m sub evil }         http-request reject if { path -m sub /. }          http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains; preload;" # Устанавливаем HSTS          use_backend http_stats if { ssl_fc_sni -i mydomain.com } { path_beg /ha-stats-secret-path/ }  #->-----tcp-backend-------------------------- backend tcp_to_https         mode tcp         server https 127.0.0.1:444 send-proxy-v2-ssl-cn check port 444 backend tcp-ssh         mode tcp         option http-keep-alive         timeout http-keep-alive 30s         server ssh 127.0.0.1:22 check port 22 #->->---http-backend------------------------- backend http_stats         mode http         http-request replace-path /ha-stats-secret-path(/)?(.*) /\2         server stats 127.0.0.1:58080

Теперь можно проверить конфигурацию на ошибки
haproxy -f /etc/haproxy/haproxy.cfg -c
Если ошибок нет, запускаем
systemctl start haproxy

Теперь приступим к получению первого сертификата для основного домена, для это выполним следующее:
acme.sh --issue -d mydomain.com --stateless
Если все настроено было правильно, HAProxy ответ на запрос LE и вы увидите полученный сертификат, и путь к нему, но это еще не все, теперь установим сертификат на «горячую»

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 mydomain.com --deploy-hook haproxy

Теперь можем перейти по адресу mydomain.com, если сайт открылся как https все сделано правильно, в любом случае можно для верности перезапустить сервис если сайт не открылся как https, при данном переходе на сайт у вас будет 503 ошибка, это нормально.
systemctl restart haproxy

Теперь подготовим GeoIP списки для фильтрации стран и континентов.
На просторах интернета нашел на github списки GeoIP, скажем автору Loyalsoldier спасибо за поддержание списков в актуальном состоянии.

Написал скрипт для выгрузки с репозитория актуального списка преобразования его в continent_code и country_iso_code.
Создадим директории для скрипта и хранения GeoIP списков
mkdir /etc/haproxy/geoip .
mkdir /etc/haproxy/acl .
mkdir /etc/haproxy/script .
Создадим скрипт в каталоге /etc/haproxy/script
nano /etc/haproxy/script/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"          # Переименуем csv:         find $GEOIP_TEMP -depth -type f -name '*IPv4.csv' -exec mv {} $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 systemctl restart haproxy

Теперь имея списки в директории /etc/haproxy/geoip можем настраивать региональные блокировки, прошу учесть что списки GeoIP не точные, вы можете случайно заблокировать себя!

Используя оператор ‘или’ добавим в http frontend следующие содержимое после
http-request reject if { req.hdr(user-agent) -m sub evil }

     # Заблочим Африку, Азию, Южную Америку, Океанию, Европу и Северную Америку не блочим для запросов ACME         http-request reject if { src -f /etc/haproxy/geoip/AF.acl } || { src -f /etc/haproxy/geoip/AS.acl } || { src -f /etc/haproxy/geoip/AN.acl } || { src -f /etc/haproxy/geoip/OC.acl } || { src -f /etc/haproxy/geoip/SA.acl }

Теперь добавим в секцию TCP frontend после tcp-request inspect-delay 6s

      # Заблочим Африку, Азию, Южную/Северную Америку, Океанию и Антарктику         tcp-request connection reject if { src -f /etc/haproxy/geoip/AF.acl } || { src -f /etc/haproxy/geoip/AS.acl } || { src -f /etc/haproxy/geoip/AN.acl } || { src -f /etc/haproxy/geoip/OC.acl } || { src -f /etc/haproxy/geoip/SA.acl } || { src -f /etc/haproxy/geoip/NA.acl }

Настало время добавить в https frontend после http-request reject if { path -m sub /. }

http-request deny deny_status 403 if !{ src -f /etc/haproxy/geoip/RU.list }

Учтите что вашего IP адреса может не оказаться и вы потеряете доступ к https, по этому в разделах выше нет данных правил, вы можете составить свои правила для своих подсетей назвать их mylist.acl разместить в /etc/haproxy/acl и добавить их в списки на разрешение

http-request deny deny_status 403 if !{ src -f /etc/haproxy/geoip/RU.list } || !{ src -f /etc/haproxy/mylist.acl }

Оператор || он же или, т.е. если ваш IP будет присутствовать хотя-бы в одном из правил, вы получите доступ. Использовать с осторожностью при работе с белым списком.

Параметр connection в TCP frontend работает с соединением на самой ранней точке, в случае правила запрета будет сбрасывает соединение без уведомления клиента и записи в журнал, можно изменить на content который в свою очередь запишет в журнал и сбросит соединение без ответа клиенту, в статистике HAProxy можно будет отслеживать сброшенные соединения. Правила connection  должны быть над правилами content.
Применяется для разрыва соединения с нежелательных IP-адресов.

Параметр silent-drop в http frontend отвечает за тихое отключение клиента без уведомления, можно заменить на deny который отправит ответ клиенту о закрытии соединения. Как говорит документация «тихое отбрасывание повлияет на любые брандмауэры с отслеживанием состояния или прокси-серверы между балансировщиком нагрузки и клиентом, поскольку они часто будут удерживать соединение, не зная, что оно было отключено.» эффективнее использовать deny, я же комбинирую правила, для IP-адресов deny, а для user-agent и скрытых файлой «/.» silent-drop.

Если установить http-request reject if { req.hdr(user-agent) -m len le 32 } в секции frontend https — перестанет работать DOH.

Конфигурация после добавления GeoIP

Скрытый текст
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 'diosrg8siojgiodsjhosdijhoisdfjsd' # поменять на полученный из acme         stats timeout 30s         user    haproxy         group   haproxy         daemon         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-options prefer-client-ciphers no-tls-tickets ssl-min-ver TLSv1.2         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-options no-tls-tickets ssl-min-ver TLSv1.2         ssl-dh-param-file /etc/haproxy/dh4096.pem       # Тюнинг http/2 для WebSocket         tune.h2.initial-window-size 536870912 # Увеличиваем начальный размер окна для входящих и исходящих соединений.         tune.h2.fe.max-concurrent-streams 512 # Установим кол-во одновременных потоков на входящие соединений.         tune.h2.fe.glitches-threshold 1024 # Установим порог автоматического завершения соединения при превышении заданного кол-ва ошибок.         tune.h2.be.max-concurrent-streams 512 # Установим кол-во одновременных потоков на исходящие соединений.         tune.h2.be.glitches-threshold 1024 # Установим порог автоматического завершения соединения при превышении заданного кол-ва ошибок в 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         nameserver ns2 127.0.0.53: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 stats         mode http         bind 127.0.0.1:58080         stats enable         stats uri /stats         stats realm Haproxy\ Statistics         stats refresh 10s         stats auth sdvhsis3w:opsdiv90-ikps         stats show-legends         stats hide-version #------------------------------------------ frontend http         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 }         # Защитим от перегрузки         # Если в течении 1m будет выполнено более 10 запросов отклоним ответом 429 Too Many Requests         stick-table type ip size 100k expire 2m store http_req_rate(30s)         http-request track-sc0 src         # Отклоним с ответом         http-request deny deny_status content-type text/html lf-string "<p>Per our policy, you are limited to 10 requests per minute, but you have exceeded that limit with %[sc_http_req_rate(0)] requests per minute.</p>" if { sc_http_req_rate(0) gt 30 }        # Заблочим Африку, Азию, Южную Америку, Океанию, Европу и Северную Америку не блочим для запросов ACME         http-request deny deny_status 403 if { src -f /etc/haproxy/geoip/AF.acl } || { src -f /etc/haproxy/geoip/AS.acl } || { src -f /etc/haproxy/geoip/AN.acl } || { src -f /etc/haproxy/geoip/OC.acl } || { src -f /etc/haproxy/geoip/SA.acl }           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 redirect scheme https unless { ssl_fc }  frontend tcp         bind *:443 # ssl passthrough         mode tcp         option tcplog         tcp-request inspect-delay 6s        # Заблочим Африку, Азию, Южную/Северную Америку, Океанию и Антарктику         tcp-request connection reject if { src -f /etc/haproxy/geoip/AF.acl } || { src -f /etc/haproxy/geoip/AS.acl } || { src -f /etc/haproxy/geoip/AN.acl } || { src -f /etc/haproxy/geoip/OC.acl } || { src -f /etc/haproxy/geoip/SA.acl } || { src -f /etc/haproxy/geoip/NA.acl }       # Защитим от перегрузки, если с одного IP за 10сек открыто более 30 соедиенний, отклоним все запросы и закроем все соединения      # Нужно учитывать что WsTunnel с активным параметром --connection-min-idle <n> будет занимать указанное колличество при старте      # У VLESS так же есть мультилексор, тоже нужно учитывать при настройке      # Может так случиться если несколько клиентов в одно и тоже время с одного IP решат подключиться, все они потеряют доступ к серверу.       # Для себя я вывел что 30 одновременных подключений с одного IP на случай если например дома выключат свет         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 30 }          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 } # Я бы не рекомендовал, вы лешитесь доступа если haproxy упадет          default_backend tcp_to_https  frontend https         bind 127.0.0.1:444 accept-proxy ssl crt /etc/haproxy/certs alpn h2,http/1.1 strict-sni         http-request reject if { req.hdr(user-agent) -m sub evil }         http-request reject if { path -m sub /. }       # Защитим от перегрузки, если перестанет работать WsTunnel (чем больше клиентов тем выше установить счетчик)      # Если в течении 30с будет выполнено более 30 запросов отклоним ответом 429 Too Many Requests         stick-table type ip size 100k expire 2m store http_req_rate(30s)         http-request track-sc0 src      # Отклоним с ответом о превышенном кол-ве запросов         http-request deny deny_status content-type text/html lf-string "<p>Per our policy, you are limited to 30 requests per 30s, but you have exceeded that limit with %[sc_http_req_rate(0)] requests per 30s.</p>" if { sc_http_req_rate(0) gt 30 }      # GeoIP блокировка всех кроме RU и белого списка (можете воспользоваться content-type из примера выше, сообщить о запрете доступа с их IP вывести IP адрес.)      # Добавьте свой IP перед использованием в белый список         http-request deny deny_status 403 if !{ src -f /etc/haproxy/geoip/RU.list } || !{ src -f /etc/haproxy/mylist.acl }          http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains; preload;" # Устанавливаем HSTS          use_backend http_stats if { ssl_fc_sni -i mydomain.com } { path_beg /ha-stats-secret-path/ }  #->-----tcp-backend-------------------------- backend tcp_to_https         mode tcp         server https 127.0.0.1:444 send-proxy-v2-ssl-cn check port 444 backend tcp-ssh         mode tcp         option http-keep-alive         timeout http-keep-alive 30s         server ssh 127.0.0.1:22 check port 22 #->->---http-backend------------------------- backend http_stats         mode http         http-request replace-path /ha-stats-secret-path(/)?(.*) /\2         server stats 127.0.0.1:58080 

Переходим к настройке сервисов

Начнем добавлять сервисы с OpenConnect
Для начала нужно получить сертификаты, для этого сделаем пару скриптов, начнем с post-hook-oc.sh
Разместим скрипты рядом с acme.sh в директории .acme.sh
находясь в домашней директории root выполняем
nano .acme.sh/post-hook-oc.sh

#!/bin/sh cp /root/.acme.sh/1.mydomain.com_ecc/fullchan.cer /etc/ocserv/fullchan.cer cp /root/.acme.sh/1.mydomain.com_ecc/1.mydomain.com.key /etc/ocserv/key.pem systemctl restart ocserv

И второй hook-renew с идентичным содержимым первому
nano .acme.sh/hook-renew-oc.sh

Теперь сделаем их исполняемыми
chmod 700 .acme.sh/post-hook-oc.sh && chmod 700 .acme.sh/hook-renew-oc.sh

Подготовим запрос
acme.sh --issue -d 1.mydomain.com --stateless --post-hook /root/.acme.sh/post-hook-oc.sh --renew-hook /root/.acme.sh/hook-renew-oc.sh
Данный запрос после успешного выполнения создаст сертификаты для ocserv в /etc/ocserv, при успешном обновлении он обновит сертификат /etc/ocserv и перезапустит сервис.

Подготовим конфигурацию OCServ

Скрытый текст
# адрес подкл: https://1.mydomain.com/?my-secret-path # cd /etc/ocserv > ocpasswd username # имя tun device = ocserv socket-file = ocserv.sock chroot-dir = /var/lib/ocserv #pid-file = /var/run/ocserv.pid  #mtu = 1420 #буфер mtu #output-buffer = 10  # глобальное ограничение скорости, байт/сек #rx-data-per-sec = 40000 #tx-data-per-sec = 40000  # уровень логгирования, 1 basic, 2 info, 3 debug, 4 http, 8 sensitive, 9 TLS, default 0 он же 2 log-level = 2  # Включаем прокси протокол listen-proxy-proto = true  # изоляция процессов, по умолчанию выкл, для включения прописать 'true' и установить apt install seccomp, снижает производительность ~2% (ocserv должен быть собран с данной опцией) isolate-workers = false  # ограничение кол-ва входящих соединений, для предотвращения перегруза rate-limit-ms = 100  # keepalive должен быть меньше чем timeout server и timeout client на haproxy, у меня 1 минута, уст в 40 сек keepalive = 40  # Авторизация через PAM с gid выше 3000 #auth = "pam[gid-min=3000]"  # Авторизация через файл auth = "plain[passwd=/etc/ocserv/ocpasswd]"  # таймаут авторизации auth-timeout = 240  # время  запрета авторизации после не удачной попытки min-reauth-time = 300  # Блок клиентов после не удачной авторизации, по умолчанию 10баллов = 1 попытка max-ban-score = 80 # время сбороса баллов ban-reset-time = 1200  # Уст время жизни куки, для повторой авторизации без пароля, в случае отвала, например для роуминга между сетями cookie-timeout = 600 # разрешим роуминг deny-roaming = false  # Постоянные куки, ввод пароля 1 раз #persistent-cookies = true  # время переподключения, запроса ключа rekey-time = 172800  # метод переключения, ssl бесшовное, new-tunnel использовать только есть проблемы с ssl rekey-method = ssl  # Слушаем на localhost listen-host = 127.0.0.1  # То же самое, но для UDP. Но открывать мы его не будем на firewall #udp-listen-host = $IPADDR  # Номер порта для входящих подключений tcp-port = 29999  # То же самое для UDP #udp-port = 443  run-as-user = ocserv run-as-group = ocserv  # LE сертификат вашего сервера. #server-cert = /etc/letsencrypt/live/my.domain.ru/fullchain.pem #server-key = /etc/letsencrypt/live/my.domain.ru/privkey.pem  # acme сертификат вашего сервера. server-cert = /etc/ocserv/fullchain.cer server-key = /etc/ocserv/1.mydomain.com.key  # banner = "Server" #pre-login-banner = "Welcome"  # Можно ограничить количество одновременно подключенных клиентов max-clients = 32  # И одновременно подключенных одинаковых клиентов (с одинаковым логином), 0 - безлимитно max-same-clients = 2  # ваш домен например: 1.mydomain.com default-domain = 1.mydomain.com  # диапазон IP-адресов, которые вы будете выдавать подключенным клиентам ipv4-network = 10.12.14.0 ipv4-netmask = 255.255.255.0 # альтернативно #ipv4-network = 10.12.14.0/24  # предотвращает утечку DNS, должно быть true # включать при если будет использоваться для выхода в интернет tunnel-all-dns = false  # DNS-сервер, которые будут использовать ваши клиенты. # Их может быть несколько dns = 8.8.8.8 dns = 1.1.1.1  # маршруты, которые будут переданы клиентам: какие диапазоны IP нужно будет отправлять через VPN route = 10.12.14.0/255.255.255.0 # route = 192.168.0.0/255.255.0.0 # route = fef4:db8:1000:1001::/64  # default = все # route = default  # маршруты которые не должны маршрутизироваться # no-route = 192.168.5.0/255.255.255.0  # сохранение ip пользователя когда это возможно predictable-ips = true  # файлы для переопределения параметров конфигурации для отдельных юзеров или групп #config-per-user = /etc/ocserv/config-per-user/ #config-per-group = /etc/ocserv/config-per-group/  # должно быть true если мы подключаемся клиентами от Cisco cisco-client-compat = true  # Включаем маскировку camouflage = true  camouflage_secret = "my-secret-path" camouflage_realm = "Admin Panel"  # для выгрузки в Prometheus #use-occtl = true  # Скрипт при подключении и отключении пользователя, можно вывести в ТГ или log файл #connect-script = /usr/bin/myscript #disconnect-script = /usr/bin/myscript  # host-update от клиента #host-update-script = /usr/bin/myhostnamescript  # Для DTLS #Dead peer detection in seconds #dpd = 90  # Dead peer detection for mobile clients #mobile-dpd = 1800  # DTLS если не пришел UDP в течении (сек) перейти на tls #switch-to-tcp-timeout = 25  # обнаружение mtu #try-mtu-discovery = false  # HTTP headers included-http-headers = Strict-Transport-Security: max-age=31536000 ; includeSubDomains included-http-headers = X-Frame-Options: deny included-http-headers = X-Content-Type-Options: nosniff included-http-headers = Content-Security-Policy: default-src 'none' included-http-headers = X-Permitted-Cross-Domain-Policies: none included-http-headers = Referrer-Policy: no-referrer included-http-headers = Clear-Site-Data: "cache","cookies","storage" included-http-headers = Cross-Origin-Embedder-Policy: require-corp included-http-headers = Cross-Origin-Opener-Policy: same-origin included-http-headers = Cross-Origin-Resource-Policy: same-origin included-http-headers = X-XSS-Protection: 0 included-http-headers = Pragma: no-cache included-http-headers = Cache-control: no-store, no-cache

Теперь можно запустить сервис
systemctl start ocserv
И проверим его
systemctl status ocserv
Если ошибок нет создадим первого пользователя
Перейдем в /etc/ocserv cd /etc/ocserv
Выполним ocpasswd nameuser дважды введем пароль и перезапустим сервис
systemctl restart ocserv
Сервис будет слушать localhost и порт 29999 для входящих tcp соединений с поддержкой прокси протокола, udp и DTLS мы не используем.

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

Настало время добавить ocserv в HAProxy для этого в секции TCP frontend добавить следующую строку после tcp-request content accept if { req_ssl_hello_type 1 } or !{ req_ssl_hello_type 1 }
use_backend ocserv { req.ssl_sni -i 1.mydomain.com }
И добавим backend

backend ocserv         mode tcp         server s1 127.0.0.1:29999 send-proxy check port 29999

И перезапустим HAProxy командой systemctl restart haproxy
Проверим что HAProxy запущен systemctl status haproxy

Теперь можно проверить подключение, адрес будет выглядеть так (я заменил : на [:] что бы не считалось ссылкой) https[:]//1.mydomain.com/?my-secret-path
Переходим в браузер вбиваем наш домен если все работает как должно, появится basic авторизация http, можем подключать клиентов.

На очереди X-UI
Развернем docker
Самый простой способ использовать официальный скрипт
curl -fsSL https://get.docker.com -o get-docker.sh && sh get-docker.sh
Проверим запуск docker командой docker ps

Теперь развернем X-UI
Создадим директорию с именем x-ui mkdir /root/x-ui
Создадим docker compose файл со следующим содержимым
nano /root/x-ui/docker-compose.yml

--- services:   xui:     image: alireza7/x-ui     container_name: x-ui     hostname: $yourhostname     volumes:       - $PWD/db/:/etc/x-ui/       - $PWD/cert/:/root/cert/     environment:       XRAY_VMESS_AEAD_FORCED: "false"     tty: true     network_mode: host     restart: unless-stopped

Теперь запустим наш контейнер
docker compose up -d
По умолчанию панель запускается на порту 54321 запомним это

Получим сертификаты для панели
Для начала создадим post и renew скрипты все шаги повторить из ocserv, отличается только содержимое
nano .acme.sh/post-hook-xui.sh

#!/bin/sh cp /root/.acme.sh/2.mydomain.com_ecc/fullchain.cer /root/x-ui/cert/cert.pem cp /root/.acme.sh/2.mydomain.com_ecc/2.mydomain.com.key /root/x-ui/cert/key.pem cd /root/x-ui && docker compose down && docker compose up -d

Подготовим запрос
acme.sh --issue -d 2.mydomain.com --stateless --post-hook /root/.acme.sh/post-hook-xui.sh --renew-hook /root/.acme.sh/hook-renew-xui.sh

Подготовим HAProxy добавим админ панель в https frontend после use_backend http_stats
use_backend http_xui if { ssl_fc_sni -i 2.mydomain.com } { path_beg /xui/ } || { path /xui }

Напишем backend

backend http_xui         mode http         option httpchk GET /xui/         option forwardfor if-none         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-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)]         server s1 127.0.0.1:54321 check

Сохраняем и перезапускаем haproxy.
Переходим mydomain.com/xui в браузере открылась панель, все ок значит, панель ругается на дефолтый порт, поменяем его на 50000 и самое главное добавим панели путь на /my-secret-path2/ и обновим конфигурацию HAProxy.

Изменим use_backend http_xui if { ssl_fc_sni -i 2.mydomain.com } на use_backend http_xui if { ssl_fc_sni -i 2.mydomain.com } { path_beg /my-secret-path2/ }

И отредактируем backend

backend http_xui         mode http         option httpchk GET /my-secret-path2/xui/         option forwardfor if-none         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-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)]         server s1 127.0.0.1:50000 check

После перезапустим haproxy и проверим доступность X-UI перейдя по адресу mydomain.com/my-secret-path2/xui если открылась админ панель значит все ок.
Настроим VLESS, дефолтый логин и пароль от панели admin

Переходим в подключения создаем подключение, протокол VLESS устанавливаем ему порт 50001, протокол передачи TCP (RAW), включаете PROXY Protocol, активируете External Proxy, безопасность TLS, указываете sni сертификата 2.mydomain.com, шифры auto, указывайте uTLS я использую firefox, пропишем путь для сертификата /root/cert/cert.pem и для ключа /root/cert/key.pem , активируем Sniffing, оставляем по дефолту, теперь добавим в HAProxy

В разделе TCP frontend добавляем под ocserv
use_backend vless if { req.ssl_sni -i 2.mydomain.com}
Добавим backend vless

backend vless         mode tcp         timeout tunnel 1h         server s1 127.0.0.1:50001 send-proxy check port 50001

Перезапустим HAProxy, и попробуем подключиться для этого с панели по QR коду и скопировав данные добавляете в приложение (я пользуюсь nekobox) и активируете его, сайты открываются, трафик идет, а в панели клиент горит как подключенный, значит все ок.

Добавим поддержку DNS over HTTPS
Создаем директорию
mkdir /root/adh/
Напишем docker compose

--- services:   adguardhome:     image: adguard/adguardhome     container_name: adguardhome     volumes:       - ./cert:/opt/adguardhome/cert       - ./conf:/opt/adguardhome/conf       - ./work:/opt/adguardhome/work       - ./log:/var/log/AdGuardHome     restart: unless-stopped     network_mode: host

Добавим конфигурацию
mkdir /root/adh/conf
nano /root/adh/conf/AdGuardHome.yaml

Скрытый текст
http:   pprof:     port: 6060     enabled: false   address: 0.0.0.0:30000   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.1   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

Прямого доступа к панели нет, логин и пароль demo
Добавим dns-query в HAProxy
Под use_backend http_xui добавим
use_backend dns_query if { ssl_fc_sni -i mydomain.com } { path_beg /dns-query/ } || { path /dns-query }
Теперь добавим backend

backend dns_query         mode http         option httpchk         option forwardfor if-none         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-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)]         server doh 127.0.0.1:30000

И перезапустим haproxy
Добавляем в любой клиент с поддержкой DoH адрес htpps[:]//mydomain.com/dns-query и проверяем проходят ли запросы, если да приступим к защите сервиса

Под use_backend добавим default_backend http_adh
И добавим backend под backend dns_query

backend http_adh         mode http         server adh 127.0.0.1:30000

Перезапускаем, переходим по нашему домену mydomain.com и у нас должна открыться панель, логин и пароль demo, настроим клиентов, создадим клиента например mydevice на вкладке клиенты, теперь добавим его во вкладке DNS внизу в раздел разрешенные клиенты.

Проверим работоспособность клиента https[:]//mydomain.com/dns-query/mydevice и проверяем проходят ли запросы, если да закончим настройку клиентов, позже можно добавить еще. Уберем default_backend http_adh и полностью закомментируем backend http_adh , перезапустим haproxy.

Настроим Wstunnel я подыму один сервис socks5, вы можете масштабировать под нужное кол-во.
Создадим директорию в etc:mkdir /etc/wstunnelПерейдем в нее:cd /etc/wstunnelЗагрузим релиз wstunnel:wget https://github.com/erebe/wstunnel/releases/download/v10.1.8/wstunnel_10.1.8_linux_amd64.tar.gz

Создадим пользователя под которым будем запускать на wstunnel:
adduser --system wstunnel
Теперь добавим группу:
addgroup wstunnel
Теперь добавим нашего пользователя в группу:
usermod -aG wstunnel wstunnel

Распакуем tar -xvf wstunnel_10.1.8_linux_amd64.tar.gz
У нас в директории появились файл нас интересует только файл wstunnel, поменяем владельца файла на созданного пользователя
chown wstunnel:wstunnel ./wstunnel
Сделаем его исполняемым
chmod 700 ./wstunnel

Теперь напишем systemd-юнит для запуска наших скриптовСоздадим файл командойsudo nano /etc/systemd/system/wstunnel-socks.service со следующим содержимым:

[Unit] Description=Start & Check Wstunnel socks5 After=network.target After=haproxy.service  [Service] WorkingDirectory=/etc/wstunnel PIDFile=/etc/wstunnel/wstunnel-socks5.pid ExecStart=/etc/wstunnel/wstunnel server -r ws-socks-secret-path ws://127.0.0.1:40001 --websocket-ping-frequency-sec 20 Restart=always RestartSec=10 User=wstunnel Group=wstunnel  [Install] WantedBy=multi-user.target

Дополнительно но не обязательно, напишем скрипт проверки туннеля, порты добавляются через пробел, сервисы через пробел с кавычками.
nano /etc/wstunnel/check-all-wstunnel.sh
и сделаем его исполняемым
chmod 700 /etc/wstunnel/check-all-wstunnel.sh

#!/bin/bash  # Установим часовой пояс для правильного отображения времени в логе export TZ=Europe/Moscow # Цель для пинга TARGET="127.0.0.1" # Массив портов PORTS=(40001) # Массив сервисов SERVICES=("wstunnel-socks5-run.service") # Для добавления добавить порт и сервис в последовательности   # Проверка работоспособности check_tunnel() {     local port=$1     local service=$2     # Проверка открытого порта через netcat     if ! nc -z $TARGET $port; then         echo "$(date '+%Y-%m-%d %H:%M:%S') Сервис на порту $port не работает... Перезапуск $service" >> /var/log/wstunnel-chk.log         systemctl restart $service     else         echo "$(date '+%Y-%m-%d %H:%M:%S') Сервис на порту $port работает."     fi }  # Запуск проверки массива портов for i in "${!PORTS[@]}"; do     check_tunnel ${PORTS[$i]} ${SERVICES[$i]} done

Добавим его в cron на проверку каждые 5 мин
crontab -e
*/5 * * * * /etc/wstunnel/check-*.sh

Добавим в HAProxy
use_backend ws-socks if { ssl_fc_sni -i mydomain.com } { path /ws-socks-secret-path } || { path_beg /ws-socks-secret-path/ }

И раздел backend

backend ws-socks         mode http         option http-keep-alive         timeout http-keep-alive 25s         timeout tunnel 1h         server s1 127.0.0.1:40001 check

Перезапускаем HAProxy.

На клиента скачиваем Wstunnel, распаковываем для примера будет windows
В папке с файлом wstunnel создаете файл start.cmd со следующим содержимым
wstunnel client -P ws-socks-secret-path -L socks5://127.0.0.1:1080 wss://mydomain.com:443 -с 7 --tls-verify-certificate --websocket-ping-frequency-sec 20

Настраиваете браузер на использование socks5 сервера 127.0.0.1 и порта 1080 включите перенаправление dns.

Остается только добавить какой нибудь безобидный сайт с котиками для этого добавьте под use_backend в разделе https backend
default_backend cats

И секцию backend

backend cats         mode http         option httpchk         option forwardfor if-none         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-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)]         server s1 127.0.0.1:[ваш порт сайта]

Заключение

В статье мы разобрали работу с GeoIP в HAProxy, сформировали списки, защитили наши сервисы от определенного кол-ва ботов через отсечение континентов от наших frontend ‘ов, показал пример как можно маршрутизировать OCServ, VLESS, и основной домен с сайтами и добавил ssh на 443 порт.

Полная конфигурация HAProxy

Скрытый текст
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 'diosrg8siojgiodsjhosdijhoisdfjsd' # поменять на полученный из acme         stats timeout 30s         user    haproxy         group   haproxy         daemon         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-options prefer-client-ciphers no-tls-tickets ssl-min-ver TLSv1.2         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-options no-tls-tickets ssl-min-ver TLSv1.2         ssl-dh-param-file /etc/haproxy/dh4096.pem       # Тюнинг http/2 для WebSocket         tune.h2.initial-window-size 536870912 # Увеличиваем начальный размер окна для входящих и исходящих соединений.         tune.h2.fe.max-concurrent-streams 512 # Установим кол-во одновременных потоков на входящие соединений.         tune.h2.fe.glitches-threshold 1024 # Установим порог автоматического завершения соединения при превышении заданного кол-ва ошибок.         tune.h2.be.max-concurrent-streams 512 # Установим кол-во одновременных потоков на исходящие соединений.         tune.h2.be.glitches-threshold 1024 # Установим порог автоматического завершения соединения при превышении заданного кол-ва ошибок в 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         nameserver ns2 127.0.0.53: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 stats         mode http         bind 127.0.0.1:58080         stats enable         stats uri /stats         stats realm Haproxy\ Statistics         stats refresh 10s         stats auth sdvhsis3w:opsdiv90-ikps         stats show-legends         stats hide-version #------------------------------------------ frontend http         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 }         # Защитим от перегрузки         # Если в течении 1m будет выполнено более 10 запросов отклоним ответом 429 Too Many Requests         stick-table type ip size 100k expire 2m store http_req_rate(30s)         http-request track-sc0 src         # Отклоним с ответом         http-request deny deny_status content-type text/html lf-string "<p>Per our policy, you are limited to 10 requests per minute, but you have exceeded that limit with %[sc_http_req_rate(0)] requests per minute.</p>" if { sc_http_req_rate(0) gt 30 }        # Заблочим Африку, Азию, Южную Америку, Океанию, Европу и Северную Америку не блочим для запросов ACME         http-request deny deny_status 403 if { src -f /etc/haproxy/geoip/AF.acl } || { src -f /etc/haproxy/geoip/AS.acl } || { src -f /etc/haproxy/geoip/AN.acl } || { src -f /etc/haproxy/geoip/OC.acl } || { src -f /etc/haproxy/geoip/SA.acl }           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 redirect scheme https unless { ssl_fc }  frontend tcp         bind *:443 # ssl passthrough         mode tcp         option tcplog         tcp-request inspect-delay 6s        # Заблочим Африку, Азию, Южную/Северную Америку, Океанию и Антарктику         tcp-request connection reject if { src -f /etc/haproxy/geoip/AF.acl } || { src -f /etc/haproxy/geoip/AS.acl } || { src -f /etc/haproxy/geoip/AN.acl } || { src -f /etc/haproxy/geoip/OC.acl } || { src -f /etc/haproxy/geoip/SA.acl } || { src -f /etc/haproxy/geoip/NA.acl }       # Защитим от перегрузки, если с одного IP за 10сек открыто более 30 соедиенний, отклоним все запросы и закроем все соединения      # Нужно учитывать что WsTunnel с активным параметром --connection-min-idle <n> будет занимать указанное колличество при старте      # У VLESS так же есть мультилексор, тоже нужно учитывать при настройке      # Может так случиться если несколько клиентов в одно и тоже время с одного IP решат подключиться, все они потеряют доступ к серверу.       # Для себя я вывел что 30 одновременных подключений с одного IP на случай если например дома выключат свет         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 30 }          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 } # Я бы не рекомендовал, вы лешитесь доступа если haproxy упадет         use_backend ocserv if { req.ssl_sni -i 1.mydomain.com }          use_backend vless if { req.ssl_sni -i 2.mydomain.com }           default_backend tcp_to_https  frontend https         bind 127.0.0.1:444 accept-proxy ssl crt /etc/haproxy/certs alpn h2,http/1.1 strict-sni         http-request reject if { req.hdr(user-agent) -m sub evil }         http-request reject if { path -m sub /. }       # Защитим от перегрузки, если перестанет работать WsTunnel (чем больше клиентов тем выше установить счетчик)      # Если в течении 30с будет выполнено более 30 запросов отклоним ответом 429 Too Many Requests         stick-table type ip size 100k expire 2m store http_req_rate(30s)         http-request track-sc0 src      # Отклоним с ответом о превышенном кол-ве запросов         http-request deny deny_status content-type text/html lf-string "<p>Per our policy, you are limited to 30 requests per 30s, but you have exceeded that limit with %[sc_http_req_rate(0)] requests per 30s.</p>" if { sc_http_req_rate(0) gt 30 }      # GeoIP блокировка всех кроме RU и белого списка (можете воспользоваться content-type из примера выше, сообщить о запрете доступа с их IP вывести IP адрес.)      # Добавьте свой IP перед использованием в белый список, применять осторожно        # http-request deny deny_status 403 if !{ src -f /etc/haproxy/geoip/RU.list } || !{ src -f /etc/haproxy/mylist.acl }          http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains; preload;" # Устанавливаем HSTS          use_backend http_stats if { ssl_fc_sni -i mydomain.com } { path_beg /ha-stats-secret-path/ }         use_backend http_xui if { ssl_fc_sni -i 2.mydomain.com } { path_beg /my-secret-path2/ }         use_backend dns_query if { ssl_fc_sni -i mydomain.com } { path_beg /dns-query/ } || { path /dns-query }         use_backend ws-socks if { ssl_fc_sni -i mydomain.com } { path /ws-socks-secret-path } || { path_beg /ws-socks-secret-path/ }        # default_backend http_adh # панель настройки AdGuardHome        # default_backend cats # раскомментируйте после добавления сайта обманки #->-----tcp-backend-------------------------- backend tcp_to_https         mode tcp         server https 127.0.0.1:444 send-proxy-v2-ssl-cn check port 444 backend tcp-ssh         mode tcp         option http-keep-alive         timeout http-keep-alive 30s         server ssh 127.0.0.1:22 check port 22 backend ocserv         mode tcp         server s1 127.0.0.1:29999 send-proxy check port 29999 backend vless         mode tcp         timeout tunnel 1h         server s1 127.0.0.1:50001 send-proxy check port 50001 #->->---http-backend------------------------- backend http_stats         mode http         http-request replace-path /ha-stats-secret-path(/)?(.*) /\2         server stats 127.0.0.1:58080 backend http_xui         mode http         option httpchk GET /xui/         option forwardfor if-none         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-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)]         server s1 127.0.0.1:54321 check backend dns_query         mode http         option httpchk         option forwardfor if-none         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-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)]         server doh 127.0.0.1:30000 #backend http_adh #        mode http #        server adh 127.0.0.1:30000 backend ws-socks         mode http         option http-keep-alive         timeout http-keep-alive 25s         timeout tunnel 1h         server s1 127.0.0.1:40001 check  #Здесь вы добавите секцию backend cats для маскировки

Дополнение по OCServ для доступа в интернет

Скрытый текст

Необходимо добавить в /etc/sysctl.conf следующую строку
net.ipv4.ip_forward = 1
и применить конфигурацию sysctl -p

Внесем изменения в /etc/default/ufw
Изменим DEFAULT_FORWARD_POLICY=»DROP» на DEFAULT_FORWARD_POLICY=»ACCEPT»

Добавим NAT в /etc/ufw/before.rules перед *filter

*nat :POSTROUTING ACCEPT [0:0] -A POSTROUTING -s 10.13.14.0/24 -o eth0 -j MASQUERADE COMMIT

После выполнить ufw disable и ufw enable


ссылка на оригинал статьи https://habr.com/ru/articles/877566/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *