mirror of
https://github.com/sol1/rustguac.git
synced 2026-10-27 05:57:06 +00:00
492e329670
Documents the map-group subcommand in the group-to-role mappings section: creating a mapping, the one-mapping-per-group rule and the --force re-map, and that mappings are applied at the user's next OIDC login rather than immediately, which matters in both directions when revoking access. Documents the new cli_audit_log table: what is and is not recorded, that the actor prefers SUDO_USER so sudo names the person rather than root, that only successful actions are logged, how to read it with sqlite3, and the 90 day retention. Also corrects two pre-existing errors found while writing the above: - deployment-guide.md showed an [oidc.group_role_mappings] config table. No such config field exists, so anyone following the guide would add it and silently get no mappings at all. Replaced with the CLI commands that actually create them. The same block had session_ttl_secs under [oidc]; the real key is auth_session_ttl_secs and it is top level. - roles-and-access-control.md documented an enable-user subcommand that does not exist. Re-enabling is done from the Admin page or the API, so the doc now says that, with a warning about disabling your own account.
395 lines
13 KiB
Markdown
395 lines
13 KiB
Markdown
# Deployment Guide
|
|
|
|
A step-by-step guide for planning and deploying rustguac in production. Covers network architecture, server preparation, RDP target setup, security hardening, and ongoing operations.
|
|
|
|
## Architecture Overview
|
|
|
|
A typical deployment has three layers:
|
|
|
|
```
|
|
Internet
|
|
|
|
|
[HAProxy] ── TLS termination, rate limiting, Knocknoc ACL
|
|
|
|
|
[rustguac] ── session management, WebSocket proxy, connections
|
|
|
|
|
[guacd] ── protocol translation (SSH, RDP, VNC)
|
|
|
|
|
[targets] ── SSH servers, RDP desktops, VNC hosts
|
|
```
|
|
|
|
**All components can run on a single server** for small deployments (up to ~50 concurrent sessions). For larger deployments, guacd is the bottleneck (~158 MB per RDP session) and can be scaled separately.
|
|
|
|
### Port allocation
|
|
|
|
| Port | Service | Exposure |
|
|
|------|---------|----------|
|
|
| 443 | HAProxy (HTTPS) | Public / Knocknoc-gated |
|
|
| 8089 | rustguac (HTTPS) | Loopback only (behind HAProxy) |
|
|
| 4822 | guacd (TLS) | Loopback only |
|
|
| 6000-6099 | Xvnc displays | Loopback only (web sessions) |
|
|
|
|
## Step 1: Install rustguac
|
|
|
|
### Debian 13 (recommended)
|
|
|
|
```bash
|
|
# Download the latest .deb from GitHub releases
|
|
wget https://github.com/sol1/rustguac/releases/latest/download/rustguac_amd64.deb
|
|
sudo apt install ./rustguac_amd64.deb
|
|
```
|
|
|
|
This installs rustguac + guacd to `/opt/rustguac` with systemd services.
|
|
|
|
### Docker (recommended for non-Debian-13 hosts)
|
|
|
|
```bash
|
|
docker pull ghcr.io/sol1/rustguac:latest
|
|
docker run -d \
|
|
-p 443:8089 \
|
|
-v rustguac-data:/opt/rustguac/data \
|
|
-v rustguac-recordings:/opt/rustguac/recordings \
|
|
-v ./config.toml:/opt/rustguac/config.toml \
|
|
ghcr.io/sol1/rustguac:latest
|
|
```
|
|
|
|
The Docker image bundles guacd + FreeRDP + dependencies, so it runs cleanly on Ubuntu, RHEL, Rocky, Arch, and other distros where the bare-metal `.deb` would hit a FreeRDP ABI mismatch. See [installation.md](installation.md#other-linux-distributions) for the full story on non-Debian-13 targets.
|
|
|
|
See [installation.md](installation.md) for all install options.
|
|
|
|
## Step 2: Initial Configuration
|
|
|
|
### Create an admin API key
|
|
|
|
```bash
|
|
/opt/rustguac/bin/rustguac --config /opt/rustguac/config.toml add-admin --name admin
|
|
```
|
|
|
|
Save the printed key (`rgu_...`) — it is shown only once. Use it for initial setup, then **delete it once OIDC is configured** (see Step 5).
|
|
|
|
### Edit config.toml
|
|
|
|
```bash
|
|
sudo nano /opt/rustguac/config.toml
|
|
```
|
|
|
|
Key settings for a production deployment:
|
|
|
|
```toml
|
|
listen_addr = "127.0.0.1:8089" # Loopback only — HAProxy handles public TLS
|
|
guacd_addr = "localhost:4822"
|
|
|
|
[tls]
|
|
cert_path = "/opt/rustguac/tls/cert.pem"
|
|
key_path = "/opt/rustguac/tls/key.pem"
|
|
guacd_cert_path = "/opt/rustguac/tls/cert.pem"
|
|
|
|
# Trust HAProxy's X-Forwarded-For header
|
|
trusted_proxies = ["127.0.0.1/32"]
|
|
|
|
# Network allowlists — restrict what targets guacd can connect to.
|
|
# Prevents SSRF via crafted session requests.
|
|
[network]
|
|
allowed_ssh_cidrs = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
|
|
allowed_rdp_cidrs = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
|
|
allowed_vnc_cidrs = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
|
|
```
|
|
|
|
See [configuration.md](configuration.md) for the full reference.
|
|
|
|
### Start services
|
|
|
|
```bash
|
|
sudo systemctl enable --now rustguac
|
|
```
|
|
|
|
Verify: `curl -k https://localhost:8089/api/health`
|
|
|
|
## Step 3: Set Up HAProxy
|
|
|
|
HAProxy provides TLS termination, HTTP/2, WebSocket support, and Knocknoc integration.
|
|
|
|
**Using nginx, Caddy, Apache, or Traefik instead?** See [reverse-proxies.md](reverse-proxies.md) for per-proxy configs and an important `%2F` gotcha that affects nested folder paths on several of them.
|
|
|
|
### Install
|
|
|
|
```bash
|
|
sudo apt install haproxy
|
|
```
|
|
|
|
### Configure
|
|
|
|
Create `/etc/haproxy/haproxy.cfg`:
|
|
|
|
```
|
|
global
|
|
log /dev/log local0
|
|
maxconn 4096
|
|
stats socket /run/haproxy/admin.sock mode 0660 level admin
|
|
ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
|
|
|
|
defaults
|
|
log global
|
|
mode http
|
|
option httplog
|
|
timeout connect 5s
|
|
timeout client 30s
|
|
timeout server 30s
|
|
timeout tunnel 8h # Long-lived WebSocket sessions
|
|
timeout http-request 10s # Slowloris protection
|
|
|
|
frontend https
|
|
bind *:443 ssl crt /etc/ssl/private/rustguac.pem alpn h2,http/1.1
|
|
bind *:80
|
|
http-request redirect scheme https unless { ssl_fc }
|
|
http-request del-header X-Forwarded-For
|
|
option forwardfor
|
|
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
|
default_backend rustguac
|
|
|
|
backend rustguac
|
|
option httpchk GET /api/health
|
|
server rustguac 127.0.0.1:8089 ssl verify none check inter 30s
|
|
```
|
|
|
|
### TLS certificate
|
|
|
|
Use Let's Encrypt or your organisation's CA:
|
|
|
|
```bash
|
|
# Let's Encrypt example (certbot + HAProxy)
|
|
sudo certbot certonly --standalone -d console.example.com
|
|
sudo cat /etc/letsencrypt/live/console.example.com/{fullchain,privkey}.pem \
|
|
> /etc/ssl/private/rustguac.pem
|
|
sudo systemctl restart haproxy
|
|
```
|
|
|
|
## Step 4: Prepare RDP Targets
|
|
|
|
### Linux (xrdp with H.264)
|
|
|
|
For the best video experience with Linux desktops, use xrdp with x264 H.264 encoding. A single setup script handles everything — desktop environment, audio, xrdp rebuild with x264, and GFX configuration:
|
|
|
|
```bash
|
|
# On the RDP target machine (not the rustguac server):
|
|
wget -O setup-xrdp-gfx.sh https://raw.githubusercontent.com/sol1/rustguac/main/contrib/setup-xrdp-gfx.sh
|
|
sudo bash setup-xrdp-gfx.sh --desktop mate
|
|
```
|
|
|
|
The `--desktop` flag installs a desktop environment (default: `mate`). Options: `mate`, `xfce`, `kde`, `gnome`, `none`. MATE is recommended — it's lightweight, Windows-like, and works reliably over xrdp without GPU.
|
|
|
|
The script runs in three phases:
|
|
1. **Phase 1 (pure trixie):** Installs desktop, Firefox, Chromium, build tools, PulseAudio xrdp audio module, switches from PipeWire to real PulseAudio
|
|
2. **Phase 2 (temporary sid):** Adds Debian sid repo, installs matching xorgxrdp, rebuilds xrdp with `--enable-x264`, removes sid
|
|
3. **Phase 3 (configure):** Xorg backend, startwm.sh, gfx.toml with H.264 + x264 encoder
|
|
|
|
Run `bash setup-xrdp-gfx.sh --help` for all options, or `bash setup-xrdp-gfx.sh --diagnose` to troubleshoot after setup.
|
|
|
|
In the rustguac connections, enable these settings on the RDP entry:
|
|
- **Enable Graphics Pipeline (GFX)** -- checked
|
|
- **H.264 Passthrough** -- checked
|
|
- **Enable Desktop Composition** -- not needed for Linux (Windows-only DWM setting)
|
|
|
|
See [rdp-video-performance.md](rdp-video-performance.md) for manual setup and tuning.
|
|
|
|
### Windows
|
|
|
|
Windows RDP works out of the box. For video-heavy workloads:
|
|
|
|
```powershell
|
|
# On the Windows RDP server (as Administrator):
|
|
.\contrib\setup-rdp-performance.ps1
|
|
|
|
# With GPU hardware encoding:
|
|
.\contrib\setup-rdp-performance.ps1 -EnableGPU
|
|
```
|
|
|
|
This enables AVC 4:4:4, 60 FPS, desktop composition, and GPU encoding.
|
|
|
|
**Note:** Windows only sends H.264 when a GPU (physical or virtual) is available. Without GPU, it uses Planar/RemoteFX which guacd re-encodes as JPEG/WebP. This is still good quality — just not as low-latency as H.264 passthrough.
|
|
|
|
## Step 5: Configure Authentication
|
|
|
|
### OIDC Single Sign-On (recommended)
|
|
|
|
Add to `config.toml`:
|
|
|
|
```toml
|
|
# Top-level key, so it must appear before the first [section] header
|
|
auth_session_ttl_secs = 28800 # OIDC auth session cookie TTL (default 86400, 24h)
|
|
|
|
[oidc]
|
|
issuer_url = "https://your-idp.example.com"
|
|
client_id = "rustguac"
|
|
redirect_uri = "https://console.example.com/auth/callback"
|
|
groups_claim = "groups"
|
|
```
|
|
|
|
Set the client secret in `/opt/rustguac/env`:
|
|
|
|
```bash
|
|
echo 'OIDC_CLIENT_SECRET=your-secret-here' | sudo tee -a /opt/rustguac/env
|
|
sudo chmod 600 /opt/rustguac/env
|
|
sudo systemctl restart rustguac
|
|
```
|
|
|
|
Group-to-role mappings are stored in the database rather than in `config.toml`. Create them with the CLI once the service is running:
|
|
|
|
```bash
|
|
/opt/rustguac/bin/rustguac --config /opt/rustguac/config.toml \
|
|
map-group --group RemoteConsoleAdmins --role admin
|
|
/opt/rustguac/bin/rustguac --config /opt/rustguac/config.toml \
|
|
map-group --group RemoteConsoleUsers --role operator
|
|
```
|
|
|
|
They can also be managed in the Admin page or through the API. See [Group-to-role mappings](roles-and-access-control.md#group-to-role-mappings).
|
|
|
|
See [integrations.md](integrations.md) for provider-specific guides (Authentik, JumpCloud, Entra ID, etc.).
|
|
|
|
### Delete the bootstrap API key
|
|
|
|
Once OIDC is working and you have an admin user, remove the initial API key:
|
|
|
|
```bash
|
|
# List admin keys
|
|
/opt/rustguac/bin/rustguac --config /opt/rustguac/config.toml list-admins
|
|
|
|
# Delete by name
|
|
/opt/rustguac/bin/rustguac --config /opt/rustguac/config.toml delete-admin --name admin
|
|
```
|
|
|
|
API keys are powerful (full admin, no MFA). For day-to-day use, OIDC with group-based roles is more secure. If you need programmatic API access, create scoped [user API tokens](roles-and-access-control.md) instead.
|
|
|
|
## Step 6: Set Up the Connections (Vault)
|
|
|
|
The connections stores connection entries in HashiCorp Vault or OpenBao. Credentials stay server-side — they never reach the browser.
|
|
|
|
```toml
|
|
[vault]
|
|
addr = "https://vault.example.com:8200"
|
|
mount = "secret"
|
|
base_path = "rustguac"
|
|
role_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
|
|
```
|
|
|
|
```bash
|
|
echo 'VAULT_SECRET_ID=your-secret-id' | sudo tee -a /opt/rustguac/env
|
|
sudo systemctl restart rustguac
|
|
```
|
|
|
|
See [integrations.md](integrations.md) for Vault setup, AppRole configuration, and mTLS.
|
|
|
|
## Step 7: Lock It Down with Knocknoc
|
|
|
|
[Knocknoc](https://knocknoc.io) removes the attack surface entirely. Instead of exposing rustguac's login page to the internet, Knocknoc gates access at the network layer:
|
|
|
|
1. **Before Knocknoc:** the login page is visible to scanners, bots, and attackers
|
|
2. **After Knocknoc:** the login page returns 403 unless the user has authenticated through Knocknoc first (SSO + MFA)
|
|
|
|
Only the front page (`/`) is gated. API endpoints, OIDC callbacks, and share links pass through to rustguac's own auth.
|
|
|
|
### HAProxy + Knocknoc configuration
|
|
|
|
Add to your HAProxy config:
|
|
|
|
```
|
|
# Dynamic ACL managed by knocknoc-agent
|
|
acl knoc_rustguac src -u 600
|
|
acl is_root path /
|
|
|
|
# Gate only the login page
|
|
use_backend rustguac if is_root knoc_rustguac
|
|
use_backend denied if is_root
|
|
use_backend rustguac
|
|
```
|
|
|
|
Install and configure [knocknoc-agent](https://docs.knocknoc.io) to manage ACL #600 via the HAProxy admin socket.
|
|
|
|
### Why this matters
|
|
|
|
rustguac gives users administrative access to servers. Even with OIDC and strong passwords, exposing the login page means:
|
|
- Brute-force and credential-stuffing attacks
|
|
- Zero-day exploits against the web layer
|
|
- Reconnaissance by scanners
|
|
|
|
Knocknoc ensures the login page is only reachable after identity-verified network authentication. The attack surface goes from "the entire internet" to "zero".
|
|
|
|
## Step 8: Enable Drive Mapping (optional)
|
|
|
|
Drive mapping lets users transfer files to/from remote sessions.
|
|
|
|
### Basic (unencrypted)
|
|
|
|
```toml
|
|
[drive]
|
|
enabled = true
|
|
drive_path = "/opt/rustguac/drives"
|
|
drive_name = "Shared Drive"
|
|
```
|
|
|
|
### Encrypted (LUKS + Vault)
|
|
|
|
For environments requiring at-rest encryption:
|
|
|
|
```bash
|
|
sudo /opt/rustguac/bin/drive-setup.sh
|
|
```
|
|
|
|
This creates a LUKS-encrypted volume with the encryption key stored in Vault. See [integrations.md](integrations.md) for details.
|
|
|
|
## Step 9: Session Recording (optional)
|
|
|
|
Session recordings are enabled by default and stored in `/opt/rustguac/recordings`.
|
|
|
|
```toml
|
|
recording_path = "/opt/rustguac/recordings"
|
|
|
|
[recording_rotation]
|
|
enabled = true
|
|
max_disk_percent = 80 # Auto-delete oldest when disk usage exceeds 80%
|
|
interval_secs = 300 # Check every 5 minutes
|
|
```
|
|
|
|
Recordings can be played back in the browser via the Sessions page, or exported for compliance.
|
|
|
|
## Ongoing Operations
|
|
|
|
### Monitoring
|
|
|
|
- **Health check:** `GET /api/health` returns 200 when rustguac and guacd are running
|
|
- **System status:** `GET /api/system/status` (admin only) shows version, uptime, active sessions
|
|
- **Reports:** Session history, top connections, top users available at `/reports.html` (poweruser+ role)
|
|
|
|
### Upgrading
|
|
|
|
```bash
|
|
# Debian package
|
|
sudo apt install ./rustguac_new-version.deb
|
|
sudo systemctl restart rustguac
|
|
```
|
|
|
|
Config files are preserved across upgrades (`--force-confold`). Database migrations run automatically on startup.
|
|
|
|
### Backup
|
|
|
|
Back up these paths:
|
|
- `/opt/rustguac/config.toml` — configuration
|
|
- `/opt/rustguac/data/rustguac.db` — users, tokens, session history
|
|
- `/opt/rustguac/env` — secrets (Vault secret ID, OIDC client secret)
|
|
- `/opt/rustguac/recordings/` — session recordings (if needed for compliance)
|
|
|
|
The connections is in Vault — back up Vault separately.
|
|
|
|
### Security checklist
|
|
|
|
- [ ] HAProxy terminates TLS with a valid certificate (not self-signed)
|
|
- [ ] rustguac listens on loopback only (`listen_addr = "127.0.0.1:8089"`)
|
|
- [ ] Network allowlists configured (prevent SSRF to unintended targets)
|
|
- [ ] OIDC configured with group-based role mappings
|
|
- [ ] Bootstrap API key deleted after OIDC setup
|
|
- [ ] Knocknoc gates the login page (optional but strongly recommended)
|
|
- [ ] Drive encryption enabled if file transfer is used in regulated environments
|
|
- [ ] Session recording enabled for audit compliance
|
|
- [ ] `/opt/rustguac/env` has `chmod 600` permissions
|
|
- [ ] Trusted proxies configured to match HAProxy IP
|