18 KiB
date, type, session_id, started, ended, messages, tools, tags
| date | type | session_id | started | ended | messages | tools | tags | ||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 2026-04-28 | claude-session | 3ccbd317-34fd-4954-ade5-01248b0bfd07 | 2026-04-28T08:27:41.560Z | 2026-04-28T19:53:44.472Z | 17 |
|
|
# Промт для Claude Code: развёртывание RustDesk API Server (lejianwen)
Запрос
Промт для Claude Code: развёртывание RustDesk API Server (lejianwen)
Контекст
Я системный администратор. У меня уже работает Proxmox VE на домене dttb.ru с следующей инфраструктурой:
- LXC-контейнер с RustDesk OSS Server (hbbs + hbbr) — текущая рабочая инсталляция,
id_ed25519иid_[ed25519.pub](http://ed25519.pub)в/data/rustdesk/server/(примерно, путь уточни через ls) - LXC с Nginx Proxy Manager на dttb.ru — управляет всеми reverse-proxy и Let's Encrypt сертификатами
- Домен:
[remot.dttb.ru](http://remot.dttb.ru)уже настроен и работает (проброс TCP/UDP стримами на hbbs/hbbr) - NetBird VPN-mesh между площадками для админ-доступа
- DNS управляется через Cloudflare
- Клиенты RustDesk развёрнуты на ~30-50 машинах (НИИКН + несколько клиентских организаций)
Цель
Мигрировать с базового RustDesk OSS Server на stack от lejianwen (https://github.com/lejianwen/rustdesk-api + парный rustdesk-server-s6) для получения:
- Жёсткой изоляции клиентов через
MUST_LOGIN=Y— техник Клиента-А не должен иметь возможности подключиться к машинам Клиента-Б даже зная ID и пароль - Адресных книг и групп — каждый техник видит только свои устройства
- Аудит-логов (login, connection, file_transfer)
- All-In-HTTPS+WebSocket архитектуры — весь трафик через 443/HTTPS, чтобы работать из-за корпоративных фаерволов клиентов
- REST API + Swagger для автоматизации через Claude Code и потенциальную MCP-интеграцию
Критическое требование: миграция должна сохранить существующий ключ сервера (id_ed25519 / id_[ed25519.pub](http://ed25519.pub)), чтобы клиенты на местах продолжили работать без перенастройки. Это нужно проверить отдельно — НИ В КОЕМ СЛУЧАЕ не генерировать новый ключ, не удалять старый.
Задачи
Фаза 1: Аудит и подготовка
- Подключись к Proxmox-хосту по SSH (узнай адрес у меня если нет в
~/.ssh/config). - Найди LXC-контейнер с текущим RustDesk Server. Подскажи команду
pct listиpct config <id>чтобы я понял какой именно. - Проверь и зафиксируй:
- Точный путь к
/data/rustdesk/server/(где лежатid_ed25519иid_[ed25519.pub](http://ed25519.pub)) - Содержимое текущего
compose.ymlилиdocker runпараметров hbbs/hbbr - Какие порты сейчас слушает hbbs (
ss -tulpn | grep -E '2111[4-9]') - Версию текущего OSS-сервера
- Размер БД (если есть
db_v2.sqlite3)
- Точный путь к
- Сделай полный бэкап перед любыми изменениями:
tarвсей директории/data/rustdesk/- Сохрани бэкап в
/root/rustdesk-backup-YYYYMMDD-HHMM.tar.gz - Дополнительно скопируй
id_ed25519*отдельно в/root/rustdesk-keys-backup/
- Покажи мне план действий и ЖДИ моего подтверждения перед фазой 2.
Фаза 2: Развёртывание lejianwen stack
- Останови текущий контейнер (
docker compose downв текущей директории), но не удаляй ни том с ключами, ни саму директорию. - Создай новую директорию для нового стека:
/opt/rustdesk-lejianwen/с подкаталогамиdata/api/и оставь существующий/data/rustdesk/server/нетронутым (новый стек будет смонтирован на него). - Сгенерируй случайный JWT-ключ длиной 32+ символов:
openssl rand -base64 48. Сохрани его в файл/opt/rustdesk-lejianwen/.envс переменнойJWT_KEY=.... Покажи мне, я сохраню его в свою password-store. - Создай
/opt/rustdesk-lejianwen/compose.ymlсо следующим содержимым (проверь актуальный формат env-переменных в README — он мог обновиться):
networks:
rustdesk-net:
external: false
services:
rustdesk:
image: lejianwen/rustdesk-server-s6:latest
container_name: rustdesk
environment:
- RELAY=remot.dttb.ru:21117
- ENCRYPTED_ONLY=1
- MUST_LOGIN=Y
- TZ=Europe/Moscow
- RUSTDESK_API_LANG=ru
- RUSTDESK_API_RUSTDESK_ID_SERVER=remot.dttb.ru:21116
- RUSTDESK_API_RUSTDESK_RELAY_SERVER=remot.dttb.ru:21117
- RUSTDESK_API_RUSTDESK_API_SERVER=https://remot.dttb.ru
- RUSTDESK_API_RUSTDESK_KEY_FILE=/data/id_[ed25519.pub](http://ed25519.pub)
- RUSTDESK_API_JWT_KEY=${JWT_KEY}
- RUSTDESK_API_APP_REGISTER=false
- RUSTDESK_API_APP_DISABLE_PWD_LOGIN=false
- RUSTDESK_API_APP_CAPTCHA_THRESHOLD=3
- RUSTDESK_API_APP_BAN_THRESHOLD=5
- RUSTDESK_API_ADMIN_TITLE=Remote Support Portal
volumes:
- /data/rustdesk/server:/data
- /opt/rustdesk-lejianwen/data/api:/app/data
networks:
- rustdesk-net
ports:
- "21114:21114"
- "21115:21115"
- "21116:21116"
- "21116:21116/udp"
- "21117:21117"
- "21118:21118"
- "21119:21119"
restart: unless-stopped
- Перед запуском убедись:
- Файлы
/data/rustdesk/server/id_ed25519иid_[ed25519.pub](http://ed25519.pub)существуют и не пустые - Контейнер не будет генерировать новый ключ (если в логах после старта появится "generating new key" — это критическая ошибка, останавливай немедленно)
- Файлы
- Запусти
docker compose up -d, отслеживай логи:docker compose logs -f rustdesk. В логах должен появиться сгенерированный парольadmin— поймай его и сохрани в/root/rustdesk-admin-initial-password.txtс правами 600. - Проверь работоспособность:
curl -I http://localhost:21114/_admin/— должен вернуть 200 или 301curl http://localhost:21114/api/heartbeat(если такой endpoint есть, иначе пропусти)ss -tulpn | grep -E '2111[4-9]'— все порты должны слушаться- Сравни
/data/rustdesk/server/id_[ed25519.pub](http://ed25519.pub)с бэкапом — должен быть идентичен. Если отличается — стоп, ключ перегенерировался, нужен rollback.
Фаза 3: Настройка Nginx Proxy Manager
- Подключись к LXC с NPM (или используй его API). Найди существующий Proxy Host для
[remot.dttb.ru](http://remot.dttb.ru)(или Stream-секции). - Текущая конфигурация со Stream'ами для портов 21115-21119 должна остаться — это для совместимости со старыми клиентами без WebSocket.
- Обнови Proxy Host для
[remot.dttb.ru](http://remot.dttb.ru):- Forward Hostname: IP нового LXC с lejianwen-stack
- Forward Port:
21114 - WebSocket Support: включить
- Block Common Exploits: включить
- SSL: уже должен быть Let's Encrypt, проверь что Force SSL и HTTP/2 включены
- В Advanced → Custom Nginx Configuration добавь:
location /ws/id {
proxy_pass http://IP_LXC:21118;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
location /ws/relay {
proxy_pass http://IP_LXC:21119;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 86400s;
proxy_send_timeout 86400s;
}
(подставь реальный IP LXC вместо IP_LXC)
- Проверь
nginx -tвнутри контейнера NPM, перезагрузи если ОК. - С внешней машины (попроси меня проверить или используй curl с другого хоста через NetBird):
curl -I https://remot.dttb.ru/_admin/— должно отвечать 200/301curl -I https://remot.dttb.ru/swagger/index.html— Swagger доступен
Фаза 4: Первичная настройка через web-консоль
- Открой
https://remot.dttb.ru/_admin/, залогинься admin /<пароль из /root/rustdesk-admin-initial-password.txt>. - Помоги мне сменить пароль admin'а (через
docker exec rustdesk ./apimain reset-admin-pwd <новый_пароль>или через UI). - Создай начальную структуру (если возможно через API/CLI — сделай скриптом):
Группы (shared):
НИИКН-паркКлиент-template(как образец, потом дублировать)
Тестовые пользователи:
niikn-admin(полные права на группу НИИКН)niikn-tech1(только подключение)test-client(для проверки изоляции)
- Создай шаблон
RustDesk2.tomlдля клиентов:
rendezvous_server = 'remot.dttb.ru:21116'
nat_type = 1
serial = 1
[options]
custom-rendezvous-server = '[remot.dttb.ru](http://remot.dttb.ru)'
relay-server = '[remot.dttb.ru](http://remot.dttb.ru)'
api-server = 'https://remot.dttb.ru'
key = '<СЮДА_ПОДСТАВЬ_СОДЕРЖИМОЕ_id_[ed25519.pub](http://ed25519.pub)>'
allow-websocket = 'Y'
verification-method = 'use-permanent-password'
approve-mode = 'password'
Прочитай актуальный публичный ключ из /data/rustdesk/server/id_[ed25519.pub](http://ed25519.pub), подставь в шаблон. Сохрани готовый файл в /root/RustDesk2.toml.template.
Фаза 5: Тестирование и rollback-план
-
Подними тестовый клиент (можешь использовать docker
rustdesk/rustdeskили попросить меня запустить на тестовой машине). Проверь:- Подключение к серверу проходит
- Без логина соединение не устанавливается (это ключевая проверка MUST_LOGIN)
- С логином
niikn-tech1доступны только машины из его адресной книги - С логином
test-clientмашины НИИКН не видны и подключение к ним по ID отклоняется
-
Подготовь rollback-скрипт на случай провала:
/root/[rustdesk-rollback.sh](http://rustdesk-rollback.sh), который:- Останавливает новый стек:
cd /opt/rustdesk-lejianwen && docker compose down - Восстанавливает из бэкапа
/root/rustdesk-backup-*.tar.gz - Поднимает старый OSS: возвращается в его директорию и
docker compose up -d - В NPM возвращает старую конфигурацию (тут лучше через бэкап настроек NPM
/data/database.sqliteесли NPM запущен в Docker)
- Останавливает новый стек:
-
Сделай финальный отчёт в
/root/[rustdesk-migration-report.md](http://rustdesk-migration-report.md)со всем что сделано: пути, IP, пароли (хеши), команды для дальнейшего обслуживания, известные проблемы.
Что важно соблюдать
- Действуй пошагово, после каждой фазы дожидайся моего подтверждения. Не уходи в Фазу 3 без ОК на Фазу 2.
- Не удаляй ничего до успешного теста новой системы. Старый OSS-стек должен оставаться на диске минимум 7 дней.
- При любой ошибке с ключом сервера (
id_[ed25519.pub](http://ed25519.pub)изменился, hbbs его перегенерировал, не подхватил из volume) — немедленный stop и rollback. Это самый критичный риск всей миграции. - Если что-то непонятно в моей инфраструктуре — спрашивай, не предполагай. Например, точный IP контейнеров, реальные пути в моём compose, версии Docker.
- Команды, требующие sudo, обязательно объясни перед запуском.
- Не выкладывай в логи и не сохраняй в git значения
JWT_KEY, паролей и публичный домен в открытом виде в файлах, которые могут быть закоммичены. - Всё, что меняется в системе, должно быть idempotent или иметь rollback. Никаких
rm -rfбез бэкапа на соседнем диске или площадке.
Что НЕ делать
- НЕ переходить сразу на MySQL-схему с репликацией — это будет отдельная задача после стабилизации базового стека на SQLite
- НЕ настраивать LDAP/OIDC до того, как обычный логин/пароль протестирован и работает
- НЕ обновлять клиенты RustDesk на местах (это я делаю отдельно через свои существующие GPO/скрипты)
- НЕ трогать существующие VPN-конфигурации (NetBird, AmneziaVPN и прочее)
- НЕ менять DNS-записи в Cloudflare без моего явного согласия
- НЕ публиковать
_adminинтерфейс без аутентификации, не открывать его из публичного интернета без проверки fail2ban / bantreshold
После завершения
Когда всё работает и протестировано, отдельной задачей мы:
- Раскатаем новый
RustDesk2.tomlна парк через GPO/скрипт - Заведём полную структуру клиентских групп
- Настроим бэкапы БД API + ключей на ArtLeon (cron + rsync через NetBird)
- Поднимем второй экземпляр на НИИКН Proxmox с общей MySQL для отказоустойчивости
- Напишем MCP-обёртку поверх Swagger API для работы из Claude Code
Начинай с Фазы 1, фиксируй всё в /root/[rustdesk-migration-report.md](http://rustdesk-migration-report.md) по ходу.
Итог (последний ответ Claude, до 500 симв.)
🎉 Поехало!
HTTP/1.1 101 Switching Protocols ← /ws/id через :21118 wss
HTTP/1.1 101 Switching Protocols ← /ws/relay через :21119 wss
Стек теперь полный:
- Браузер →
wss://remot.dttb.ru:21118(TLS) - → docker port mapping NPM :21118
- → nginx TLS-decrypt (мой
stream.conf) - → plain WebSocket →
10.0.0.244:21118(hbbs) - → hbbs отвечает 101 ✅
Что сделано:
- Положил compose в
/data/compose/2/docker-compose.yml(host) с портами 21115-21119 docker compose -p npm up -dп
Автосохранено из session 3ccbd317 — 17 сообщ., тулы: Bash, Edit, Read, TodoWrite, ToolSearch, Write, mcp__Claude_in_Chrome__list_connected_browsers, mcp__Claude_in_Chrome__switch_browser