Files
knowledge-base/projects/dttb/rustdesk-runbook.md
dttb d58873368a RustDesk: pre-prod security audit + операционный runbook
Аудит перед запуском в прод. Найдены и исправлены critical и medium issues:

Critical (fixed):
- File permissions 644 → 600/640 для id_ed25519, БД, config.yaml
- Нет logrotate (диск 2GB зальётся) → /etc/logrotate.d/rustdesk
- Нет авто-бэкапа → daily cron 03:00 в /root/rustdesk-backups/

Medium (fixed):
- Brute-force на /api/admin/login → NPM rate-limit 5r/m

Medium (deferred):
- NC share без пароля (NC сейчас down — отдельный task)
- Security headers не наследуются в location / (NPM template limitation)

Документация:
- decisions/2026-04-30-rustdesk-pre-prod-audit.md — полный отчёт
- projects/dttb/rustdesk-runbook.md — операционный runbook (recovery,
  troubleshooting, onboarding, updates)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-30 10:42:19 +03:00

348 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
date: 2026-04-30
type: runbook
tags: [rustdesk, runbook, operations, recovery, troubleshooting]
---
# RustDesk Server — Operational Runbook
Операционная инструкция для повседневной эксплуатации, troubleshooting и recovery.
Связанные документы:
- [[rustdesk]] — справочник по серверу
- [[../../decisions/2026-04-28-rustdesk-lejianwen-pro-migration]] — история миграции с OSS
- [[../../decisions/2026-04-29-rustdesk-client-deployment-package]] — пакет установки клиентов
- [[../../decisions/2026-04-30-rustdesk-pre-prod-audit]] — pre-production audit + fixes
- [[credentials#RustDesk Server (LXC 116)]] — креды
---
## 1. Архитектура и компоненты
```
┌────────────────────┐
│ Public Internet │
│ (через роутер │
│ dttb.ru NAT) │
└─────────┬──────────┘
│ 443 (HTTPS) 21115-21119 (TCP/UDP)
┌────────────────────────────────────┐
│ NPM (LXC 103, 10.0.0.195) │
│ • TLS termination + reverse proxy │
│ • Custom stream.conf для wss │
│ • Rate-limit на /api/login │
└─────────┬──────────────────────────┘
│ HTTP/plain TCP
┌──────────────────────────────────────────┐
│ RustDesk Server (LXC 116, 10.0.0.244) │
│ • hbbs (id-server, 21116/UDP+TCP) │
│ • hbbr (relay, 21117 TCP) │
│ • hbbs WebSocket :21118 │
│ • hbbr WebSocket :21119 │
│ • rustdesk-api 2.7 :21114 (web admin) │
│ • SQLite DB │
│ └─ MUST_LOGIN=Y enforced │
└──────────────────────────────────────────┘
```
| Компонент | Откуда | Версия | Where |
|---|---|---|---|
| `hbbs` | lejianwen-pro (extracted from docker `rustdesk-server-s6`) | 1.1.14 | `/usr/bin/hbbs` |
| `hbbr` | lejianwen-pro | 1.1.14 | `/usr/bin/hbbr` |
| `rustdesk-api` | lejianwen `rustdesk-api` deb (community-script) | 2.7 | `/usr/bin/rustdesk-api` |
| `nginx` | NPM (jc21/nginx-proxy-manager) | 2.12.2 / OpenResty | docker container `npm-app-1` |
| Cert | Let's Encrypt | renew auto | `/etc/letsencrypt/live/npm-41/` |
| Bind | systemd `rustdesk-{hbbs,hbbr,api}.service` | — | LXC 116 |
## 2. Daily operations
### 2.1 Проверить здоровье сервера
```bash
# С Mac (или другой машины)
curl -ksI https://remot.dttb.ru/_admin/ # → 200 OK
curl -ksI https://remot.dttb.ru/webclient/ # → 200 OK
# WebSocket endpoints
curl -ks --http1.1 -H "Upgrade: websocket" -H "Connection: Upgrade" \
-H "Sec-WebSocket-Version: 13" -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
--max-time 3 https://remot.dttb.ru:21118/ws/id 2>&1 | grep "101"
# Сервисы на LXC 116
sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 \
"pct exec 116 -- systemctl is-active rustdesk-hbbs rustdesk-hbbr rustdesk-api"
```
### 2.2 Кто сейчас в системе (peers онлайн)
```bash
# Login в админку
ADMIN_PWD=$(cat /root/rustdesk-backup-20260428-1134/admin-password.txt) # на LXC 116
# или из kb: 1qaz!QAZ
# Веб-админка
open https://remot.dttb.ru/_admin/
# admin / 1qaz!QAZ → Devices/Peers вкладка
# Через SQL
sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 "pct exec 116 -- sqlite3 \
/var/lib/rustdesk-api/data/rustdeskapi.db \
'select id, hostname, last_online_ip, datetime(last_online_time,\"unixepoch\") \
from peers order by last_online_time desc limit 20;'"
```
### 2.3 Включить/выключить пользователя
В `/_admin/` → Users → редактирование `status` (1=enabled, 2=disabled).
CLI смена пароля:
```bash
sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 \
"pct exec 116 -- bash -c 'cd /var/lib/rustdesk-api && /usr/bin/rustdesk-api reset-pwd <username> <new-pw>'"
```
## 3. Onboarding нового клиента
### 3.1 Workflow на стороне сервера
1. **Создать юзера в `/_admin/`** для клиентской организации (или техника):
- admin → Users → "Add"
- Username, password, group
2. **Создать группу** (если нужна):
- Groups → "Add"
- Привязать к ней этого юзера
3. **Сообщить клиенту** username/password + ссылку на установщик
### 3.2 Workflow на стороне клиента
1. Скачать пакет из Nextcloud (см. `[[../../decisions/2026-04-29-rustdesk-client-deployment-package]]`):
- **Public link**: см. в kb или генерится через NC API/UI
- Файл: `RustDesk-Setup-<OS>.zip` (Windows ~23 MB, Linux ~190 MB, macOS ~57 MB)
2. Распаковать, запустить:
- Windows: `Install-Windows.bat` (UAC → Да)
- Linux: `sudo bash install-linux.sh`
- macOS: `bash install-macos.sh`
3. После установки клиент получает **ID + permanent password** (показывается в конце скрипта + сохраняется в файл)
4. Клиент шлёт ID/password админу
5. **Клиент логинится в RustDesk-приложении** (Settings → Account → Sign in `<его-username>` / `<password>`)
- Без login: `MUST_LOGIN=Y` отбрасывает входящие connections — peer виден как online но connect к нему отказан
6. **Админ в `/_admin/`** → Devices → находит peer ID → привязывает к нужной группе
### 3.3 Создание public-link на установщик
```bash
# Через Nextcloud OCS API
APP_PWD=$(cat ~/.config/nextcloud-kb/app-password)
EXPIRY=$(date -v+30d +%Y-%m-%d 2>/dev/null || date -d "+30 days" +%Y-%m-%d)
SHARE_PWD="rustdesk-$(openssl rand -hex 4)"
curl -s -u "admin:$APP_PWD" -X POST \
-H "OCS-APIRequest: true" \
--data-urlencode "path=/RustDesk install/RustDesk-Setup-Windows.zip" \
--data-urlencode "shareType=3" \
--data-urlencode "permissions=1" \
--data-urlencode "password=$SHARE_PWD" \
--data-urlencode "expireDate=$EXPIRY" \
"https://dttb.ru/ocs/v2.php/apps/files_sharing/api/v1/shares?format=json" | python3 -m json.tool
```
Ссылка публичная, **с паролем + 30 дней expiration**. Установщик содержит наш `key=R0lA...`, чтобы не утечь.
## 4. Troubleshooting
### 4.1 Сервер недоступен снаружи
**Симптом:** `https://remot.dttb.ru/_admin/` не отвечает
```bash
# 1. NPM container жив?
sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 \
"pct exec 103 -- docker ps | grep npm"
# 2. Backend сервисы на LXC 116
sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 \
"pct exec 116 -- systemctl status rustdesk-hbbs rustdesk-hbbr rustdesk-api --no-pager"
# 3. Логи NPM
sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 \
"pct exec 103 -- docker exec npm-app-1 tail -30 /data/logs/proxy-host-14_error.log"
# 4. Логи rustdesk
sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 \
"pct exec 116 -- tail -30 /var/log/rustdesk-server/hbbs.log /var/log/rustdesk-api/rustdesk-api.log"
```
### 4.2 Peer показан online, но connect не работает
Чек-лист:
1. **Залогинен ли peer в API?**
```bash
sshpass -p '1qaz!QAZ' ssh root@10.0.0.250 "pct exec 116 -- sqlite3 \
/var/lib/rustdesk-api/data/rustdeskapi.db \
\"select * from user_tokens where device_id='<peer-id>';\""
```
Если **пусто** → клиент не логинился через RustDesk Settings → Account → Sign in. **MUST_LOGIN=Y** отбивает.
2. **Heartbeat актуальный?** В `/_admin/` → Peers → "Last seen" должен быть < 5 минут.
3. **Релай работает?** На клиенте RustDesk:
- Settings → Network → ID server: `remot.dttb.ru:21116`
- Settings → Network → Relay server: `remot.dttb.ru:21117`
- Если поля пустые — `RustDesk2.toml` не подхватился (см. путь в section 5).
### 4.3 WebClient timeout / "Не удалось подключиться"
WebClient (`/webclient/`) Flutter имеет ограничения:
- Mac→Mac не работает (controller не может быть peer'ом)
- Peer offline → relay timeout → ошибка
- Peer не залогинен → MUST_LOGIN отбивает
**Используй desktop-клиент** для рутинной работы. WebClient — emergency only.
### 4.4 Rate-limit заблокировал
Если 5 неудачных login → `503 Service Unavailable` от NPM на 1 минуту. Подожди или login с другого IP.
После 5 неудачных также **`ban-threshold: 5`** в `lejianwen` забанит на ~1 час по user-account (отдельно от NPM IP-ban).
## 5. Конфиги и пути
### LXC 116
| Что | Путь | Перм |
|---|---|---|
| id_ed25519 (приватный) | `/var/lib/rustdesk-server/id_ed25519` | 600 |
| id_ed25519.pub | `/var/lib/rustdesk-server/id_ed25519.pub` | 600 |
| db_v2.sqlite3 (hbbs) | `/var/lib/rustdesk-server/db_v2.sqlite3` | 640 |
| rustdeskapi.db (api) | `/var/lib/rustdesk-api/data/rustdeskapi.db` | 640 |
| api config | `/var/lib/rustdesk-api/conf/config.yaml` | 640 |
| systemd hbbs override | `/etc/systemd/system/rustdesk-hbbs.service.d/override.conf` | — |
| systemd hbbr override | `/etc/systemd/system/rustdesk-hbbr.service.d/override.conf` | — |
| logs | `/var/log/rustdesk-server/`, `/var/log/rustdesk-api/` | — |
| Бэкап (вручную) | `/root/rustdesk-backup-20260428-1134/` | drwx— |
| Бэкап (auto, daily 03:00) | `/root/rustdesk-backups/` | drwxr-xr-x |
| Auto-backup script | `/usr/local/bin/rustdesk-backup.sh` | 700 |
| Cron | `/etc/cron.d/rustdesk-backup` | — |
| Logrotate | `/etc/logrotate.d/rustdesk` | — |
| Rollback script | `/root/rustdesk-rollback.sh` | 700 |
### LXC 103 (NPM)
| Что | Путь |
|---|---|
| Compose файл | `/data/compose/2/docker-compose.yml` (host) — содержит ports 21115-21119 |
| Volume mount: NPM data | `/data/compose/2/data` |
| Volume mount: LE | `/data/compose/2/letsencrypt` |
| Custom http config | `/data/compose/2/data/nginx/custom/http.conf` (limit_req_zone) |
| Custom stream config | `/data/compose/2/data/nginx/custom/stream.conf` (TLS terminate 21118/21119) |
| Custom server_proxy | `/data/compose/2/data/nginx/custom/server_proxy.conf` (security headers) |
| Proxy host 14 | `/data/compose/2/data/nginx/proxy_host/14.conf` (auto-gen NPM) |
| Logs | `/data/compose/2/data/logs/proxy-host-14_*.log` |
## 6. Backup & Restore
### 6.1 Auto-backup
Cron на LXC 116 запускает `/usr/local/bin/rustdesk-backup.sh` ежедневно в 03:00.
- Tarball в `/root/rustdesk-backups/rustdesk-YYYYMMDD.tar.gz`
- Содержит: ключи + обе БД + config.yaml + systemd overrides
- Retention: 30 дней (find -mtime +30 -delete)
### 6.2 Восстановление БД из backup
```bash
# Например, восстановить состояние на 5 дней назад
TARBALL=$(ls -1t /root/rustdesk-backups/rustdesk-*.tar.gz | head -5 | tail -1)
# Стоп
systemctl stop rustdesk-api rustdesk-hbbs rustdesk-hbbr
# Распаковка (поверх)
tar xzf "$TARBALL" -C /
# Старт
systemctl start rustdesk-hbbs rustdesk-hbbr rustdesk-api
```
### 6.3 Полный rollback к OSS
`bash /root/rustdesk-rollback.sh` — за 30 секунд откатит к pre-migration (OSS hbbs/hbbr).
## 7. Updates & Maintenance
### 7.1 Обновление RustDesk-сервера
Клиенты RustDesk-приложения автоматически обновляются с github releases (если включено auto-update). Сервер `hbbs/hbbr` не требует обновлений каждый месяц — обновлять при выпуске **major** версии (1.2.x → 1.3.x) или при security advisory.
```bash
# 1. Скачать новые pro-бинари из docker
docker pull lejianwen/rustdesk-server-s6:latest
docker create --name rd-extract lejianwen/rustdesk-server-s6:latest
docker cp rd-extract:/usr/bin/hbbs /tmp/hbbs.new
docker cp rd-extract:/usr/bin/hbbr /tmp/hbbr.new
docker rm rd-extract
# 2. Передать в LXC 116
pct push 116 /tmp/hbbs.new /usr/bin/hbbs.new --perms 0755
pct push 116 /tmp/hbbr.new /usr/bin/hbbr.new --perms 0755
# 3. Стоп → swap → старт
pct exec 116 -- bash -c "
systemctl stop rustdesk-hbbs rustdesk-hbbr
mv /usr/bin/hbbs /usr/bin/hbbs.old
mv /usr/bin/hbbr /usr/bin/hbbr.old
mv /usr/bin/hbbs.new /usr/bin/hbbs
mv /usr/bin/hbbr.new /usr/bin/hbbr
systemctl start rustdesk-hbbs rustdesk-hbbr
sha256sum /var/lib/rustdesk-server/id_ed25519.pub # проверка ключ не изменился
"
```
### 7.2 Обновление rustdesk-api (lejianwen)
```bash
# Через apt (если репо подключено) или вручную:
# 1. Скачать deb с https://github.com/lejianwen/rustdesk-api/releases
# 2. dpkg -i rustdesk-api-2.x.y.deb
# 3. Проверить config.yaml — могут добавиться новые ключи
# 4. systemctl restart rustdesk-api
```
### 7.3 Лет's Encrypt cert
Auto-renew через NPM (он сам делает).
Проверка:
```bash
docker exec npm-app-1 openssl x509 -in /etc/letsencrypt/live/npm-41/fullchain.pem -noout -dates
```
## 8. Известные ограничения
- **WebClient (Flutter)** не реализует full rustdesk-protocol — для рутины не используй (см. 4.3)
- **Connection from Mac→Mac** не работает (peer не может быть controller'ом самого себя)
- **Strategy (config push)** в `/_admin/` ещё не настроена — клиенты получают конфиг через `RustDesk2.toml` при установке, не пушится дальше
- **Security headers** в NPM не наследуются в `location /` (NPM template ставит `add_header X-Served-By` который перебивает наследование). Workaround — через more_set_headers модуль или patch NPM template. Cosmetic, не критично — TLS 1.2/1.3 + LE cert + rate-limit основной защиты.
- **`apt-mark hold` rustdesk-server-hbbs/hbbr** — apt не обновит. Снять hold перед апгрейдом: `apt-mark unhold rustdesk-server-hbbs rustdesk-server-hbbr`
## 9. Connectivity matrix
| Откуда | Куда | Порт | Назначение |
|---|---|---|---|
| Клиент (peer) | hbbs | 21116/UDP | rendezvous, NAT-punching |
| Клиент (peer) | hbbs | 21116/TCP | id-server, heartbeat |
| Клиент (peer) | hbbs | 21115/TCP | NAT-test |
| Клиент (peer) | hbbr | 21117/TCP | relay для P2P |
| Браузер (WebClient) | NPM/443 | wss | id-server WebSocket (через `/ws/id` или `:21118` SSL) |
| Браузер (WebClient) | NPM/443 | wss | relay WebSocket (через `/ws/relay` или `:21119` SSL) |
| Админ браузер | NPM/443 | https | `/_admin/` интерфейс |
| Админ CLI | rustdesk-api | https | `/api/admin/login`, `/api/admin/...` |
DNS: `remot.dttb.ru` → public IP (через Cloudflare), внутри LAN → 10.0.0.195 (NPM).
## 10. Контакты на критические incidents
- Сервер не отвечает > 10 минут → Олег батюшка
- Все peers одновременно offline → проверить hbbs.log + LE cert + DNS
- Брute-force попытки login (>100/час) → ban IP в NPM Access Lists или fail2ban
- Утечка `id_ed25519` → НЕМЕДЛЕННЫЙ key rotation + reinstall всех клиентов с новым `RustDesk2.toml`