117 lines
5.5 KiB
Markdown
117 lines
5.5 KiB
Markdown
# RustDesk Server on Docker, behind Caddy
|
|
|
|
Deploys the self-hosted [RustDesk](https://rustdesk.com) server (OSS) on `monoco`: the
|
|
ID/rendezvous server `hbbs` and the relay server `hbbr`, both supervised by s6 inside the
|
|
official `rustdesk/rustdesk-server-s6` container, reachable only through Caddy on
|
|
`rust.tanshu.com:443`. No RustDesk port is published on the host.
|
|
|
|
## How the traffic is carried
|
|
|
|
RustDesk's native transports on 21115/21116/21117 are a custom TCP/UDP protocol, not HTTP,
|
|
so an HTTP reverse proxy cannot carry them. What *is* HTTP is the WebSocket transport that
|
|
`hbbs` and `hbbr` also listen on (21118 and 21119), and clients that enable WebSocket use
|
|
it for both rendezvous and relay. Caddy terminates TLS on 443 and forwards two paths into
|
|
the container over the Docker network:
|
|
|
|
| Path | Upstream | Purpose |
|
|
| --- | --- | --- |
|
|
| `/ws/id` | `rustdesk:21118` (hbbs) | ID registration and signalling |
|
|
| `/ws/relay` | `rustdesk:21119` (hbbr) | Relayed sessions |
|
|
|
|
The code that decides this is in the client: `hbb_common::websocket::check_ws` maps a
|
|
domain `host:21116` to `wss://host/ws/id` and `host:21117` to `wss://host/ws/relay`, and
|
|
the path names are fixed there. Caddy sets `X-Real-IP`, which `hbbs`/`hbbr` trust to keep
|
|
the real client address for rate limiting and logs (upstream warns that these ports must
|
|
therefore never be exposed directly).
|
|
|
|
## Settings
|
|
|
|
- `host`: the public name, `rust.tanshu.com`. Both the Caddy site and the address `hbbs`
|
|
advertises come from it.
|
|
- `docker_container`, `image_name`, `image_version`: container name and the
|
|
`rustdesk/rustdesk-server-s6` tag to run.
|
|
- `data_location`: host directory bind-mounted to `/data` (`/var/lib/rustdesk/data`),
|
|
holding the SQLite database and the server key pair.
|
|
- `rustdesk_relay_server`: handed to clients by `hbbs` as the relay address. It keeps the
|
|
public name, so clients rewrite it to `wss://rust.tanshu.com/ws/relay`.
|
|
- `rustdesk_publish_raw_ports` / `rustdesk_raw_ports`: publish 21115/21116/21117 as well.
|
|
Off by default, see "Clients that cannot use WebSocket" below.
|
|
- `rustdesk_encrypted_only`: `ENCRYPTED_ONLY=1`, so `hbbr` validates the server key too
|
|
instead of accepting unkeyed clients.
|
|
- `rustdesk_always_use_relay`: `ALWAYS_USE_RELAY=Y`, forcing relayed sessions. WebSocket
|
|
clients are relay-only anyway, so this only affects clients on the raw ports.
|
|
|
|
## Prerequisites
|
|
|
|
- A DNS `A` record for `rust.tanshu.com` pointing at `monoco`; Caddy issues the certificate
|
|
over ports 80/443.
|
|
- The Caddy container already running on `monoco` (`caddy/playbook.yml`), with its Caddyfile
|
|
at `/var/lib/caddy/conf/Caddyfile`.
|
|
|
|
## Running this Playbook
|
|
|
|
The inventory in `/etc/ansible/hosts` already lists `monoco`, which is what `hosts:`
|
|
targets:
|
|
|
|
```command
|
|
ansible-playbook playbook.yml
|
|
```
|
|
|
|
The playbook creates the `rustdesk_net` network, attaches Caddy to it, starts the
|
|
container, appends the site block to the Caddyfile, reloads Caddy, and prints the public
|
|
key.
|
|
|
|
## Configuring the clients
|
|
|
|
On each client, open **Settings → Network → ID/Relay Server** and set:
|
|
|
|
| Field | Value |
|
|
| --- | --- |
|
|
| ID Server | `rust.tanshu.com` |
|
|
| API Server | `https://rust.tanshu.com` |
|
|
| Key | the printed value, i.e. `/var/lib/rustdesk/data/id_ed25519.pub` on the host |
|
|
|
|
then enable **Settings → Network → Use WebSocket** (`allow-websocket=Y`).
|
|
|
|
The **API Server** field looks odd for an OSS deployment, which has no API, but it is what
|
|
selects the TLS scheme: the client uses `wss://` only when the API server starts with
|
|
`https`, and otherwise silently falls back to plaintext `ws://rust.tanshu.com/...` on port
|
|
80. Because the client also skips its own client↔server key exchange whenever WebSocket is
|
|
enabled — on the assumption that the transport is already encrypted — that fallback would
|
|
leave the connection unprotected rather than merely failing.
|
|
|
|
## Trade-offs
|
|
|
|
- **Relayed sessions only.** With WebSocket enabled the client never hole-punches
|
|
(`force_relay` is set, and `hbbs` skips the same-intranet path for WebSocket peers), so
|
|
every session flows through `monoco`. Remote control works, but throughput is bounded by
|
|
the server, and latency is higher than a direct connection.
|
|
- **Upstream labels this a Pro feature.** The client's own tooltip says "RustDesk server
|
|
OSS doesn't include this feature", even though the OSS binaries do listen on 21118/21119
|
|
and the OSS docs describe proxying them. Treat the WSS path as needing a real test: if
|
|
clients fail to register, the fallback is the raw ports below.
|
|
- **The address has to be the domain.** `check_ws` only builds a TLS URL and the `/ws`
|
|
paths for hostnames; an IP would become `ws://ip:21118` and bypass Caddy entirely.
|
|
|
|
### Clients that cannot use WebSocket
|
|
|
|
Clients without the WebSocket option (older builds, third-party clients, or anything that
|
|
must connect directly) need the native ports. Set `rustdesk_publish_raw_ports: true` and
|
|
re-run the playbook, then open 21115-21116 (TCP) plus 21116/udp and 21117 (TCP) on the host
|
|
and in any cloud firewall:
|
|
|
|
```command
|
|
ufw allow 21115:21117/tcp
|
|
ufw allow 21116/udp
|
|
```
|
|
|
|
WebSocket clients keep working at the same time; they ignore the raw ports.
|
|
|
|
## Notes
|
|
|
|
- **Logs.** `docker logs rustdesk` covers both services.
|
|
- **Server key.** If `/var/lib/rustdesk/data/id_ed25519` is lost, every client has to be
|
|
reconfigured with the new public key. Back it up.
|
|
- **Upgrading.** Re-running the playbook pulls the tag in `image_version` and recreates the
|
|
container; the key pair and database live in `data_location` and survive.
|