Files
knowledge-base/decisions/2026-04-28-rustdesk-lejianwen-pro-migration.md
dttb da0b57e26c RustDesk LXC 116: миграция OSS → lejianwen-pro 1.1.14 (MUST_LOGIN=Y)
- Подмена hbbs/hbbr на pro-бинари из docker lejianwen/rustdesk-server-s6, apt-mark hold
- Systemd overrides: hbbs --must-login Y -k _ -r remot.dttb.ru:21117; hbbr -k _
- config.yaml: lang ru, домен remot.dttb.ru, ключ + jwt прописаны, swagger on
- NPM Proxy Host 14: forward 10.0.0.244, SSL force, http2, /ws/id /ws/relay locations
- NPM streams 21115-21119 пересозданы (id 38-43), Stream 30 удалён
- Admin pw сменён, бэкап + rollback-скрипт в /root/rustdesk-* на LXC 116
- Ключ id_ed25519 идентичен до/после, 13 peers продолжают работать

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-28 12:20:17 +03:00

10 KiB
Raw Permalink Blame History

date, type, tags
date type tags
2026-04-28 migration
rustdesk
lxc116
npm
lejianwen
must-login
isolation

RustDesk migration: OSS hbbs/hbbr → lejianwen-pro

Цель

Закрыть дыру: техник Клиента-А не должен подключаться к машинам Клиента-Б, даже зная ID и пароль. Это задача MUST_LOGIN=Y на уровне hbbs.

Что было

  • LXC 116 rustdeskserver — Debian 12, 10.0.0.244 / 100.70.191.161 (NetBird)
  • Установлено через ProxmoxVE community-script (нативно, не Docker):
    • hbbs 1.1.14 — OSS Purslane (поддерживает только --key, -r, --rendezvous-servers, ALWAYS_USE_RELAY)
    • hbbr 1.1.14 — OSS
    • rustdesk-api 2.7 — lejianwen (это уже не OSS, но без настроек: lang=zh-CN, jwt пустой, домен 192.168.1.66 мусорный)
  • Конфиг /var/lib/rustdesk-api/conf/config.yaml — почти дефолтный, никто не правил
  • БД rustdeskapi.db 352K с 1 admin user + 13 peers (внутренние LAN)
  • NPM Proxy Host 14 для remot.dttb.ru смотрел на мёртвый 10.0.0.43:21114, streams 30-35 на мёртвый 10.0.0.44, SSL force/HTTP/2/block_exploits всё off
  • https://remot.dttb.ru/_admin/ снаружи: 502 Bad Gateway (никто никогда не пользовался web-админкой через домен)
  • Peers всё равно работали: внутренние через LAN 10.0.0.244, внешние видимо через NetBird mesh / DNAT на роутере минуя NPM

Найденная развилка

OSS hbbs не понимает MUST_LOGIN. Этот патч есть только в lejianwen/rustdesk-server-pro (тот же бинарь, что в docker-образе lejianwen/rustdesk-server-s6). Без него изоляция работает только на уровне rustdesk-api (адресные книги по группам), но любой клиент с правильным ключом всё равно может ходить по ID минуя API.

Варианты

Вариант Действие Плюс Минус
A1 Подменить только бинари hbbs/hbbr на pro, остальное оставить Минимум потрясений, ключ/БД/api/NPM streams не трогаем На LXC два разных способа управления (apt + override). apt-mark hold нужен
A2 Оставить OSS, изоляция только через api Ноль рисков ломки Главное требование (изоляция по ID) не закрыто
A3 Полная миграция в Docker s6 (как в исходном промте Олега) Единый стек, проще будущие upgrades Требует Docker, расширения rootfs LXC, миграции БД, больше точек отказа сейчас

Выбран: A1. A3 — отдельной задачей через 2-4 недели когда дойдём до MySQL/HA/MCP-обёртки.

Что сделано

LXC 116

  1. Бэкап /root/rustdesk-backup-20260428-1134/ (ключи, db_v2.sqlite3, rustdeskapi.db, config.yaml.orig, systemd units, JWT, admin pw)
  2. Pro-бинари извлечены из lejianwen/rustdesk-server-s6:latest через docker на Proxmox-host (на самом 10.0.0.250 docker есть, в LXC 116 — нет):
    docker create --name rd-extract lejianwen/rustdesk-server-s6:latest
    docker cp rd-extract:/usr/bin/hbbs /tmp/...
    docker cp rd-extract:/usr/bin/hbbr /tmp/...
    pct push 116 ...
    
  3. Подмена через mv:
    • /usr/bin/hbbs.oss-1.1.14 ← старый
    • /usr/bin/hbbs ← новый pro (поддерживает --must-login Y)
  4. apt-mark hold rustdesk-server-hbbs rustdesk-server-hbbr — apt не перезапишет
  5. Systemd overrides:
    • /etc/systemd/system/rustdesk-hbbs.service.d/override.conf:
      ExecStart=
      ExecStart=/usr/bin/hbbs -k _ -r remot.dttb.ru:21117 --must-login Y
      
    • /etc/systemd/system/rustdesk-hbbr.service.d/override.conf:
      ExecStart=
      ExecStart=/usr/bin/hbbr -k _
      
  6. config.yaml правки (точечно, без пересборки):
    • lang: ru
    • admin.title: "Remote Support Portal"
    • app.show-swagger: 1, ban-threshold: 5
    • rustdesk.id-server / relay-server: remot.dttb.ru
    • rustdesk.api-server: https://remot.dttb.ru
    • rustdesk.key: R0lA4r77hAGw6YRL1qG3JioVqQ0Q0fJfzkwlAGqR6jU= (значение id_ed25519.pub)
    • rustdesk.key-file: /var/lib/rustdesk-server/id_ed25519.pub
    • jwt.key: <openssl rand -base64 48>
  7. Admin pw сменён через CLI: rustdesk-api reset-admin-pwd <pw> (cd в /var/lib/rustdesk-api/)

NPM (LXC 103 через API)

  1. Proxy Host 14 (id 14): forward_host: 10.0.0.244, ssl_forced/http2/block_exploits=true, caching_enabled=false, allow_websocket_upgrade=true
  2. Custom Nginx Configuration (advanced_config) добавлен с location /ws/id (→21118) и /ws/relay (→21119) — для будущей All-in-HTTPS архитектуры (клиенты за корпоративными firewall'ами через 443)
  3. Stream 30 (TCP 21114) удалён — API не должен жить голым TCP
  4. Streams 31-35 пересозданы (id 38-43) на 10.0.0.244 (PUT не работает на streams API — additional properties ошибка; делать DELETE+POST с минимальным body без certificate_id)
  5. Добавлен Stream id 43: 21116/TCP (id-server) дополнительно к Stream id 39: 21116/UDP

Подтверждения

  • sha256sum id_ed25519.pub идентичен до/после миграции (abfbebe9…a38c6b)
  • sha256sum id_ed25519 (приватный) идентичен
  • MUST_LOGIN=Y явно в /var/log/rustdesk-server/hbbs.log после рестарта
  • Key: R0lA4r77hAGw6YRL1qG3JioVqQ0Q0fJfzkwlAGqR6jU= тот же
  • Private key comes from id_ed25519 — pro-hbbs нашёл существующий ключ, не перегенерировал
  • Все 7 портов 21114-21119 слушают
  • https://remot.dttb.ru/_admin/ снаружи: HTTP/2 200 OK (раньше 502)
  • 13 существующих peers продолжают подключаться (БД та же)

Грабли

  1. community-script ставит OSS hbbs, не pro. Бинарь от Purslane Ltd. Версия 1.1.14 у обоих идентична, но pro имеет дополнительный CLI-флаг --must-login Y и читает MUST_LOGIN env. Проверка через grep -ao MUST_LOGIN /usr/bin/hbbs (strings в LXC нет).
  2. Admin API не пускает через curl даже с правильным admin-токеном из /api/admin/login — требует web-сессию. Юзеров и группы создавать через UI.
  3. NPM PUT на streams возвращает 400 additional properties — даже если отправить только разрешённые поля. Решение: DELETE + POST с минимальным body (5 полей: incoming_port, forwarding_host, forwarding_port, tcp_forwarding, udp_forwarding). Без certificate_id.
  4. ENCRYPTED_ONLY env-переменная не нашлась как строка в pro-бинаре hbbs. Возможно управляется через config или этого патча нет в публичном lejianwen/rustdesk-server-s6. MUST_LOGIN сам по себе закрывает изоляцию — шифрование RustDesk и так включено для key-серверов.
  5. NPM streams для портов 21115-21119 по факту не использовались — peers подключались внутри LAN (10.0.0.244 напрямую) или через NetBird (100.70.191.161). Поэтому "поломанная" конфигурация NPM не мешала. Внешний DNAT на роутере dttb.ru вероятно идёт прямо на rustdesk-сервер минуя NPM (надо ещё разок проверить — или подтвердить что внешние клиенты ходят через NetBird).

Rollback

bash /root/rustdesk-rollback.sh — за 30 секунд:

  • mv бинари обратно (/usr/bin/hbbs.oss-1.1.14/usr/bin/hbbs)
  • удаляет systemd overrides
  • восстанавливает config.yaml.orig
  • снимает apt-mark hold
  • рестартует сервисы

NPM-настройки скриптом не откатываются — ручной откат через UI или из /data/database.sqlite LXC 103.

TODO (отдельные задачи)

  • Создать клиентские группы и юзеров через web (Олег, в /_admin/)
  • Протестировать изоляцию на тестовой клиентской машине
  • Раскатать новый RustDesk2.toml на ~30-50 машин через GPO/скрипт (старые конфиги тоже работают — ключ тот же)
  • Бэкап БД API + ключей по cron на ArtLeon через NetBird/rsync
  • Через 2-4 недели — миграция в Docker rustdesk-server-s6 (Вариант A3), MySQL/HA, MCP-обёртка

Связанные файлы

На LXC 116:

  • /root/rustdesk-backup-20260428-1134/ — бэкап и креды
  • /root/rustdesk-rollback.sh — откат (chmod 700)
  • /root/RustDesk2.toml — шаблон клиента
  • /root/rustdesk-migration-report.md — расширенный отчёт

В kb: