Skip to content

About

HAProxy deployment for WhatsApp Chat and Media proxy

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

8 Commits

Folders and files

Repository files navigation

Standalone HAProxy proxy for WhatsApp

Нативный setup для Ubuntu 24.04 без Docker. Он устанавливает HAProxy из репозитория Ubuntu и по умолчанию поднимает два публичных TCP-порта:

  • 443: TLS termination и передача chat-трафика в g.whatsapp.net:5222 с корректным PROXY header
  • 587: прозрачная передача media-трафика в whatsapp.net:443

Схема совместима с рекомендацией официального проекта WhatsApp Proxy для неблагоприятных сетевых условий. VoIP официальным proxy не поддерживается.

Изменения версии 1.4.1

  • В шапку README добавлены ссылки на оригинальные репозитории WhatsApp Proxy и HAProxy

Изменения версии 1.4.0

  • Добавлен opt-in режим shared_443: chat и media клиента могут использовать один публичный порт 443, при этом адрес proxy не меняется
  • Публичный 443 становится raw TCP SNI-mux. Известный media SNI передаётся в прежний raw media backend без PROXY header, а остальные соединения идут в local-only TLS terminator chat и далее с обязательным PROXY v1
  • Публичный raw media listener 587 остаётся рабочим fallback; оба media-входа используют один backend и один выбранный direct/SOCKS4 маршрут
  • VM/e2e healthcheck при включённом режиме дополнительно выполняет проверенный TLS handshake до WhatsApp media через общий 443
  • Без блока shared_443 или при enabled: false генерируется byte-for-byte прежняя конфигурация версии 1.3.0

Изменения версии 1.3.0

  • Добавлены независимые исходящие маршруты routes.chat и routes.media: прежний direct либо локальный SOCKS4 bridge на 127.0.0.1
  • HAProxy передаёт через SOCKS4 и рабочий трафик, и L4 health-check; если bridge или его parent недоступен, backend закрывается без direct fallback
  • Новый healthcheck.py --scope routes проверяет SOCKS4 CONNECT до каждого настроенного upstream. Для media дополнительно выполняется TLS handshake с обычной проверкой CA и SNI probes.media_upstream_host
  • Отсутствующий routes или отдельный backend сохраняет прежний byte-for-byte direct render и всю семантику runtime DNS, frontend и PROXY v1

Изменения версии 1.2.1

  • Для каждого hostname-upstream HAProxy использует три независимых DNS-view: resolver VM, Cloudflare (1.1.1.1/1.0.0.1) и Google (8.8.8.8/8.8.4.4). Это позволяет одновременно проверять разные geo-DNS-ответы и выбирать доступный с VM адрес отдельно для chat и media
  • Cold-start readiness больше не принимает переходное состояние UP 1/2: backend считается готовым только после успешного L4 health-check (L4OK)
  • Внешняя YAML-схема не изменилась; literal IPv4 по-прежнему остаётся одним статическим upstream без DNS-ротации

Изменения версии 1.2.0

  • DNS-upstream теперь представлен пулом из 16 независимо проверяемых IPv4: недоступный с VM адрес исключается из новых подключений, пока остальные доступные адреса продолжают обслуживать трафик
  • HAProxy в течение 15 минут сохраняет ранее полученные одиночные A-records, поэтому последовательная DNS-ротация наполняет пул без restart или reload
  • Healthcheck проверяет состояние всего runtime-пула и считает backend рабочим, если назначен и доступен хотя бы один адрес
  • Внешняя YAML-схема не изменилась; literal IPv4 остаётся одним статическим upstream без DNS-ротации

Изменения версии 1.1.0

  • HAProxy переразрешает DNS-имена chat- и media-upstream в runtime и обновляет их IPv4-адреса без restart или reload
  • Внешняя YAML-схема не изменилась: конфигурации версии 1.0.3 продолжают работать без изменений
  • TLS termination и PROXY v1 для chat 443, raw TCP passthrough для media 587, ACL, limits, timeouts и certificate lifecycle не изменились

Изменения версии 1.0.3

  • Production-профили config.<ssh-alias>.yaml и локальный AGENTS.md исключаются из Git и standalone ZIP
  • Release builder исключает локальные окружения и runtime artifacts

Быстрый запуск

unzip whatsapp-haproxy-setup-standalone-1.4.1.zip
cd whatsapp-haproxy-setup
cp config.example.yaml config.yaml
nano config.yaml
chmod +x setuphaproxy.sh cleanhaproxy.sh steps/*.sh tools/*.py
sudo ./setuphaproxy.sh all

В config.yaml обязательно замените server.public_ip. Пустой access.allowed_cidrs означает публичный беспарольный WhatsApp proxy. Это не HTTP- и не SOCKS-прокси: направления backend зафиксированы в HAProxy.

Скрипт поддерживает отдельные шаги:

sudo ./setuphaproxy.sh 0  # диагностика, backup и остановка
sudo ./setuphaproxy.sh 1  # установка HAProxy и зависимостей
sudo ./setuphaproxy.sh 2  # сертификат и конфигурация
sudo ./setuphaproxy.sh 3  # systemd start и VM-side health-check

Можно хранить YAML вне распакованного каталога:

sudo ./setuphaproxy.sh all --config /secure/path/whatsapp-proxy.yaml

Исходящие маршруты

По умолчанию chat и media подключаются к upstream напрямую. Для отдельного backend можно включить локальный SOCKS4 egress:

routes:
  chat:
    mode: "direct"
  media:
    mode: "socks4"
    socks4:
      host: "127.0.0.1"
      port: 11081

routes, routes.chat и routes.media опциональны; отсутствие означает direct. В режиме socks4 обязательны оба поля, host должен быть ровно 127.0.0.1, а port — целым числом 1..65535. HAProxy не принимает здесь логин или пароль: listener должен быть passwordless и доступен только через loopback. Его установка, upstream-аутентификация и failover принадлежат 3proxy-setup; этот проект их не создаёт и не удаляет.

SOCKS4 и check-via-socks4 применяются вместе через backend-local default-server. Поэтому при отказе bridge обычные соединения и health-check не обходят его напрямую. Chat сохраняет обязательный PROXY v1, media остаётся raw TCP passthrough без PROXY header.

При all setup выполняет route preflight до остановки HAProxy и повторяет его перед записью нового конфига. Отдельный step 2 также проверяет route до мутаций. Его можно запустить отдельно:

sudo python3 tools/healthcheck.py --scope routes --config config.yaml

Direct backend только отмечается как не требующий отдельного preflight. Для SOCKS4 preflight получает IPv4-кандидаты через системный resolver VM и пробует CONNECT к ним; media считается рабочим только после проверенного TLS handshake через созданный tunnel. Полные три DNS-view и все runtime slots затем проверяет VM-side healthcheck запущенного HAProxy.

Общий порт 443 для chat и media

Некоторые сети доставляют ClientHello до VM на 443, но блокируют или фильтруют payload на нестандартном media-порту. Для такого случая можно включить экспериментальный SNI-mux:

shared_443:
  enabled: true
  chat_loopback_port: 18443
  probe_sni: "media-hel3-1.cdn.whatsapp.net"
  media_sni:
    exact:
      - "whatsapp.net"
      - "mmg.whatsapp.net"
    suffixes:
      - ".cdn.whatsapp.net"

При включённом режиме public listener 443 не завершает TLS сразу, а ждёт ClientHello до 5 секунд. SNI из media_sni направляется без расшифровки в whatsapp_media; полный TLS ClientHello с неизвестным SNI или без SNI fail-safe направляется в local-only 127.0.0.1:18443, где HAProxy завершает chat TLS и передаёт трафик в whatsapp_chat. Неполный и не-TLS payload отклоняется после inspect-delay.

chat_loopback_port должен быть свободным локальным портом, отличаться от публичных портов и портов настроенных SOCKS4 routes. Его не нужно открывать в UFW или cloud firewall. В правилах SNI разрешены только точные ASCII DNS-имена и узкие suffixes; широкая маска .whatsapp.net запрещена, потому что она перехватила бы chat host g.whatsapp.net. Exact-список обязан содержать probes.media_upstream_host.

probe_sni — обязательный CDN-like SNI для VM/e2e canary; он должен совпадать с одним из exact/suffix правил. Значение выше соответствует реально наблюдаемому Android media ClientHello. Если клиент начинает использовать другой CDN namespace, сначала обновите media rules и этот canary.

Media TLS остаётся сквозным и проверяется телефоном против сертификата WhatsApp; HAProxy видит только незашифрованный SNI стандартного TLS ClientHello. 587 продолжает работать независимо. routes.media, DNS-пулы и health-check backend одинаковы для media на 443 и 587.

Один chat-клиент на общем 443 занимает внешний raw stream и внутренний TLS stream HAProxy. Генератор резервирует для loopback дополнительные process slots и добавляет к process accept-rate резерв в пределах limits.tls_rate_per_second, но отдельная aggregate stick-table по-прежнему ограничивает сумму внешних 443/587 значениями limits.global_connections и limits.global_connection_rate_per_second. Поэтому внутренний re-entry не может быть вытеснен внешними media-соединениями и одновременно не расширяет заданный публичный лимит. ACL и per-IP rate limit применяются только на публичном frontend, а не повторно на loopback. Неполный или не-TLS payload на 443 отклоняется после inspect-delay и не занимает внутренний TLS slot. Резерв предотвращает steady-state starvation, но не гарантирует fairness между listeners во время непрерывного внешнего flood: для публичного proxy cloud DDoS protection остаётся обязательной.

Режим остаётся opt-in: набор media SNI контролируется конфигурацией, поскольку закрытый клиент WhatsApp может менять используемые CDN-имена. Успешный synthetic TLS healthcheck доказывает маршрут и сертификат, но окончательная проверка — отправка и загрузка media в приложении.

Runtime DNS-ротация upstream

HAProxy переразрешает probes.chat_upstream_host и probes.media_upstream_host независимо через три DNS-view: DNS-серверы VM из /etc/resolv.conf, Cloudflare (1.1.1.1/1.0.0.1) и Google (8.8.8.8/8.8.4.4). Ответы разных resolver не смешиваются в гонке за первый ответ: у каждого view собственная группа server slots, а L4 health-check выбирает реально доступные с VM адреса. Если список DNS-серверов VM изменился, HAProxy нужно reload/restart. /etc/hosts в этом маршруте не используется. При SOCKS4-маршруте DNS по-прежнему выполняет HAProxy на VM, а bridge получает уже выбранный IPv4: это не SOCKS4a и не DNS-view страны upstream proxy.

Для каждого DNS-upstream действует следующая семантика:

  • Базовый интервал переразрешения — 5 секунд, когда нет другого триггера; connection timeout health-check может запустить его раньше. Интервал задаёт timeout resolve, а не authoritative DNS TTL и не hold valid
  • HAProxy создаёт по четыре слота на каждый из трёх DNS-view, запрещает дублирование адресов внутри одной группы и накапливает как одновременные, так и последовательные одиночные A-records. Один IP может ожидаемо повториться в разных DNS-view; это не влияет на проверку его доступности
  • Ранее замеченный адрес остаётся кандидатом 15 минут после последнего появления в корректном DNS-ответе. Повторное появление продлевает это окно
  • Каждый назначенный адрес проверяется TCP health-check с VM. Рабочие адреса участвуют в leastconn, недоступные получают DOWN и исключаются из новых подключений. Если UP-адресов нет, backend закрывается без direct fallback
  • Основная проверка выполняется каждые 10 секунд; переходное состояние перепроверяется через 2 секунды, DOWN-адрес — каждые 30 секунд. Для исключения адреса нужны две неудачи подряд, для возврата — один успех
  • Смена адреса или его переход в DOWN не переносит и принудительно не разрывает уже установленные TCP-сессии. Новые сессии используют доступные слоты
  • Внутри неудачного цикла HAProxy делает до трёх DNS-попыток с интервалом 1 секунда и принимает DNS-payload до 4096 байт
  • При NXDOMAIN, REFUSED, timeout и других DNS-ошибках HAProxy проверяет, был ли valid answer за предыдущие 30 секунд. Если нет, соответствующий server выводится из работы до нового корректного answer; затем его готовность снова определяют L4 health-checks
  • init-addr last,none не делает libc fallback. Если прежнего state нет, backend стартует без адреса и сам восстанавливается после успешного DNS-answer
  • VM-side readiness ждёт хотя бы один назначенный сервер со стабильным UP и успешным L4OK в каждом backend; переходные UP 1/2/INI не считаются готовностью. Только после этого проверяются локальные frontend 443/587
  • DNS-пул хранится в памяти процесса. После холодного restart HAProxy или VM он собирается заново из последующих DNS-ответов; отдельного selector-daemon и постоянного кэша нет

Если в upstream указан literal IPv4, HAProxy оставляет его статическим. Для перехода на 1.2.1 не нужно добавлять ключи в config.yaml: все текущие поля и их значения сохраняют прежнюю семантику.

Cloud firewall и DDoS

Setup не меняет cloud security group и не подключает DDoS-защиту. Для публичного сервиса разрешите ingress TCP только на 443 и 587. SSH должен быть ограничен отдельно. Direct backend требует исходящий TCP к своему WhatsApp upstream; SOCKS4 backend требует работоспособный локальный bridge и его собственный egress к parent proxy. VM также нужен DNS-доступ к resolver VM, 1.1.1.1, 1.0.0.1, 8.8.8.8 и 8.8.4.4 на UDP/TCP 53. Если firewall не поддерживает DNS-правила, используйте подходящие общие egress-правила.

UFW по умолчанию не меняется. server.manage_ufw: true добавляет два allow правила, но не заменяет cloud firewall.

Публичного HAProxy stats-порта нет. Диагностика использует локальный сокет /run/haproxy/admin.sock.

Проверки

На VM:

sudo python3 tools/healthcheck.py --scope vm --config config.yaml

С клиентской машины:

python -m venv venv
venv/bin/pip install PyYAML
venv/bin/python tools/healthcheck.py --scope e2e --config config.yaml \
  --json-out whatsapp-haproxy-e2e.json

Windows PowerShell использует venv\Scripts\python.exe. E2E выполняет TLS handshake с самоподписанным frontend 443 и проверенный сквозной TLS handshake до *.whatsapp.net через 587. При shared_443.enabled: true та же проверенная media TLS-проверка дополнительно проходит через общий 443. Он не эмулирует закрытый протокол WhatsApp и не доказывает доставку сообщения в приложении.

Неблокирующая smoke-проверка per-IP лимита:

python tools/healthcheck.py --scope limits --config config.yaml

Настройка WhatsApp

В обычном режиме укажите публичный IP или DNS-имя VM, порт чата 443 и порт медиа 587. При включённом shared_443 можно указать 443 для обоих портов; 587 остаётся fallback. Сертификат chat на 443 создаётся локально при установке и не требует публичного CA. Media TLS на общем 443 HAProxy не завершает.

Полная очистка

Без --yes cleaner всегда работает в dry-run:

./cleanhaproxy.sh
sudo ./cleanhaproxy.sh --yes
sudo ./cleanhaproxy.sh --yes --purge-package --purge-setup

Дополнительные флаги:

sudo ./cleanhaproxy.sh --yes --keep-backups
sudo ./cleanhaproxy.sh --yes --purge-ufw

Cleaner останавливает HAProxy и удаляет /etc/haproxy, выделенные логи, runtime state и backup этого setup. --purge-package удаляет пакет HAProxy, но намеренно не запускает глобальный apt autoremove. Cloud firewall и общий systemd journal не меняются. Cleaner не затрагивает 3proxy.

Источники

About

HAProxy deployment for WhatsApp Chat and Media proxy

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages