Replace the monolithic README with a short landing page, align wiki with current 3.5.x product facts, add PRIVACY.md, Monitoring and Unattended/WoL guides, and park historical audits under docs/archive. Thanks: INSOLVE (Honorary); Marco Jakobs (@jacotec); MyNameisStitch (@MyNameisStitch); Redspin (@playerumpknow)
8.5 KiB
Security
Practical hardening for a self-hosted BetterDesk install. License: Licensing. Privacy / no vendor analytics: Privacy. SSO: OIDC SSO.
Production minimum checklist
- Keep the panel on localhost or behind TLS reverse proxy (
HOST=127.0.0.1, see REVERSE_PROXY). - Strong admin password + TOTP for operators who need it.
- Prefer enrollment
managedorlockedover defaultopenonce you are past lab testing (Settings / Go envENROLLMENT_MODE). - Do not expose
:21114admin API to the WAN; clients use:21121. - Turn on TLS for signal/relay/API when clients are off your LAN — TLS / SSL Certificates.
- Gate
/metrics(auth or IP allowlist) — Monitoring. - Disable LAN mDNS if you do not need it (
PANEL_MDNS=off) — Privacy.
Enrollment modes
| Mode | Behaviour |
|---|---|
open |
Default. Accept new device registrations (lab-friendly). |
managed |
New devices need approval or a valid token. |
locked |
Only devices with valid tokens register. |
Encryption
Signal (NaCl)
Client–server signal (:21116) uses NaCl:
- Server Ed25519 key pair (
id_ed25519/.pub) - DH session key
- Encrypted messages after handshake
Relay
Relay (:21117) forwards opaque peer traffic. Peers set up E2E through signal; the relay does not hold your session keys.
Stock RustDesk clients show a green lock when E2E is up (P2P or relay). That is client–client crypto on your infrastructure — not a cloud “trust us” feature.
TLS transport
Optional TLS on signal / relay / API — see TLS / SSL Certificates. Dual-mode listeners accept TLS or plain on the same port when configured.
Chat crypto details: Chat E2E Encryption (operator summary up front; protocol appendix for developers).
Authentication
Web Console
| Mechanism | Description |
|---|---|
| Session cookies | HttpOnly, Secure, SameSite=Lax |
| Session regeneration | New session ID after login (prevents fixation) |
| Bcrypt passwords | Automatic salt, timing-safe comparison |
| TOTP 2FA | 30-second TOTP with one-window tolerance |
| Partial 2FA token | 5-minute TTL JWT for 2FA step |
API Authentication
| Method | Usage |
|---|---|
API Key (X-API-Key header) |
Server-to-server (Node.js ↔ Go) |
JWT Bearer (Authorization: Bearer header) |
User API access (Pro/Admin/Operator) |
| Session cookie | Web panel requests |
RustDesk Client Auth
| Method | Description |
|---|---|
| Public key | Ed25519 key exchange on signal connection |
| Registration token | Optional token required for client registration |
| User login | Username/password via /api/login on Client API (port 21121) |
Authorization (RBAC)
Four-tier role-based access control:
| Role | Panel | API | Devices | Users | Settings |
|---|---|---|---|---|---|
| Admin | ✅ | ✅ | Full | Full | Full |
| Operator | ✅ | ✅ | View + Connect | ❌ | ❌ |
| Viewer | ✅ | Read | View only | ❌ | ❌ |
| Pro | ❌ | ✅ | Full API | ❌ | ❌ |
See User Management for details.
Rate Limiting
IP-Based Limits
| Endpoint | Limit | Description |
|---|---|---|
POST /api/auth/login |
5/min per IP | Login attempts |
POST /api/auth/login/2fa |
5/min per IP | TOTP verification |
| TCP signal connections | Configurable | Per-IP connection rate |
| WebSocket upgrades | Per-IP | Signal and relay |
Connection Limits
| Resource | Limit | Description |
|---|---|---|
| TCP punch connections | 10,000 max, 2-min TTL | DDoS protection |
| Relay sessions | Idle timeout (configurable) | Stale session cleanup |
| API WebSocket | Per-IP | Event stream connections |
Input Validation
API Endpoints
| Validation | Rule |
|---|---|
| Peer ID | Alphanumeric, 1-32 characters |
| New peer ID (rename) | [A-Za-z0-9_-]{6,16} |
| Config keys | [a-zA-Z0-9._-]{1,64} |
| SQL LIKE patterns | % and _ escaped with \ |
| Tags | String or JSON array accepted |
| Device IDs | Coerced to string (numeric accepted) |
WebSocket
| Check | Description |
|---|---|
| Origin validation | WS_ALLOWED_ORIGINS env var (signal + relay) |
| API origin validation | API_WS_ALLOWED_ORIGINS env var |
| Session required | WebSocket upgrade requires valid session cookie |
CSRF Protection
The web console uses double-submit cookie pattern:
- CSRF token generated per session
- Token included in forms and AJAX requests
- Server validates token against cookie
- Implemented via
csrf-csrfmiddleware
Audit Logging
All security-relevant events are logged:
| Event | Details Logged |
|---|---|
| Login success/failure | IP, user agent, username |
| 2FA attempts | IP, success/failure |
| Password changes | User, IP |
| Device ban/unban | Device ID, admin |
| Device deletion | Device ID, admin, revoke flag |
| API key usage | Key ID, endpoint |
| Config changes | Key, old/new values |
| Sysinfo updates | Device ID, hostname, OS |
| Connection audit | Host ID, peer ID, action, IP |
Audit entries are stored in the Go server's ring buffer and queryable via API.
Device Security
Soft Delete
Deleted devices are soft-deleted (marked soft_deleted=1). They:
- Cannot re-register on the signal server
- Are filtered from device list queries
- Prevent "zombie device" reappearance
Device Revocation
The ?revoke=true flag on device deletion:
- Soft-deletes the device
- Blocks the device ID (
IsPeerBanned) - Disconnects all active TCP and WebSocket connections
- Logs
ActionPeerRevokedaudit entry - Optionally cascades to linked devices (
?cascade=true)
Peer Banning
Banned devices:
- Cannot register on the signal server
- Cannot establish relay connections
- Ban is per-device-ID (not per-IP)
Dependency Security
Node.js Console
npm audit --omit=dev --audit-level=moderatemust report 0 vulnerabilities (CI enforced)tarpackage overridden to ^7.5.16+ (GHSA-vmf3-w455-68vh)- Dependencies pinned via committed
web-nodejs/package-lock.json; production installs usenpm ci
Go Server
- Go toolchain version pinned in
go.mod govulncheck ./...in CI- No CGO dependencies (static binary)
- Minimal external dependencies
Rust (Tauri clients)
cargo auditin CI (known glib RUSTSEC-2024-0429 ignored until GTK stack migration)- Dependabot weekly for Cargo, npm (
web-nodejs), and Go modules
Logging (3.4+)
| Component | Control | Default (production) |
|---|---|---|
| Node console | LOG_LEVEL=error|warn|info|debug |
warn |
| Go server | LOG_LEVEL or -log-level |
info |
Auth and audit details redact usernames and secrets before stdout/DB insert. Set LOG_LEVEL=info temporarily when debugging auth issues.
Operational logs: use systemd journal or Docker log driver; optional Go audit JSONL via AUDIT_LOG_FILE (configure logrotate externally).
Production deployment (3.4+)
Recommended before Internet-facing deployment:
| Control | Variable | Recommended |
|---|---|---|
| Signal TLS | TLS_SIGNAL=Y |
Required on WAN |
| Relay TLS | TLS_RELAY=Y |
Required on WAN |
| Enrollment | ENROLLMENT_MODE |
managed or locked |
| WS origins | WS_ALLOWED_ORIGINS |
Explicit panel URL(s) |
| Panel bind | HOST |
127.0.0.1 behind reverse proxy |
| Panel TLS | SSL_CERT_PATH / SSL_KEY_PATH |
Valid public certificate |
| Relay limits | RELAY_MAX_CONNS_PER_IP |
20 (adjust for NAT scale) |
| Signal rate limit | SIGNAL_RATE_LIMIT_PER_IP |
Default 20; raise for large NAT |
Startup banners log ERROR when open enrollment is combined with missing signal/relay TLS (Go) or HTTP panel on 0.0.0.0 with open enrollment (Node).
Security Headers
The web console sets standard security headers:
X-Content-Type-Options: nosniffX-Frame-Options: DENYX-XSS-Protection: 1; mode=blockReferrer-Policy: strict-origin-when-cross-originContent-Security-Policy(configured per environment)
See also
- TLS / SSL Certificates — transport encryption
- Chat E2E Encryption — operator chat crypto
- User Management — 2FA and sessions
- Licensing — AGPL and Commercial Grant