Это продолжение статьи про совмещение 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/sufw allow 22/tcp
ufw allow 80,443/tcp
Активируем фаерволufw enable
Теперь настроим unbound в качестве локального резолвера (шаг не обязательный и можно пропустить)
Остановим systemd resolved systemctl stop systemd-resolved.service
Отключим DNSStubListenersh -c 'echo DNSStubListener=no' >> /etc/systemd/resolved.conf
Удалим resolv.confrm -f /etc/resolv.conf
Пропишем localhost в качестве ответчика DNSecho 'nameserver 127.0.0.1' >> /etc/resolv.conf
Запустим systemd resovedsystemctl 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.keychown 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 в группу haproxyadduser root haproxy
Создадим директорию для acmemkdir /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/
Создадим директорию для хранения сертификатов HAProxymkdir /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/scriptnano /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_statsuse_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 добавляем под ocservuse_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 backenddefault_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
Комментарии (12)
vagon333
29.01.2025 12:05Простите, если я недопонимаю конечную цель, но не проще ли поднять PFSense как виртуальную машину, в которой уже есть Acme и HAProxy, как плагины с удобным UI для менеджмента?
jhoag
29.01.2025 12:05echo '' > /etc/haproxy/haproxy.cfg
Можно
echo > /path/to/file
# Заблочим Африку, Азию, Южную Америку, Океанию, Европу и Северную Америку не блочим для запросов ACME
Опечатка, наверно?
ki11j0y Автор
29.01.2025 12:05Опечатка, наверно?
Нет, на f http блочим все кроме этих континентов. В f tcp уже да, отсекаем и америку оставив европу, на f https вообще можно отработать по белому списку
В целом так можно защититься от ботов с этих IP в этом смысл был и защитить сервис определённой локацией или подсетью
jhoag
29.01.2025 12:05Перечитайте предложение так, будто не знаете, что делает код: «Заблочим и Африку, и Азию, и Южную Америку, и Океанию, и Европу, и Северную Америку не блочим».
baldr
Есть ли хоть одна причина делать такое в docker-compose?
Если уж монтируете эту папку, то лучше, наверное, в readonly?
ki11j0y Автор
Я использовал базовый docker compose файл разработчика панели, в том виде в котором пользователь увидит его на github.
Больше скажу, я не использую докер в своих инсталляциях, я использую пользователей с ограниченным набором прав под каждый сервис, это сложнее и дольше в настройке, AdGuardHome например не желает обновляться встроенными механизмами.
baldr
Ну у разработчика два варианта запуска докера - просто через командную строку и через compose. В первом случае сеть явно не задаётся (и хорошо), однако, порты всё равно вешаются на все интерфейсы.
В вашем случае, вероятнее всего, нужно убрать `network_mode: host` и порт указать как
127.0.0.1:54321:54321
, поскольку вы его проксируете.Ммм.. Вообще-то докер примерно это же и делает, только ещё cgroups использует, чтобы ещё больше всех порезать.
ki11j0y Автор
Докер запущенный демоном от рута не является безопасным и побег из контейнера ни кто не отменял, тогда нужно использовать rootless инсталляцию, и уже тогда резать сеть, права на volume.
Acme так же выносить в отдельного пользователя, как в офф инструкции, добавлять его в группы haproxy, docker, ocserv для обновления сертификатов, раздавать права на директории где лежат сертификаты для того что бы acme мог обновить сертификаты, и права на перезапуск ocserv.
Порт в панели после меняется. 54321 только при первом запуске.
В целом это тоже самое если выбрать инсталляцию amnezia на сервер, вы даете приложению root доступ к серверу и пользуетесь готовым продуктом.
baldr
Подождите, это же две разные вещи. "Докер, запущенный демоном от рута" - это просто приложение на Go, которое только запускает процессы. Сами процессы - это нативные Linux-процессы, которым просто ставятся права штатными средствами. После запуска контейнера, теоретически, сам докер ему уже не нужен. В общем-то, вы можете запустить "контейнер" и без докера - на хабре были статьи на эту тему.
Побег из контейнера - это уже эксплуатация уязвимостей Linux. Нужно запускать приложения внутри под непривилегированным юзером, мапить его снаружи на что-то без прав и тп.
Вообще говоря, моё основное замечание было на запуск приложений на интерфейс 0:0:0:0 - поскольку вы их проксируете локально, то лучше порты мапить на 127.0.0.1, чтобы не было доступа снаружи (даже если файрволл вроде как это блокирует).
ki11j0y Автор
Не претендую на истину с докер. Не использую в своих проектах, взял compose файл разработчика, чтобы не описывать ручную установку xray, смысл был не в этом.