Files
knowledge-base/decisions/2026-04-30-rustdesk-pre-prod-audit.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

10 KiB
Raw Blame History

date, type, tags
date type tags
2026-04-30 audit
rustdesk
security
audit
hardening

RustDesk pre-production audit + fixes

Контекст

Перед запуском RustDesk-сервера в продакшн (для удалённого доступа клиентских организаций + личного использования) — системный аудит security, reliability, operations. Findings + примененные fixes.

Findings & fixes

🔴 Critical (исправлено)

1. Права на критичные файлы 644 → 600/640

Что было: все sensitive файлы на LXC 116 были world-readable (-rw-r--r--):

  • /var/lib/rustdesk-server/id_ed25519 (приватный ключ сервера)
  • /var/lib/rustdesk-server/id_ed25519.pub
  • /var/lib/rustdesk-server/db_v2.sqlite3* (hbbs БД с peers UUID)
  • /var/lib/rustdesk-api/data/rustdeskapi.db (хеши паролей юзеров, токены сессий)
  • /var/lib/rustdesk-api/conf/config.yaml (содержит JWT secret и key)

Риск: при компрометации любого non-root юзера на LXC 116 → leak приватного ключа сервера → весь парк клиентов перестаёт быть приватным; возможность подделки JWT и login as admin.

Fix (2026-04-30):

chmod 600 /var/lib/rustdesk-server/id_ed25519*
chmod 640 /var/lib/rustdesk-server/db_v2.sqlite3*
chmod 640 /var/lib/rustdesk-api/data/rustdeskapi.db
chmod 640 /var/lib/rustdesk-api/conf/config.yaml

2. Нет logrotate для rustdesk-логов на 2GB-диске

Что было: /var/log/rustdesk-server/hbbs.log ~260KB и rosт каждый день. На 2GB-диске LXC 116 (где уже 55% used) логи зальют диск за пару месяцев → сервер ляжет.

Fix (2026-04-30): /etc/logrotate.d/rustdesk:

/var/log/rustdesk-server/*.log /var/log/rustdesk-server/*.error
/var/log/rustdesk-api/*.log /var/log/rustdesk-api/*.error {
    daily
    rotate 14
    compress
    delaycompress
    notifempty
    missingok
    copytruncate
    su root root
}

3. Нет авто-бэкапа БД и ключей

Что было: одна вручную сделанная копия /root/rustdesk-backup-20260428-1134/. Если завтра LVM упадёт — теряем весь стейт (peers, users, ключи).

Fix (2026-04-30): cron daily 03:00 → /usr/local/bin/rustdesk-backup.sh:

  • tar.gz в /root/rustdesk-backups/rustdesk-YYYYMMDD.tar.gz
  • ключи + обе БД + config.yaml + systemd overrides
  • retention 30 дней (find -mtime +30 -delete)
  • chmod 600 на каждый tarball

TODO: доставить копии бэкапа на ArtLeon (off-site) через NetBird/rsync.

🟡 Medium (исправлено)

4. Нет rate-limit на login → brute-force admin password

Что было: https://remot.dttb.ru/api/admin/login принимал безlimit запросы. С учётом стандартного pw 1qaz!QAZ — кто-то с словарём найдёт его за час.

Fix (2026-04-30): в NPM /data/compose/2/data/nginx/custom/http.conf:

limit_req_zone $binary_remote_addr zone=adminlogin:10m rate=5r/m;
limit_req_zone $binary_remote_addr zone=apilogin:10m rate=10r/m;

В Proxy Host 14 advanced_config:

location = /api/admin/login {
    limit_req zone=adminlogin burst=3 nodelay;
    proxy_pass http://10.0.0.244:21114;
    ...
}
location = /api/login {
    limit_req zone=apilogin burst=5 nodelay;
    proxy_pass http://10.0.0.244:21114;
    ...
}

Verified: после 4 быстрых попыток — 503 от NPM. На уровне rustdesk-api также ban-threshold: 5 забанит user-account на ~1 час.

Грабли при настройке: limit_req_zone нельзя класть в server-контекст — только http. Первая попытка положить в advanced_config привела к удалению NPM proxy_host/14.conf и обвалу remot.dttb.ru. Восстановилось через disable/enable + переноса zone в http.conf.

🟡 Medium (отложено)

Что было: https://dttb.ru/s/wPm8oaiFEyz7Ywx без пароля и без expiration. ZIP внутри содержит key=R0lA4r77.... Любой нашедший ссылку может зарегистрировать peer'а на нашем сервере.

Status: Nextcloud-AIO сейчас лежит (502 from NPM upstream), новую share создать не получается. Спавнен отдельный task на восстановление NC. После — пересоздать share с паролем + 30 дней expiration.

Митigation в текущем виде: MUST_LOGIN=Y отбивает входящие connections к незалогиненным peers. Кто-то регистрируется как peer — heartbeat-флуд возможен, но connections не пройдут.

6. Security headers не наследуются в location /

Что было: HSTS, X-Frame-Options, X-Content-Type-Options, Referrer-Policy не появляются в response от https://remot.dttb.ru/_admin/.

Корневая причина: NPM в template для Proxy Host генерит location / со встроенным add_header X-Served-By (через include proxy.conf). По правилу nginx — если в location есть хоть один add_header, наследование от server-block отключается. Мои add_header в advanced_config (server-level) не применяются к location /.

Fix-варианты (выбран wait):

  • (a) Patch NPM template /etc/nginx/conf.d/include/proxy.conf чтобы добавить headers — теряется при container recreate
  • (b) Использовать more_set_headers (модуль ngx_headers_more) — нужно проверить что он есть в openresty
  • (c) Override location / в advanced_config с явными add_header — конфликт с NPM-сгенерированным location /
  • (d) Принять как cosmetic — TLS 1.2/1.3 + LE cert + rate-limit обеспечивают основную защиту; HSTS на server-level записан в nginx.conf (не на response).

Status: отложено как cosmetic. Если будем делать (b) или (a) — отдельной задачей.

🟢 Low / Informational

Item Status
Server header раскрывает openresty Accept (мелкий fingerprinting, не критично)
show-swagger: 1 в API Accept (Олег использует для разработки)
TLS 1.2 + 1.3, intermediate ciphers optimal
LE cert valid until 28 June 2026 auto-renew
Block Common Exploits (NPM) enabled на host 14
block_exploits: true, caching_enabled: false verified
register: false в API закрытая система
ban-threshold: 5, captcha-threshold: 3 enabled
MUST_LOGIN=Y enforced (видно в hbbs.log)
RustDesk peers версии 1.4.6 (latest stable)
Disk LXC 116 free 826 MB / 2 GB (55% used) — monitoring
RAM LXC 116 used 105M / 512M — большой запас
NPM compose ports 21115-21119 exposed после 2026-04-29 update

Что осталось сделать (production todo)

Перед массовым deploy

  • Восстановить Nextcloud (отдельный task)
  • Создать share-link с паролем + expiration после восстановления NC
  • Создать минимум 2 группы в /_admin/ (например НИИКН, Клиенты-Internal)
  • Настроить Configuration Strategy в админке lejianwen — push настроек существующим клиентам автоматически
  • Off-site бэкап /root/rustdesk-backups/*.tar.gz на ArtLeon через rsync over NetBird (cron weekly)

Полезное (не блокирует prod)

  • more_set_headers фикс security headers
  • fail2ban на NPM access logs (поверх rate-limit) — extra слой против distributed brute-force
  • Monitoring: alert на падение rustdesk сервиса (через watchyourlan/zabbix/uptime-kuma)
  • Auto-login клиента в API через service-token (упрощает onboarding)
  • rustdesk-utils 2.x для single-file rustdesk-licensed-*.exe deployment

Долгосрочное

  • Через 1-2 месяца — миграция в Docker lejianwen/rustdesk-server-s6 (Вариант A3)
  • HA-схема на втором экземпляре (НИИКН Proxmox) с общей MySQL
  • MCP-обёртка поверх Swagger API для управления из Claude Code

Верификация

# Permissions OK
ssh root@10.0.0.250 "pct exec 116 -- ls -la /var/lib/rustdesk-server/id_ed25519"
# → -rw------- 1 root root 88 Oct 10  2025

# Logrotate OK
ssh root@10.0.0.250 "pct exec 116 -- logrotate -d /etc/logrotate.d/rustdesk"
# → "considering log /var/log/rustdesk-..."

# Auto-backup ran
ssh root@10.0.0.250 "pct exec 116 -- ls -la /root/rustdesk-backups/"
# → rustdesk-20260430.tar.gz

# Rate-limit работает
for i in {1..6}; do
  curl -ks -o /dev/null -w "%{http_code} " -X POST https://remot.dttb.ru/api/admin/login \
    -H "Content-Type: application/json" -d '{"username":"x","password":"y"}'
done
# → 200 200 200 200 503 503

# Сервер отвечает
curl -ksI https://remot.dttb.ru/_admin/ | head -1
# → HTTP/2 200

Связанные