Three pieces of v1.6.0 work that happened together and are easier to
review as one save point.
Rename: Address Book → Connections
- static/addressbook.html renamed to static/connections.html
- Nav links, page titles, empty states, onboarding, and prose updated
across all 8 static pages (connections, admin, docs, index,
recordings, reports, sessions, tokens).
- README, CLAUDE.md, and every file under docs/ updated.
- src/main.rs: connections.html added to the branded-page map and
route list; /addressbook.html returns a 308 permanent redirect so
existing bookmarks keep working.
- Backend API paths, Rust types, and Vault storage paths are
deliberately unchanged — internal only.
Folder allowed_groups picker
- New SQLite table `seen_groups` tracks OIDC groups observed in any
user login; OIDC callback upserts after extracting groups.
- `GET /api/auth/known-groups` (admin-only) returns the union of
group_role_mappings and seen_groups.
- `GET /api/addressbook/folders/{scope}/{folder}/config` adds the
missing endpoint the frontend was already calling — existing
allowed_groups now prefill the edit-folder modal.
- Folder modal swaps the free-text comma-separated input for a chip
picker with a themed combobox dropdown: autocomplete over known
groups, keyboard nav, "+ add custom" row for unlisted groups.
Active session visibility (GitHub #102)
- `GET /api/sessions` scopes to the caller's own sessions by default;
`?all=true` lets admins opt in (used by the Sessions page).
- `GET /api/sessions/{id}` and the thumbnail GET/PUT endpoints are
now owner-or-admin, returning 404 for other callers so session
existence isn't leaked.
- Connections' Active Sessions strip is now always owner-scoped —
admins still manage everyone via the Sessions page.
12 KiB
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)
# 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
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
See installation.md for all options including bare-metal script and Docker.
Step 2: Initial Configuration
Create an admin API key
/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
sudo nano /opt/rustguac/config.toml
Key settings for a production deployment:
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 for the full reference.
Start services
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.
Install
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:
# 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:
# 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:
- Phase 1 (pure trixie): Installs desktop, Firefox, Chromium, build tools, PulseAudio xrdp audio module, switches from PipeWire to real PulseAudio
- Phase 2 (temporary sid): Adds Debian sid repo, installs matching xorgxrdp, rebuilds xrdp with
--enable-x264, removes sid - 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 for manual setup and tuning.
Windows
Windows RDP works out of the box. For video-heavy workloads:
# 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:
[oidc]
issuer_url = "https://your-idp.example.com"
client_id = "rustguac"
redirect_uri = "https://console.example.com/auth/callback"
groups_claim = "groups"
session_ttl_secs = 28800 # 8 hours
[oidc.group_role_mappings]
"RemoteConsoleAdmins" = "admin"
"RemoteConsoleUsers" = "operator"
Set the client secret in /opt/rustguac/env:
echo 'OIDC_CLIENT_SECRET=your-secret-here' | sudo tee -a /opt/rustguac/env
sudo chmod 600 /opt/rustguac/env
sudo systemctl restart rustguac
See 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:
# 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 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.
[vault]
addr = "https://vault.example.com:8200"
mount = "secret"
base_path = "rustguac"
role_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
echo 'VAULT_SECRET_ID=your-secret-id' | sudo tee -a /opt/rustguac/env
sudo systemctl restart rustguac
See integrations.md for Vault setup, AppRole configuration, and mTLS.
Step 7: Lock It Down with Knocknoc
Knocknoc removes the attack surface entirely. Instead of exposing rustguac's login page to the internet, Knocknoc gates access at the network layer:
- Before Knocknoc: the login page is visible to scanners, bots, and attackers
- 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 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)
[drive]
enabled = true
drive_path = "/opt/rustguac/drives"
drive_name = "Shared Drive"
Encrypted (LUKS + Vault)
For environments requiring at-rest encryption:
sudo /opt/rustguac/bin/drive-setup.sh
This creates a LUKS-encrypted volume with the encryption key stored in Vault. See integrations.md for details.
Step 9: Session Recording (optional)
Session recordings are enabled by default and stored in /opt/rustguac/recordings.
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/healthreturns 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
# 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/envhaschmod 600permissions- Trusted proxies configured to match HAProxy IP