Публикация через WAF и AntiDDoS

Публикация через WAF и AntiDDoS — публикация клиентского порта сервера RuDesktop наружу через средства веб-защиты (WAF, AntiDDoS и подобные узлы очистки трафика). Раздел описывает рекомендуемые режимы работы защитного решения (TCP passthrough / L7), требования к публикуемым эндпоинтам, типичные ошибки разделения портов, пример конфигурации Nginx на бэкенде и приёмы диагностики

Важно

Раздел применим только при работе с локальным сервером RuDesktop, развёрнутым внутри периметра организации с внешним защитным узлом перед ним

Общий принцип

Клиент RuDesktop использует один клиентский адрес и порт не только для WebSocket, но и для служебных HTTPS API-запросов. Через клиентский порт должны проходить:

  • /v1/ — WebSocket/WSS, основной клиентский канал

  • /api/login/ — авторизация пользователя

  • /api/logout/ — выход из учётной записи

  • /api/ssolist/ — получение списка SSO-доменов

  • /api/user/ — информация об авторизованном пользователе

  • /api/client-settings/ — клиентские настройки

  • /api/banner/ — баннер в клиенте

  • /api/ab/load/ — загрузка адресной книги

  • /root.pem — публичный CA-сертификат сервера RuDesktop

  • /download/ — загрузка дистрибутива клиента, если используется

Важно

Нельзя публиковать наружу только /v1/ как отдельный WebSocket-порт, если остальные API остаются на другом порту. Клиент RuDesktop не разделяет автоматически «API-порт» и «WSS-порт»: если в клиенте указан server.example.ru:8443, то и /v1/, и /api/..., и /root.pem будут запрашиваться через 8443

Рекомендуемый вариант — TCP passthrough

Если WAF/AntiDDoS некорректно обрабатывает WebSocket или смешанный трафик HTTP + WebSocket, рекомендуется публиковать весь клиентский порт RuDesktop в одном из режимов TCP proxy / RAW / L4 passthrough / TLS passthrough

Защитное решение в этом режиме не должно:

  • разбирать HTTP- и WebSocket-протоколы;

  • менять методы, заголовки или тело запроса;

  • инспектировать трафик как обычный короткий HTTP-запрос.

Оно должно передавать TCP-трафик до backend как есть

Рабочая схема:

Клиент RuDesktop
WAF, AntiDDoS  (TCP proxy / RAW / L4 passthrough)
Reverse proxy (Nginx, UserGate, HAProxy)
Сервер RuDesktop

Подсказка

В этом режиме TLS обычно завершается уже на backend-стороне — на встроенном Nginx сервера RuDesktop или на собственном обратном прокси

Когда можно использовать L7

L7-режим допустим только если WAF/AntiDDoS умеет на одном listener (порту) корректно пропускать одновременно:

  • обычные HTTPS API-запросы (/api/..., /root.pem и другие);

  • WebSocket Upgrade на /v1/.

Для /v1/ клиент отправляет:

GET /v1/ HTTP/1.1
Upgrade: websocket
Connection: Upgrade

Успешный ответ сервера:

101 Switching Protocols

Примечание

HAProxy в штатной схеме умеет переводить HTTP-соединение в WebSocket-туннель после upgrade, но для таких соединений критичны корректные заголовки и таймауты. Готовый пример конфигурации — в разделе Развёртывание за обратным прокси

При работе через L7 защитное решение обязательно должно:

  • не менять HTTP-метод для /v1/ — серверный компонент rude принимает на этом пути только GET с Upgrade: websocket, прочие методы (POST, HEAD, OPTIONS) возвращают 404 Not Found;

  • не делать healthcheck на /v1/ — обычный GET без WebSocket-заголовков возвращает 400 Bad Request. Для health-проб используйте GET / на бэкенде rude (отдаёт OK) или GET /root.pem на nginx-vhost;

  • не инспектировать /v1/ как короткий HTTP-запрос — WebSocket-сессия может жить часами;

  • увеличить таймауты для долгоживущих WebSocket-сессий (рекомендуемые значения — proxy_send_timeout 600s, proxy_read_timeout 600s);

  • передавать без модификации заголовки Upgrade и Connection;

  • передавать заголовки Host, X-Real-Ip (в формате $remote_addr:$remote_port), X-Forwarded-For, X-Forwarded-Proto.

Важно

Если L7-решение не умеет корректно пропускать WebSocket — его нужно переводить в режим TCP/L4. Это типовой подход: не все reverse proxy корректно поддерживают WebSocket, и в таких случаях TCP mode — рабочая альтернатива

Разделение портов RuDesktop

Разделение портов между админкой и клиентским трафиком — поддерживаемый сценарий (см. Разделение портов), но критически важно правильно распределить эндпоинты по портам

Правильно:

  • 443 — веб-интерфейс администратора, доступен только внутри сети или через VPN;

  • 8443 — клиентский порт RuDesktop, публикуется наружу через WAF/AntiDDoS.

На клиентском порту 8443 обязательно должны быть доступны:

  • /v1/

  • /api/login/

  • /api/logout/

  • /api/ssolist/

  • /api/user/

  • /api/client-settings/

  • /api/banner/

  • /api/ab/load/

  • /root.pem

  • /download/ — если используется загрузка дистрибутивов через клиентский порт

Неправильно:

  • 443 — API;

  • 8443 — только /v1/ WebSocket.

Такой вариант приведёт к ошибкам авторизации, получения настроек, адресной книги и SSO: клиент запросит /api/login/ через 8443, не получит ответа и не сможет залогиниться

Пример backend Nginx для клиентского порта

Условный пример, который можно адаптировать под конкретную установку:

server {
    listen 8443 ssl;
    server_name rd.example.com;

    ssl_certificate     /path/to/fullchain.pem;
    ssl_certificate_key /path/to/private.key;

    client_max_body_size 1000G;

    location /v1/ {
        proxy_pass http://unix:/run/rudesktop/rude.sock;

        proxy_connect_timeout 120s;
        proxy_send_timeout    600s;
        proxy_read_timeout    600s;

        proxy_http_version 1.1;
        proxy_set_header Host              $http_host;
        proxy_set_header X-Real-Ip         $remote_addr:$remote_port;
        proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Upgrade           $http_upgrade;
        proxy_set_header Connection        "upgrade";
    }

    location /api/ {
        include proxy_params;
        # Для сценария «двойной прокси» (WAF/AntiDDoS перед nginx) переопределяем
        # X-Forwarded-For, чтобы сохранить цепочку IP, добавленную защитным узлом
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_pass http://unix:/run/rudesktop-web.sock;
    }

    location /root.pem {
        expires -1;
        alias /var/lib/rudesktop/ca/CA.pem;
    }

    location /download/ {
        alias /usr/share/rudesktop/public/download/;
    }

    include rudesktop_params;
}

Важно

Для /v1/ заголовок X-Real-Ip должен передаваться в формате $remote_addr:$remote_port — с указанием порта источника. Серверный компонент rude парсит это значение как SocketAddr и при отсутствии порта отбивает соединение с ошибкой Failed to parse X-Real-IP. Для остальных эндпоинтов (/api/, обслуживается Django через proxy_params) достаточно стандартного $remote_addr

Важно

Если WAF/AntiDDoS работает в режиме TCP RAW и не передаёт X-Forwarded-For, backend будет видеть IP защитного узла, а не реальный IP пользователя. Если нужен реальный IP клиента — отдельно согласовывайте поддержку PROXY protocol на стороне защитного решения. Включать proxy_protocol на принимающей стороне можно только если защитный узел действительно его передаёт, иначе все соединения будут разрываться

Диагностика

Проверка TLS-уровня

openssl s_client -connect rd.example.com:8443 -servername rd.example.com -showcerts

Нормально — возвращается сертификат сервера

Плохо — ошибки вида:

wrong version number
packet length too long
no peer certificate available

Обычно это означает путаницу режимов (HTTP, HTTPS, TCP) на стороне защитного узла или включённый PROXY protocol без поддержки на backend

Проверка HTTP API

curl -vk https://rd.example.com:8443/root.pem
curl -vk -X POST https://rd.example.com:8443/api/ssolist/ \
    -H "Content-Type: application/json" \
    -d '{}'

Подсказка

Даже если сервер вернул 401, 403 или 405это лучше, чем обрыв соединения: запрос дошёл до HTTP-приложения, и проблема не в публикации

Плохо — следующие симптомы означают, что трафик до приложения не доходит:

503 Service Unavailable
connection closed before message completed
empty reply from server

Связанные страницы