Комментарии 6
Дополнение к статье
VLESS TCP TLS
РКН начали ломать TLS, что бы обойти можно открыть диапазон портов, пример для секции f_tcp:
frontend f_tcp
bind [::]:443 v4v6 # SSL Passthrough
bind [::]:4000-9000 v4v6 # SSL Passthrough extended_1 ports // ufw allow 4000:9000/tcp
bind [::]:15000-20000 v4v6 # SSL Passthrough extended_2 ports // ufw allow 15000:20000/tcp
bind [::]:63000-65534 v4v6 # SSL Passthrough extended_4 ports // ufw allow 63000:65534/tcp
Пока Сибирь и ДВ жаловались на проблемы с REALITY, VLESS TLS нужно тестировать.
Создадим скрипт который будет мониторить обновление сертификата через сокет haproxy:
import subprocess
import re
import time
import os
# Путь к файлу для хранения даты сертификата
CERT_DATA_FILE = '/tmp/certdata.txt'
# Путь к сокету HAProxy
SOCKET_PATH = '/var/run/haproxy/admin.sock'
# Команда для получения информации о сертификате
COMMAND = f'echo "show ssl cert /etc/haproxy/certs/$вашдомен.pem" | nc -U {SOCKET_PATH}'
# Период проверки в секундах
CHECK_INTERVAL = 25
def get_cert_not_before():
"""Получает дату 'notBefore' из сертификата."""
try:
result = subprocess.check_output(COMMAND, shell=True, text=True)
match = re.search(r'notBefore:\s*(.*)', result)
if match:
return match.group(1).strip()
except subprocess.CalledProcessError as e:
print(f"Error executing command: {e}")
return None
def read_previous_cert_date():
"""Читает предыдущую дату сертификата из файла."""
if os.path.exists(CERT_DATA_FILE):
with open(CERT_DATA_FILE, 'r') as f:
return f.read().strip()
return None
def write_cert_date(date):
"""Записывает дату сертификата в файл."""
with open(CERT_DATA_FILE, 'w') as f:
f.write(date)
def restart_service():
"""Перезапускает сервис x-ui."""
try:
subprocess.run(['systemctl', 'restart', 'x-ui'], check=True)
print("Сервис x-ui перезапущен.")
except subprocess.CalledProcessError as e:
print(f"Ошибка при перезапуске сервиса: {e}")
def main():
"""Основная функция для мониторинга сертификата."""
while True:
current_cert_date = get_cert_not_before()
if current_cert_date:
previous_cert_date = read_previous_cert_date()
if current_cert_date != previous_cert_date:
print(f"Дата сертификата изменилась: {previous_cert_date} -> {current_cert_date}")
write_cert_date(current_cert_date)
restart_service()
else:
print("Дата сертификата не изменилась.")
time.sleep(CHECK_INTERVAL)
if __name__ == "__main__":
main()
Теперь сделаем его исполняем chmod 700 certmon.py
Разместим в systemd
nano /etc/systemd/system/certmon.service
[Unit]
Description=Monitor HAProxy SSL Certificate
[Service]
ExecStart=/usr/bin/python3 /path/to/your/certmon.py
Restart=always
[Install]
WantedBy=multi-user.target
Для VLESS TLS необходимо настроить хранилище сертификатов таким же путем как и с haproxy, в директорию /etc/x-ui/ssl
После получим сертификат:
#!/bin/sh
DEPLOY_HAPROXY_HOT_UPDATE=yes \
DEPLOY_HAPROXY_STATS_SOCKET=/var/run/haproxy/admin.sock \
DEPLOY_HAPROXY_PEM_PATH=/etc/haproxy/certs \
acme.sh --issue -d $ВАШДОМЕН --stateless && \
acme.sh --install-cert -d $ВАШДОМЕН --cert-file /etc/x-ui/ssl/cert.pem \
--key-file /etc/x-ui/ssl/cert.key --fullchain-file /etc/x-ui/ssl/fullchain.ru.pem && \
acme.sh --deploy -d $ВАШДОМЕН --deploy-hook haproxy
Подождем и зайдем в панель, и настроим vless tls
Обязательно указываем в inbound, мониторинг IP /var/lib/haproxy/xui/vlesstls.sock,0660
Proxy Protocol = true
Sockopt = true
TCP Fast Open = true
Multipath TCP = true
External Proxy = true
Тот же / cloud.вашдомен / 443
Безопасность TLS
SNI cloud.вашдомен
uTLS на ваш выбор
ALPN h2 http/1.1
И укажем путь к сертификату
Публичный ключ: /etc/x-ui/ssl/cert.pem
Закрытый ключ: /etc/x-ui/ssl/cert.key.
Теперь обязательно создадим fallback для маскировки,
В настройках inbound VLESS TLS добавляем fallback, sni alpn path оставляем пустыми, в Dest указываем /var/lib/haproxy/fakevless1.sock и xVer устанавливаем в 1.
Теперь настроим haproxy, настроим фронтенд tcp для отправки трафика на x-ui:
use_backend tcp_to_vlesstls if { req.ssl_sni -i cloud.вашдомен }
Теперь настроим backend:
backend tcp_to_vlesstls # cloud
mode tcp
server reality1 /x-ui/vlesstls.sock send-proxy tfo #check
retry-on conn-failure empty-response response-timeout
Теперь создадим фейковый сервис:
frontend f_vless # cloud
bind /var/lib/haproxy/fakevless1.sock allow-0rtt tfo accept-proxy alpn h2,http/1.1 mode 660 user haproxy group haproxy
http-request reject if { req.hdr(user-agent) -m sub evil }
acl url_discovery path /.well-known/caldav /.well-known/carddav
http-request redirect location /remote.php/dav/ code 301 if url_discovery
http-request deny if { path -m sub /. }
stick-table type ip size 100k expire 2m store http_req_rate(10s)
http-request track-sc0 src
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }
http-response set-header X-Content-Type-Options nosniff
http-response set-header X-XSS-Protection 1;mode=block
http-response set-header X-Frame-Options SAMEORIGIN
http-response set-header Strict-Transport-Security "max-age=16000000;" # Устанавливаем HSTS
default_backend nextcloud
Однако, можно провернуть все тоже самое без дополнительного frontend'а добавив необходимое в основной frontend, приме:
frontend f_https #
bind abns@frontendhttps.sock accept-proxy ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 strict-sni tfo
bind /var/lib/haproxy/fakevless1.sock allow-0rtt tfo accept-proxy alpn h2,http/1.1 mode 660 user haproxy group haproxy
http-request reject if { req.hdr(user-agent) -m sub evil }
http-request deny if { path -m sub /. } # запрет доступа к скрытым файлам
http-response set-header X-Content-Type-Options nosniff
http-response set-header X-XSS-Protection 1;mode=block
http-response set-header X-Frame-Options SAMEORIGIN
http-after-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains;" # Устанавливаем HSTS
stick-table type ip size 100k expire 5m store http_req_rate(10s)
http-request track-sc0 src
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 300 }
http-request return status 200 content-type text/plain lf-string "Status 200 OK! Your IP %[src]." if { path /https-status-443 }
use_backend nextcloud if { req.hdr(host) -i cloud.вашдомен } || { ssl_fc_sni -i вашдомен }
use_backend http_adh if { ssl_fc_sni -i вашдомен } { path_beg -i /dns-query/ }
Спасибо за пост. Напишите пожалуйста область применения.
mTLS для сервисов внутри компании, сотрудник ушел - отозвали. Так же этим методом защищаю админ панели и метрики.
GeoIP как раз применяется для маршрутизации по странам.
DoH что бы раздавать клиентам в OpenVPN приватные домены.
Автопродление сертификатов без перезапуска HAProxy через acme и deploy метод.
С 3xui я думаю понятно.
отклоним невалидные юзер агенты
http-request reject if { req.hdr(user-agent) -m len le 32 }
curl и wget такие

Это если они нужны ) а если пользователи только браузера и по другому не умеют, то им оно и не нужно, прикрываем лозейку и все.
ну и curl и wget юзерагент могут в ключах командной строки указать.
Так как решение не для открытого интернета, в целях тестирования чего-либо указываем кастомный UA который не будет блокироваться, и все
Дополнение к статье
VLESS TCP TLS
РКН начали ломать TLS, что бы обойти можно открыть диапазон портов, пример для секции f_tcp:
frontend f_tcp
bind [::]:443 v4v6 # SSL Passthrough
bind [::]:4000-9000 v4v6 # SSL Passthrough extended_1 ports // ufw allow 4000:9000/tcp
bind [::]:15000-20000 v4v6 # SSL Passthrough extended_2 ports // ufw allow 15000:20000/tcp
bind [::]:63000-65534 v4v6 # SSL Passthrough extended_4 ports // ufw allow 63000:65534/tcp
Пока Сибирь и ДВ жаловались на проблемы с REALITY, VLESS TLS нужно тестировать.
Создадим скрипт который будет мониторить обновление сертификата через сокет haproxy:
import subprocess
import re
import time
import os
# Путь к файлу для хранения даты сертификата
CERT_DATA_FILE = '/tmp/certdata.txt'
# Путь к сокету HAProxy
SOCKET_PATH = '/var/run/haproxy/admin.sock'
# Команда для получения информации о сертификате
COMMAND = f'echo "show ssl cert /etc/haproxy/certs/$вашдомен.pem" | nc -U {SOCKET_PATH}'
# Период проверки в секундах
CHECK_INTERVAL = 25
def get_cert_not_before():
"""Получает дату 'notBefore' из сертификата."""
try:
result = subprocess.check_output(COMMAND, shell=True, text=True)
match = re.search(r'notBefore:\s*(.*)', result)
if match:
return match.group(1).strip()
except subprocess.CalledProcessError as e:
print(f"Error executing command: {e}")
return None
def read_previous_cert_date():
"""Читает предыдущую дату сертификата из файла."""
if os.path.exists(CERT_DATA_FILE):
with open(CERT_DATA_FILE, 'r') as f:
return f.read().strip()
return None
def write_cert_date(date):
"""Записывает дату сертификата в файл."""
with open(CERT_DATA_FILE, 'w') as f:
f.write(date)
def restart_service():
"""Перезапускает сервис x-ui."""
try:
subprocess.run(['systemctl', 'restart', 'x-ui'], check=True)
print("Сервис x-ui перезапущен.")
except subprocess.CalledProcessError as e:
print(f"Ошибка при перезапуске сервиса: {e}")
def main():
"""Основная функция для мониторинга сертификата."""
while True:
current_cert_date = get_cert_not_before()
if current_cert_date:
previous_cert_date = read_previous_cert_date()
if current_cert_date != previous_cert_date:
print(f"Дата сертификата изменилась: {previous_cert_date} -> {current_cert_date}")
write_cert_date(current_cert_date)
restart_service()
else:
print("Дата сертификата не изменилась.")
time.sleep(CHECK_INTERVAL)
if __name__ == "__main__":
main()
Теперь сделаем его исполняем chmod 700 certmon.py
Разместим в systemd
nano /etc/systemd/system/certmon.service
[Unit]
Description=Monitor HAProxy SSL Certificate
[Service]
ExecStart=/usr/bin/python3 /path/to/your/certmon.py
Restart=always
[Install]
WantedBy=multi-user.target
Для VLESS TLS необходимо настроить хранилище сертификатов таким же путем как и с haproxy, в директорию /etc/x-ui/ssl
После получим сертификат:
#!/bin/sh
DEPLOY_HAPROXY_HOT_UPDATE=yes \
DEPLOY_HAPROXY_STATS_SOCKET=/var/run/haproxy/admin.sock \
DEPLOY_HAPROXY_PEM_PATH=/etc/haproxy/certs \
acme.sh --issue -d $ВАШДОМЕН --stateless && \
acme.sh --install-cert -d $ВАШДОМЕН --cert-file /etc/x-ui/ssl/cert.pem \
--key-file /etc/x-ui/ssl/cert.key --fullchain-file /etc/x-ui/ssl/fullchain.ru.pem && \
acme.sh --deploy -d $ВАШДОМЕН --deploy-hook haproxy
Подождем и зайдем в панель, и настроим vless tls
Обязательно указываем в inbound, мониторинг IP /var/lib/haproxy/xui/vlesstls.sock,0660
Proxy Protocol = true
Sockopt = true
TCP Fast Open = true
Multipath TCP = true
External Proxy = true
Тот же / cloud.вашдомен / 443
Безопасность TLS
SNI cloud.вашдомен
uTLS на ваш выбор
ALPN h2 http/1.1
И укажем путь к сертификату
Публичный ключ: /etc/x-ui/ssl/cert.pem
Закрытый ключ: /etc/x-ui/ssl/cert.key.
Теперь обязательно создадим fallback для маскировки,
В настройках inbound VLESS TLS добавляем fallback, sni alpn path оставляем пустыми, в Dest указываем /var/lib/haproxy/fakevless1.sock и xVer устанавливаем в 1.
Теперь настроим haproxy, настроим фронтенд tcp для отправки трафика на x-ui:
use_backend tcp_to_vlesstls if { req.ssl_sni -i cloud.вашдомен }
Теперь настроим backend:
backend tcp_to_vlesstls # cloud
mode tcp
server reality1 /x-ui/vlesstls.sock send-proxy tfo #check
retry-on conn-failure empty-response response-timeout
Теперь создадим фейковый сервис:
frontend f_vless # cloud
bind /var/lib/haproxy/fakevless1.sock allow-0rtt tfo accept-proxy alpn h2,http/1.1 mode 660 user haproxy group haproxy
http-request reject if { req.hdr(user-agent) -m sub evil }
acl url_discovery path /.well-known/caldav /.well-known/carddav
http-request redirect location /remote.php/dav/ code 301 if url_discovery
http-request deny if { path -m sub /. }
stick-table type ip size 100k expire 2m store http_req_rate(10s)
http-request track-sc0 src
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }
http-response set-header X-Content-Type-Options nosniff
http-response set-header X-XSS-Protection 1;mode=block
http-response set-header X-Frame-Options SAMEORIGIN
http-response set-header Strict-Transport-Security "max-age=16000000;" # Устанавливаем HSTS
default_backend nextcloud
Однако, можно провернуть все тоже самое без дополнительного frontend'а добавив необходимое в основной frontend, приме:
frontend f_https #
bind abns@frontendhttps.sock accept-proxy ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 strict-sni tfo
bind /var/lib/haproxy/fakevless1.sock allow-0rtt tfo accept-proxy alpn h2,http/1.1 mode 660 user haproxy group haproxy
http-request reject if { req.hdr(user-agent) -m sub evil }
http-request deny if { path -m sub /. } # запрет доступа к скрытым файлам
http-response set-header X-Content-Type-Options nosniff
http-response set-header X-XSS-Protection 1;mode=block
http-response set-header X-Frame-Options SAMEORIGIN
http-after-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains;" # Устанавливаем HSTS
stick-table type ip size 100k expire 5m store http_req_rate(10s)
http-request track-sc0 src
http-request deny deny_status 429 if { sc_http_req_rate(0) gt 300 }
http-request return status 200 content-type text/plain lf-string "Status 200 OK! Your IP %[src]." if { path /https-status-443 }
use_backend nextcloud if { req.hdr(host) -i cloud.вашдомен } || { ssl_fc_sni -i вашдомен }
use_backend http_adh if { ssl_fc_sni -i вашдомен } { path_beg -i /dns-query/ }
Маршрутизация силами Haproxy, DoH, GeoIP, защита сервисов через mTLS и выгрузка метрик в Prometheus, настройка ACME.SH