Это моя первая статья, я буду ссылаться на других авторов и статьи для уточнения и инструкции.
Так как про VPN писать у нас запрещено, напишу про балансировку и маршрутизацию в Haproxy, про VPN дам лишь общие рекомендации для защиты удаленного доступа. Я использую VPS на территории РФ что бы не нарушать законодательство, мы ведь настраиваем средства для удаленного доступа и защиты информации, ведь так?! Приступим
1. Нам потребуется два домена (поддомена), подойдут и технические домены хостера.
2. Сам VPS (лучше 2 ядра) по результатам тестов канал в 200мбит можно утилизировать от 150 до 190мбит, но об этом позже.
Все эксперименты провожу на Ubuntu 22.04 LTS
Обновляем список пакетов и обновляем систему работаю от sudo пользователя:
sudo apt update && apt upgrade -y
Устанавливаем необходимые пакеты: sudo apt install -y haproxy netcat-traditional htop
Фаервол настроили? Если нет настраиваемsudo ufw allow 22/tcp
разрешаем ssh (если не поменяли)sudo ufw allow 80,443/tcp
Разрешаем доступ к Web портам
Сгенерируем DH, терпеливо ждем окончанияsudo openssl dhparam -out /etc/haproxy/dh4096.pem 4096
Настроим Haproxy
Я использую acme.sh для получения сертификатов и deploy метод описанный тут.
В секцию global, пропишем современные шифры, оставим TLSv1.2 на случай блокировки TLSv1.3:
global log /dev/log local0 log /dev/log local1 notice chroot /var/lib/haproxy stats socket /var/run/haproxy/admin.sock level admin mode 660 setenv ACCOUNT_THUMBPRINT '(ваш отпечаток полученный при настройке 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
Пропишем секцию defaults, резолвер DNS и сервер статистики, настроим timeout, для туннелей выставим 1 час.
defaults log global mode http option httplog option dontlognull timeout connect 20s timeout client 20s timeout server 20s timeout tunnel 1h timeout http-request 20s 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.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 user:password stats show-legends stats hide-version
Теперь напишем TCP frontend, правила acl и backend для перенаправления на http frontend и vless
frontend tcp bind *:80,*:443 mode tcp option tcplog tcp-request inspect-delay 10s 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 vless if { req.ssl_sni -i api-app.$вашдомен } use_backend tcp_to_http if { dst_port 80 } #redirect https default_backend tcp_to_https # перенаправляем все на frontend http # use_backend ssh if !{ req.ssl_hello_type 1 } { payload(0,7) -m bin 5353482d322e30 } or !{ req.ssl_hello_type 1 } { req.len 0 } backend tcp_to_http mode tcp server http 127.0.0.1:8080 send-proxy-v2-ssl-cn backend tcp_to_https mode tcp timeout tunnel 1h timeout client 50s timeout server 50s server https 127.0.0.1:8443 send-proxy-v2-ssl-cn backend vless #x-ui mode tcp timeout tunnel 2h balance roundrobin server s1 127.0.0.1:4444 check send-proxy #backend ssh # mode tcp # timeout tunnel 2h # balance roundrobin # server s1 127.0.0.1:22 check
Слушаем 80 и 443 порт в tcp и инспектируем SNI, по умолчанию не прошедший SNI перенаправляем на http frontend.
Все что пришло на 80 порт перенаправляем на backend tcp_to_http который нужен лишь для редиректа на https об этом в секции http frontend, используем proxy protocol v2 ssl cn для передачи информации о клиенте на frontend.
Если раскомментировать use_backend ssh и backend ssh вы получите рабочий ssh на 443 порту.
Теперь секция frontend http:
frontend https bind 127.0.0.1:8080 accept-proxy bind 127.0.0.1:8443 accept-proxy ssl crt /etc/haproxy/certs/ alpn h2,http/1.1 strict-sni http-request deny if { req.hdr(user-agent) -m len le 32 } http-request deny if { req.hdr(user-agent) -m sub evil } http-request deny if { path -m sub /. } http-response set-header Strict-Transport-Security "max-age=16000000; includeSubDomains; preload;" redirect scheme https if !{ ssl_fc } http-request return status 200 content-type text/plain lf-string "%[path,field(-1,/)].${ACCOUNT_THUMBPRINT}\n" if { path_beg '/.well-known/acme-challenge/' } #позволим отвечать haproxy на запросы LE use_backend wstunnel1 if { ssl_fc_sni -i $вашдомен } { path /wss-secretpath1 } || { path_beg /wss-secretpath1/ } use_backend wstunnel2 if { ssl_fc_sni -i $вашдомен } { path /wss-secretpath2 } || { path_beg /wss-secretpath2/ } use_backend http_xui if { ssl_fc_sni -i $вашдомен } { path_beg /xui-secretpath/ } || { path_beg /xui-secretpath } use_backend http_stats if { ssl_fc_sni -i $вашдомен } { path_beg /stats-secretpath/ }
В данном варианте наш http фронтед слушает на портах 8080 и 8443 соответственно и принимает прокси протокол, перенаправляем http на https и устанавливаем HSTS что бы использовать только https.
Теперь разберем acl:use_backend wstunnel
и номер отвечают как за WebSocket, параметр масштабируемый, можно запустить необходимые кол-во экземпляров wstunnel.
use_backend http_xui
тут просто обычно xui запускается на порту 54321 туда и перенаправим.
use_backend http_stats
отвечает за сервер статистики настроенный выше в секции defaults, с логином и паролем user:password (поменяли надеюсь?)
Все правила path_beg
и path
замените на свои, обязательно установите в x-ui путь, для этого сделаете default_backend http_xui
или откройте порт, ufw allow 54321/tcp
а потом закройте как поменяете заменив allow на deny.
Секция backend http:
backend wstunnel1 mode http option http-keep-alive timeout connect 30s timeout http-keep-alive 30s timeout client 50s timeout server 50s timeout tunnel 4h balance roundrobin server s1 127.0.0.5:54443 backend wstunnel2 mode http option http-keep-alive timeout connect 30s timeout http-keep-alive 30s timeout client 50s timeout server 50s #option http-server-close timeout tunnel 4h balance roundrobin server s1 127.0.0.5:5453 backend http_xui mode http option httpchk option forwardfor 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 X-Forwarded-For %[src] http-request set-header X-Forwarded-Host %[req.hdr(host)] http-request set-header X-Forwarded-Proto https if { ssl_fc } http-request set-header X-Forwarded-Proto http if !{ ssl_fc } server x-ui 127.0.0.1:54321 backend http_stats mode http http-request replace-path /stats-secretpath(/)?(.*) /\2 server stats 127.0.0.1:58080
backend wstunnel
отвечают за WebSocket, в данном случае их два.
backend http_xui
понятно отвечает за веб панель x-ui.
http_stats
за сервер статистики.
Теперь когда конфиг haproxy готов, а сертификаты (надеюсь получены) займемся настройкой бекендов.
Я использую wstunnel от erebe, с помощью него можно туннелировать трафик через websocket, можно использовать как общий socks5 прокси, либо туннель для конкретного сервиса, дополнительно можно прочитать на странице проекта, приступим к его настройке:
Берем ссылку со страницы release на момент написания статьи последняя версия 10.1.8.
Создадим директорию в etc:
sudo mkdir /etc/wstunnel
Перейдем в нее: sudo cd /etc/wstunnel
Загрузим релиз wstunnel:
wget https://github.com/erebe/wstunnel/releases/download/v10.1.8/wstunnel_10.1.8_linux_amd64.tar.gz
Создадим пользователя под которым будем запускать на wstunnel:sudo adduser --system wstunnel
Теперь добавим группу:sudo addgroup wstunnel
Теперь добавим нашего пользователя в группу:sudo usermod -aG wstunnel wstunnel
Распакуем tar -xvf wstunnel_10.1.8_linux_amd64.tar.gz
У нас в директории появились файл нас интересует только файл wstunnel, поменяем владельца файла на созданного пользователя sudo chown wstunnel:wstunnel ./wstunnel
Сделаем его исполняемым sudo chmod 700 ./wstunnel
Теперь создадим скрипт запуска и проверки туннеля c именем socksnano /etc/wstunnel/socks.sh
со следующим содержимым:
#!/bin/bash # Настройки WSTUNNEL_PATH="/etc/wstunnel/wstunnel" # Укажите путь к wstunnel WS_URL="ws://127.0.0.5:14443" # URL для туннеля RESTRICT_PATH_PREFIX="wss-secretpath1" # Укажите путь для --restrict-http-upgrade-path-prefix PING_TARGET="127.0.0.5" # Цель для пинга CHECK_PORT=14443 # Порт проверки CHECK_INTERVAL=10 # Интервал проверки в секундах # Запуск wstunnel start_tunnel() { echo "Запуск wstunnel..." $WSTUNNEL_PATH server $WS_URL --restrict-http-upgrade-path-prefix $RESTRICT_PATH_PREFIX & TUNNEL_PID=$! } # Проверки работоспособности туннеля check_tunnel() { while true; do # Проверка порта с помощью netcat if ! nc -z $PING_TARGET $CHECK_PORT; then echo "Туннель не работает, перезапуск..." kill $TUNNEL_PID start_tunnel fi sleep $CHECK_INTERVAL done } # Запуск туннеля и проверки start_tunnel check_tunnel
Рассмотрим скрипт в секции «настройка» мы устанавливаем параметры запуска поменяйте на свои, RESTRICT_PATH_PREFIX обязательно меняем и главное что бы он совпадал с haproxy. Секция «Запуск wstunnel» запустит наш wstunnel с указанными параметрами. Секция «Проверки работоспособности туннеля» отвечает за проверку поднятого туннеля по средствам netcat, проверяем наличие открытого порта wstunnel если порт закрыт перезагрузим нам wstunnel.
Поменяем создателя: sudo chown wstunnel:wstunnel ./socks.sh
Сделаем скрипт исполняемым:
sudo chmod 700
./socks.sh
Теперь можно проверить скрипт выполнив ./socks.sh
Теперь напишем второй скрипт только для работы c системным резолвером DNS для защиты наших DNS запросовsudo nano
/etc/wstunnel/dns.sh
со следующим содержимым (вы так же можете написать и для ssh добавив еще backend и frontend в haproxy с другим путем):
#!/bin/bash # Настройки WSTUNNEL_PATH="/etc/wstunnel/wstunnel" # Укажите путь к wstunnel WS_URL="ws://127.0.0.5:5453" # URL для туннеля RESTRICT_PATH_PREFIX="wss-secretpath2" # Укажите путь для --restrict-http-upgrade-path-prefix PING_TARGET="127.0.0.5" # Цель для пинга CHECK_PORT=5453 # Порт проверки CHECK_INTERVAL=10 # Интервал проверки в секундах Restrict_TO="127.0.0.53:53" # сервис для коннекта в данном случае системный DNS резолвер # Функция для запуска wstunnel start_tunnel() { echo "Запуск wstunnel..." $WSTUNNEL_PATH server $WS_URL --restrict-http-upgrade-path-prefix $RESTRICT_PATH_PREFIX --restrict-to $Restrict_TO & TUNNEL_PID=$! } # Функция для проверки работоспособности туннеля check_tunnel() { while true; do # Проверка порта с помощью netcat if ! nc -z $PING_TARGET $CHECK_PORT; then echo "Туннель не работает, перезапуск..." kill $TUNNEL_PID start_tunnel fi sleep $CHECK_INTERVAL done } # Запуск туннеля и проверки start_tunnel check_tunnel
Выполняем тоже что и со скриптом socks.sh
меняем владельца и даем права на исполнение, отличие только в имени скрипта.
Теперь напишем systemd-юнит для запуска наших скриптов
Создадим файл командойsudo nano /etc/systemd/system/ws-socks.service
со следующим содержимым:
[Unit] Description=Start & Check Wstunnel After=network.target [Service] ExecStart=/etc/wstunnel/socks.sh User=wstunnel Group=wstunnel [Install] WantedBy=multi-user.target
И для DNS:
sudo nano /etc/systemd/system/ws-dns.service
со следующим содержимым:
[Unit] Description=Start & Check Wstunnel After=network.target [Service] ExecStart=/etc/wstunnel/dns.sh User=wstunnel Group=wstunnel [Install] WantedBy=multi-user.target
Запускаем:
sudo systemctl start ws-socks.service ws-dns.service
Проверяем:sudo systemctl status ws-socks.service
sudo systemctl status ws-dns.service
Если в обоих случаях все ок и все процессы запущены, проверим через htop
от какого пользователя запущены, ищем наши процессы, если запущены от пользователя wstunnel то все сделано правильно.
Теперь поставим их в автозагрузку sudo systemctl enable ws-socks.service ws-dns.service
Теперь настроим клиентов, в моем случае первый клиент armdebian с systemd, второй OpenWRT с init.d, и Windows.
Выполним загрузку wstunnel и настройку аналогичную серверу, создадим директорию в etc, добавим пользователя и группу.
Для systemd, запустим на локальной машине socks5 прокси через WebSocket.
Командой создадим сервис:nano /etc/systemd/system/ws-socks.service
[Unit] Description=wstunnel service After=network.target [Service] ExecStart=/etc/wstunnel/wstunnel client --http-upgrade-path-prefix wss-secretpath1 --local-to-remote socks5://0.0.0.0:2080 wss://$ваш.домен:443 --tls-verify-certificate --connection-min-idle 4 Restart=no User=nobody Group=nogroup [Install] WantedBy=multi-user.targets
Теперь второй сервис для запросов DNS на 5353 порту например для локального adguardhome nano /etc/systemd/system/ws-dns.service
[Unit] Description=wstunnel service After=network.target [Service] ExecStart=/etc/wstunnel/wstunnel client --http-upgrade-path-prefix wss-secretpath2 --local-to-remote udp://5353:127.0.0.53:53 wss://$ваш.домен:443 --tls-verify-certificate --connection-min-idle 2 Restart=no User=nobody Group=nogroup [Install] WantedBy=multi-user.targets
Напишем скрипт проверки туннеля socks5 и разместим его так же в /etc/wstunnelnano /etc/wstunnel/chk-socks.sh
#!/bin/bash # настройки проверки TARGET=127.0.0.1 PORT=2080 SERVICE=ws-socks.service # Проверяем доступность порта if nc -z $TARGET $PORT; then # Если соединение установлено, ничего не делаем echo "Порт $PORT доступен." else # Если соединение не удалось, перезапускаем службу echo "Порт $PORT недоступен. Перезапуск службы wstunnel" systemctl restart $SERVICE fi
И второй скрипт для проверки туннеля dnsnano /etc/wstunnel/chk-dns.sh
#!/bin/bash # настройки проверки TARGET=127.0.0.1 PORT=5353 SERVICE=ws-dns.service # Проверяем доступность порта if nc -z $TARGET $PORT; then # Если соединение установлено, ничего не делаем echo "Порт $PORT доступен." else # Если соединение не удалось, перезапускаем службу echo "Порт $PORT недоступен. Перезапуск службы wstunnel" systemctl restart $SERVICE fi
Теперь разместим наши скрипты в Crontab от root: выполнимsudo crontab -e
И добавим в конец:*/5 * * * * /etc/wstunnel/chk-dns.sh
*/5 * * * * /etc/wstunnel/chk-socks.sh
В данных вариантах скрипта проверки мы только проверяем наличие доступного порта, если не доступен перезапускаем сервис.
Теперь напишем клиента для OpenWRT, для начала узнаем архитектуру нашего роутера
командой opkg print-architecture
у моего роутера архитектура aarch64_cortex-a53
Теперь загрузим Wstunnel для нашей архитектуры
Перейдем в tmp:cd /tmp
Загрузим:wget https://github.com/erebe/wstunnel/releases/download/v10.1.8/wstunnel_10.1.8_linux_arm64.tar.gz
Распакуем:tar -xvf wstunnel_10.1.8_linux_arm64.tar.gz
Переместим wstunnel:mv wstunnel /usr/local/bin/wstunnel
Сделаем исполняемым:chown +x /usr/local/bin/wstunnel
Теперь напишем сервис:
vi /etc/init.d/ws-socks
#!/bin/sh /etc/rc.common # Определяем имя службы START=99 STOP=10 SERVICE_NAME="wstunnel socks5" # Определяем команды для запуска и остановки start() { echo "Starting $SERVICE_NAME..." /usr/local/bin/wstunnel client --http-upgrade-path-prefix wss-secretpath1 --local-to-remote socks5://0.0.0.0:2080 wss://$ваш.домен:443 --tls-verify-certificate --connection-min-idle 4 & echo $! > /var/run/$SERVICE_NAME.pid } stop() { echo "Stopping $SERVICE_NAME..." if [ -f /var/run/$SERVICE_NAME.pid ]; then kill $(cat /var/run/$SERVICE_NAME.pid) rm /var/run/$SERVICE_NAME.pid else echo "$SERVICE_NAME is not running." fi }
Аналогично напишем для нашего dns сервиса vi /etc/init.d/ws-dns
#!/bin/sh /etc/rc.common # Определяем имя службы START=99 STOP=10 SERVICE_NAME="wstunnel dns" # Определяем команды для запуска и остановки start() { echo "Starting $SERVICE_NAME..." /usr/local/bin/wstunnel client --http-upgrade-path-prefix wss-secretpath2 --local-to-remote udp://5353:127.0.0.53:53 wss://$ваш.домен:443 --tls-verify-certificate --connection-min-idle 2 & echo $! > /var/run/$SERVICE_NAME.pid } stop() { echo "Stopping $SERVICE_NAME..." if [ -f /var/run/$SERVICE_NAME.pid ]; then kill $(cat /var/run/$SERVICE_NAME.pid) rm /var/run/$SERVICE_NAME.pid else echo "$SERVICE_NAME is not running." fi }
Сделаем скрипты исполняемым: chmod +x /etc/init.d/ws-dns
chmod +x /etc/init.d/ws-socks
Запустим:/etc/init.d/ws-socks start
/etc/init.d/ws-dns start
Добавим в автозагрузку:/etc/init.d/ws-socks enable
/etc/init.d/ws-dns enable
Теперь у вас есть socks5 прокси через wstunnel скрытый за haproxy как альтернатива для VPN, далее можно воспользоваться точечной маршрутизацией например инструкция от itdog для роутеров с использованием tun2socks или просто указать в браузере ip роутера (локального сервера) порт 2080 где развернут wstunnel и получить доступ в удаленную сеть, так же при наличии adguardhome или dnsmasq можете и нужно прописать наш локальный 127.0.0.1:5353 как основной резолвер, тем самым мы защитим наши DNS запросы от подмены.
Клиент для Windows так же скачиваем со страницы проекта, распаковываем переходим в папку и создаем файл с именем например start, копируем туда следующие содержимое:wstunnel client --http-upgrade-path-prefix wss-secretpath1 --local-to-remote socks5://0.0.0.0:2080 wss://$ваш.домен:443 --tls-verify-certificate --connection-min-idle 4
Теперь сохраним его как start.cmd, запустив его получим локальный socks5 прокси, можно забить в браузер или воспользоваться nekobox с маршрутизаций и подключить наш socks5 к нему.
В конфигурации клиента wstunnel присутствует флаг —tls-verify-certificate он нужен для проверки сертификата сервера, если его убрать можно будет подключится к невалидному сертификату например самоподписанному или просроченному.
Рекомендация по настройке VPN
Для мобильных клиентов можно использовать VLESS + TLS
Пример настройки для X-UI:
Протокол VLESS Порт например 4444 (он будет не публичный) Протокол передачи TCP Обязательно включаем PROXY Protocol и External Proxy: Для External Proxy: Тот же $ваш.домен(поддомен для VLESS) 443 Безопасность TLS SNI $ваш.домен(поддомен для VLESS) Cipher Suites Auto Min/Max Version 1.2 1.3 Сертификат должен лежать в директории ./x-ui/cert Сертификат /root/cert/fullchain.pem Ключ /root/cert/privkey.pem
На Хабре уже был целый цикл статей про настройку панелей для VLESS например вот.
Вот мой пример docker compose файла, я использую сеть host чтобы избежать nat докера:
--- 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
Еще не забыли что нужно сайт еще разместить?
Разместите сайт на apache2 или nginx на localhost и любом доступном порте
Откроем конфиг Haproxy sudo nano /etc/haproxy/haproxy.cfg
И добавим в конец:
backend http_defailt mode http option httpchk option forwardfor 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 X-Forwarded-For %[src] http-request set-header X-Forwarded-Host %[req.hdr(host)] http-request set-header X-Forwarded-Proto https if { ssl_fc } http-request set-header X-Forwarded-Proto http if !{ ssl_fc } server s1 127.0.0.1:8090
Теперь в секции frontend http нужно добавить следующий текст под use_backend:
defailt_backend http_defailt
Перезапускаем haproxy sudo systemctl restart haproxy
Проверяем в браузере вводим ваш домен, если открылся сайт все сделано правильно, теперь у вас есть рабочий VPN с VLESS, WebSocket в качестве альтернативы VPN и рабочий сайт обманка.
Если тема будет интересна напишу продолжение с добавлением stunnel со SNI, расширенной маршрутизацией и защитой haproxy по спискам белых IP или блоклистам.
ссылка на оригинал статьи https://habr.com/ru/articles/872080/
Добавить комментарий